6

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.

baptiste
  • 75,767
  • 19
  • 198
  • 294

3 Answers3

5

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() )
      ) 
thelatemail
  • 91,185
  • 12
  • 128
  • 188
2

Evidently if we evaluate the functions in fun_wrap2 it works. The problem with the approach in the question is that the character strings get converted to functions inside one of the processing functions which changes the lookup path.

fun_wrap2 <- function(){

  funa1 <- function(x) x^2
  funb1 <- function(x) x^3

  nms <- c("funa1", "funb1")
  funs <- lapply(nms, match.fun)
  lapply(funs, do.call, list(x=3))

}

fun_wrap2()
G. Grothendieck
  • 254,981
  • 17
  • 203
  • 341
  • that makes sense, thanks for the added explanation. I've since realised that my problem is slightly different though, and I can't get this approach to work there (see Edit) – baptiste Sep 25 '14 at 01:08
  • while I really appreciate the help, i've accepted the other answer as it clarified the explicit passing of the right environment, which was key to the problem. `match.fun` achieved the same here, but perhaps not as explicitly. – baptiste Sep 25 '14 at 01:22
0

A slightly simpler version of @g-grothendieck's answer. Rather than using the function names, we just put the functions themselves into the list that is fed to lapply.

fun_wrap1 <- function(){
  funa1 <- function(x) x^2
  funb1 <- function(x) x^3
  lapply(list(funa1, funb1), do.call, list(x=3))
}

fun_wrap1()
Hong Ooi
  • 56,353
  • 13
  • 134
  • 187
  • 1
    That was my original answer but he indicated in the comments that the input must be a character vector of names so I changed it. – G. Grothendieck Sep 25 '14 at 00:37