简体   繁体   中英

Problem passing arguments to called functions in R

Context: I am trying to use a function ( caller ) that calls other functions ( callees ). Each callee function uses a set of arguments that can be different from a function to another.

Problem: it seems like I have to use arguments in the caller function in order to have them passed to the callees functions (see fun_d in the example below).

Question: how to use ellipsis to avoid using explicit arguments in the caller function?

Using ellipsis in nested functions and Running a function with multiple arguments when an argument is not used seems not to answer this (or I do not understand correctly).

Reprex:

# fun_a and fun_b have the same set of argument but do not do the same things with them
fun_a <- function(x, y, ...){x+y}
fun_b <- function(x, y, ...){x-y}

# I would like to use fun_a and fun_b in another function (fun_c) AND, have the possibility to 
# give different values for each argument of fun_a and fun_b (ex: y1 and y2)

# I thought I could use the ellipsis like in fun_c:
fun_c <- function(...){
  fa <- fun_a(x = x, y = y1)
  fb <- fun_b(x = x, y = y2)
  paste(fa, fb)
}
# fun_d works but I you like to understand why func_c does not
fun_d <- function(x, y1, y2, ...){
  fa <- fun_a(x = x, y = y1)
  fb <- fun_b(x = x, y = y2)
  paste(fa, fb)
}

mapply(FUN = fun_c, x = c(1, 2, 3), y1 = c(1, 2, 3), y2 = c(0, 0, 0)) # not working, it says "x" is missing (and I suppose "y" too)
#> Error in fun_a(x = x, y = y1): object 'x' not found
mapply(FUN = fun_d, x = c(1, 2, 3), y1 = c(1, 2, 3), y2 = c(0, 0, 0)) # working
#> [1] "2 1" "4 2" "6 3"

Created on 2021-06-23 by the reprex package (v2.0.0)

... allows callers of a function to pass arbitrary arguments into the function.

But it does not create corresponding parameter variables inside the function. If you want to use arguments passed via ... inside a function, you have these choices:

  1. You can pass ... on as-is. For example:

     print_two_vectors = function (x, y, ...) { print(x, ...) print(y, ...) }

    This can be used to pass arbitrary arguments on to the regular print function:

     print_two_vectors(pi, exp(1), digits = 2L)
     [1] 3.1 [1] 2.7

    This should be the most frequent use of ... . For most other purposes, you should instead accept regular parameters.

  2. You can access them in order via ..1 , ..2 , etc. ...elt(n) gives you the n th argument. To find out how many arguments are passed, you can use ...length() :

     example = function (...) { message('Got ', ...length(), ' arguments. The first two are: ', toString(c(..1, ..2))) message('The last one is: ', toString(...elt(...length()))) }

    Here's how the output would look like:

     example(1, 2, 3, 4)
     Got 4 arguments. The first two are: 1, 2 The last one is: 4
  3. You can unpack them into a list. Strictly speaking, this is the same as (1), ie you're just passing ... on to the list function. This allows you to access the elements by name , since lists can be named:

     add_xy = function (...) { args = list(...) args$x + args$y }
     add_xy(x = 1, y = 2)
     [1] 3

    … OK, that example is a bit useless. But you can use the same to solve your issue.

  4. You can access the value of ... in an unevaluated context. This is an advanced topic . It's not often useful in “regular” usage of R, but it becomes powerful when using non-standard evaluation for metaprogramming .

     add_xy2 = function (...) { call = match.call() eval.parent(call$x) + eval.parent(call$y) }
     add_xy2(x = 1, y = 2)
     [1] 3

In my experience, this issue can often be solved by pulling the arguments into the namespace like so (untested):

fun_c <- function(...){
  with(list(...), {
    fa <- fun_a(x = x, y = y1)
    fb <- fun_b(x = x, y = y2)
    paste(fa, fb)
  })
}

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