I'm using the following construct in a package,
## two functions in the global environment
funa <- function(x) x^2
funb <- function(x) x^3
## called within a function, fine
fun_wrap <- function(){
lapply(c('funa', 'funb'), do.call, list(x=3))
}
fun_wrap()
[[1]]
[1] 9
[[2]]
[1] 27
but I've just been bitten by the fact that it won't work if the functions are in a different (local) frame,
## same construct, but the functions are local
fun_wrap1 <- function(){
funa1 <- function(x) x^2
funb1 <- function(x) x^3
lapply(c('funa1', 'funb1'), do.call, list(x=3))
}
## now it fails
fun_wrap1()
##Error in FUN(c("funa1", "funb1")[[1L]], ...) :
## could not find function "funa1"
I've tried passing envir=parent.frame(2)
to do.call()
(doesn't work); frankly the help page of ?parent.frame
goes way over my head. Any hint for a more robust use of do.call?
Note that the list of functions comes as a character vector, passed from another piece of code; I prefer not to pass the functions directly.
Edit: one more twist... I thought I'd illustrated the right problem with my toy example, but the actual code I'm using is slightly different, in the sense that I'm calling fun_wrap1
within a separate function. The proposed solutions fail in this context.
fun_wrap1 <- function(funs){
lapply(funs, do.call, args=list(x=3), envir=environment())
}
foo <- function(){
funa1 <- function(x) x^2
funb1 <- function(x) x^3
fun_wrap1(c('funa1', 'funb1'))
}
foo()
##Error in FUN(c("funa1", "funb1")[[1L]], ...) :
## could not find function "funa1"
(and the same happens with the match.fun
approach)
I can get it to work by passing an optional environment to fun_wrap1
,
fun_wrap1 <- function(funs, e=parent.frame()){
lapply(funs, do.call, args=list(x=3), envir=e)
}
foo <- function(){
funa1 <- function(x) x^2
funb1 <- function(x) x^3
fun_wrap1(c('funa1', 'funb1'))
}
foo()
and that's hopefully it.
You can't make a function call except from within a function, so the answer to the headline question is "yes: it is not only good practice; it is unavoidable".
In R, the environment() function returns the environment associated with a given function or formula. An environment is a collection of objects such as functions, variables, and so on. Whenever we hit up the R interpreter, an environment is created.
The parent environment of a function is the environment in which the function was created. If a function was created in the execution environment (for example, in the global environment), then the environment in which the function was called will be the same as the environment in which the function was created.
When a function is evaluated, R looks in a series of environments for any variables in scope. The evaluation environment is first, then the function's enclosing environment, which will be the global environment for functions defined in the workspace.
This seems to work, but i'm not sure if it has other implications I'm not considering:
fun_wrap1 <- function(){
funa1 <- function(x) x^2
funb1 <- function(x) x^3
lapply(c('funa1', 'funb1'), do.call, args=list(x=3), envir=environment())
}
fun_wrap1()
#[[1]]
#[1] 9
#
#[[2]]
#[1] 27
So this is essentially equivalent to having the lapply
statement as:
lapply(
c('funa1', 'funb1'),
function(f) do.call(f, args=list(x=3), envir=environment() )
)
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