I would like to be able to pass named objects in the arguments of a function that have been matched to ellipsis (..., AKA dots) to be made generally available in the execution environment of that function, or of functions executed inside that environment wherever defined, as if the arguments had been typed there.
I tried to do this, for a function, a nested function defined outside of that function, and a nested function defined inside of that function, using list2env(), which is supposed to return the elements of its argument list into the parent.frame() environment, which I understand to be the calling environment. Thus:
# Ellipsis in nested functions
inner_f1<- function(x){list2env(list(...)[-1L]); x + b}
outer_f <- function(x, ...){
list2env(list(...)[-1L])
in01 <- x + a
inner_f2 <- function(x){list2env(list(...)[-1L]); print(ls()); x + c}
in02 <- inner_f2(x)
in03 <- inner_f1(x)
out <- list(in01, in02, in03)
}
outer_f(x=0, a=1, b=2, c=3)
I tried this with and without ...'s in the definitions of the nested functions, but neither works. The desired output would be:
$ in01
[1] 1
$ in02
[1] 2
$ in03
[1] 3
The R help file under "dots" provides no information on passing ... values to interior functions, and the only way it mentions of getting information out of a ... is through the ..(n) method. It refers to "An Introduction to R," but the par example seems to suggest, falsely, that it is sufficient for the interior function to have its own ..., although the par code (not cited there) gets at the contents by doing complicated things to args = list(...)
, and the R language definition also describes the list(...) method. I have not found the idiom substitute(list(...))[-1], frequently used in the R base packages, officially documented anywhere, but in any case neither this nor the eval(substitute(alist(...))) from from "Advanced R"::Nonstandard Evaluation seem to do what I want.
There are a lot of answers to questions about ...s and nested functions here on stackoverflow, but all of the 15 or so that I read seem more specialized than the generalized method I am seeking.
Note that the variables that are available is not identical to the variables listed by ls()
. In particular, ls()
won't list the variables in the parent environment but the variables in the parent environment are still available as inputs (and also as outputs if you use <<-). We assume you just want the variables available and don't care about ls()
. (If you do want to actually inject the variables from the outer function's execution environment into the inner function then pass ... to the inner function and use the same methods as shown here for the outer function.) The following example shows that while b is accessible it is not shown in the output of ls()
.
f1 <- function() {
b <- 1
f2 <- function() {
print(b) # will print the value of b showing b is accessible
print(ls()) # no variables will be shown
}
f2()
}
f1()
giving:
[1] 1
character(0)
Now to get back to the question here are some alternatives:
1) with Try with
:
inner_fun0 <- function() in1
outer_fun <- function(...) with(list(...), {
inner_fun <- function() in1
environment(inner_fun0) <- environment()
list(in1, inner_fun(), inner_fun0())
})
outer_fun(in1 = 7)
giving:
[[1]]
[1] 7
[[2]]
[1] 7
[[3]]
[1] 7
2) list2env An alternative is to use list2env
like this:
outer_fun2 <- function(...) {
list2env(list(...), environment())
inner_fun <- function() in1
environment(inner_fun0) <- environment()
list(in1, inner_fun(), inner_fun0())
}
outer_fun2(in1 = 7)
giving:
[[1]]
[1] 7
[[2]]
[1] 7
[[3]]
[1] 7
You can construct additional variations using the ideas in overwrite variable in parent function from inner function without making variable outside parent function
Also the proto package could be used to recast all this into an object oriented framework.
An alternative:
# note how I add an argument to specify the exact environment:
inner_f1<- function(x, env, ...){
with(env, {list2env(list(...)); x + b})
}
outer_f <- function(x, ...){
# get the "address", i.e. the environment:
here <- environment()
# specify the environment in list2env:
list2env(list(...), envir = here)
in01 <- x + a
inner_f2 <- function(x){list2env(list(...), envir = here); print(ls()); x + c}
# alternatively, use: list2env(list(...), envir = parent.frame(n=2))
in02 <- inner_f2(x)
in03 <- inner_f1(x, here)
out <- list(in01, in02, in03)
return(out)
}
outer_f(x=0, a=1, b=2, c=3)
[1] "x"
[[1]]
[1] 1
[[2]]
[1] 3
[[3]]
[1] 2
In essence, you need to make sure the correct "address" is available for all functions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With