简体   繁体   中英

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 ).

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM