Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use match.call to pass all arguments to other function

From ?match.call:

match.call is most commonly used in two circumstances: […] To pass most of the call to another function […]

After reading that, I expected I could use match.call when I want to pass all arguments of one function to another function without listing these arguments one by one.

Example: outer1 passes arguments one by one, outer2 uses match.call.

outer1 <- function(a, b, c) {
  inner1(a, b, c)
}

inner1 <- function(a, b, c) { 
  return(a + b + c$first + c$second)
}

outer2 <- function(a, b, c) {
   mycall <- match.call()
   inner2(mycall)
}

inner2 <- function(call) {
   return(call$a + call$b + call$c$first + call$c$second)
}

outer1(1, 2, list(first = 3, second = 4)) # OK: 10
outer2(1, 2, list(first = 3, second = 4)) # OK: 10

The first problem arises, when -1 instead of 1 is passed to outer2:

outer2(-1, 2, list(first = 3, second = 4)) # Error in call$a + call$b : non-numeric argument to binary operator

Question 1: What is the technical difference between passing -1 instead of 1? I know that

typeof(quote(1)) # double
typeof(quote(-1)) # language

but I suppose that the fact that I passed a language object in the second case is not the (only) relevant difference because passing something of type language to argument c works (typeof(quote(list(first = 3, second = 4))) # language).

In order to overcome the problem above, I try to eval all arguments that are of type language:

outer3 <- function(a, b, c) {

parsedCall <- lapply(match.call()[-1L], FUN=function(argument) {
    if(is.language(argument)) {
      return(eval(argument))
    } else {
      return(argument)
    }
  })

  inner3(parsedCall)
}

inner3 <- function(parsedCall) {  
  return(parsedCall$a + parsedCall$b + parsedCall$c$first + parsedCall$c$second)
}

outer3(-1, 2, list(first = 3, second = 4)) # OK: 8

Question 2: The approach in outer3 seems to "work" but are there further pitfalls I need to take into account? (I know that in some cases it might be disadvantageous to evaluate the arguments but for my case this should not be an issue.)

Question 3: I suppose that the desire to pass all arguments to another function is not very uncommon. Is there a better/standard approach than what I did?

Question 4: Is it advantageous to pass the raw call to the inner function an do the eval stuff there? Would this be helpful if I would like to have the arguments as local variables in the inner functions (instead of elements of the parsedCall list)? Then, the body of inner3 could be identical to the body of inner1 (while with the current solution, I have to replace a+b with parsedCall$a + parsedCall$b).

like image 701
CL. Avatar asked Mar 29 '15 09:03

CL.


People also ask

How do you pass a function as a parameter to another function?

Function Call When calling a function with a function parameter, the value passed must be a pointer to a function. Use the function's name (without parentheses) for this: func(print); would call func , passing the print function to it.

How do you get all arguments in a function?

You can access specific arguments by calling their index. var add = function (num1, num2) { // returns the value of `num1` console. log(arguments[0]); // returns the value of `num2` console. log(arguments[1]); // ... };

What does match call do in R?

match. call returns a call in which all of the specified arguments are specified by their full names.

Which variable contains all the arguments passed to a function?

arguments is an Array -like object accessible inside functions that contains the values of the arguments passed to that function.


1 Answers

Regarding your question:

I suppose that the desire to pass all arguments to another function is not very uncommon. Is there a better/standard approach than what I did?

Then I would say this is a more common way to pass the arguments on:

inner1 <- function(a, b, c) { 
  return(a + b + c$first + c$second)
}

outer3 <- function(a, b, c) {
  mycall <- match.call()
  mycall[[1]] <- as.symbol("inner1") # use inner 1
  eval(mycall)
}

outer4 <- function(a, b, c) {
  .args <- as.list(match.call()[-1])
  do.call(inner1, .args)
}

outer3(-1, 2, list(first = 3, second = 4))
#R> [1] 8

outer4(-1, 2, list(first = 3, second = 4))
#R> [1] 8
like image 50
Benjamin Christoffersen Avatar answered Sep 27 '22 20:09

Benjamin Christoffersen