2

I'd like to make below program work.

I know, the error lies in list(f1, f2), but I didn't find a way to make it work so far. The problem is that f1 and f2 are not known outside func's environment, but I'd like to just pass them as a name / symbol...

x = 1:2
func = function(f,x){
  f1 = function(x) 
    sum(x)
  f2 = function(x) 
    prod(x)
  eval(substitute(f))(x)
}
func(f1, x) # works
func(f2, x) # works
sapply(list(f1,f2), function(f) func(f,x)) # cannot iterate over f's
  • 1
    `sapply` is going to evaluate the list `list(f1,f2)` for you. You cannot control when evaluation happens with `sapply`. You could pass formulas; you could explicitly quote() the symbols; you could pass an `alist()`. But each of those would require sub adjustment on some part. It would help probably to say why you think you need such a syntax in the first place. – MrFlick Jan 11 '18 at 21:45
  • well, otherwise I'd have to write the call `func(fi,x)` n times, if I have n functions `f1` to `fn`, which is repetitive. – PeterPancake Jan 11 '18 at 21:56
  • 1
    But you'd have to type all those in the `list()` anyway. If you stored your functions in a named list, this would be much easier. `myfuns <- list(f1=sum, f2=prod); sapply(c("f1","f2"), function(name) myfuns[[name]](x))`. Or you could do them all with `Map(function(fn) fn(x), myfuns)`, Or if they were just functions defined in the working environment rather than just inside the closure. – MrFlick Jan 11 '18 at 21:59
  • Fair enough. The thing is, 1. my original definitions for `fi` are somewhat longer than only `sum` or `prod`, and 2. I don't want to screw up my workspace / global envir with these definitions, but rather like them to be encapsuled in another fcn – PeterPancake Jan 11 '18 at 22:04
  • 1
    Then I definitely think you should be storing them in a named list. Then you have just one object in your environment with all the definitions. And you can programmatically find out what functions are available or iterate over the entire list. – MrFlick Jan 11 '18 at 22:15
  • Yep, I changed the code now and think nse can be avoided. I just executed the `sapply()` inside `func()`, that's fine in my case, too without loosing too much on flexibility. – PeterPancake Jan 11 '18 at 22:43
  • I'm not seeing any nonstandard evaluatuion here. – IRTFM Jan 11 '18 at 22:45

3 Answers3

3

With this definition f can be a character string naming a function or an unevaluated expression naming a function.

func <- function(f, x) { 
  funs <- list(f1 = sum, f2 = prod)
  ch <- deparse(substitute(f))
  if (! ch %in% names(funs)) ch <- as.name(f)
  funs[[ch]](x)
}



func(f1, 1:2)
## [1] 3

func("f1", 1:2)
## [1] 3

sapply(c("f1", "f2"), func, x = 1:2)
## f1 f2 
##  3  2 

sapply(c(quote(f1), quote(f2)), func, x = 1:2)
## [1] 3 2

sapply(expression(f1, f2), func, x = 1:2)
## [1] 3 2

In a comment under the question, the poster mentioned that they may not use unevaluated expressions after all. If character strings only are used then it could be simplified to this:

func2 <- function(f, x) { 
  funs <- list(f1 = sum, f2 = prod)
  funs[[f]](x)
}

func2("f1", 1:2)
## [1] 3

sapply(c("f1", "f2"), func2, x = 1:2)
## f1 f2 
##  3  2 

Updates

Corrections and improvements.

G. Grothendieck
  • 254,981
  • 17
  • 203
  • 341
  • Added a character string only solution since poster mentioned in a comment that they may not use unevaluated expressions after all. – G. Grothendieck Jan 12 '18 at 00:41
1

Okay, found a solution, but I guess it can be done somewhat prettier...

x = 1:2
func = function(f,x){
  f1 = function(x) 
    sum(x)
  f2 = function(x) 
    prod(x)
  eval(f)(x)
}
sapply(list(substitute(f1),substitute(f2)), function(f) func(f,x)) # can now iterate over f's
1

I don't know how you would do it with NSE, but you can just use get instead of substitute. This seems more neat to me, since you could use the strings to more easily name the outputs

x = 1:2
func = function(f,x){
  f1 = function(x) 
    sum(x)
  f2 = function(x) 
    prod(x)
  eval(get(f))(x)
}
func("f1", x)
func("f2", x) 
sapply(list("f1", "f2"), function(f) func(f,x))
alan ocallaghan
  • 3,116
  • 17
  • 37