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