Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elegant way to define a function inside another function

Tags:

r

I want to construct

f <- function(...) {
   g <- function(x) x ^ 2
   list(...)
}

so that I can invoke using f(g(4)) and have list(...) result in list(16).

In general I will define several temporary functions inside f that the user can invoke when calling f(...).

I have experimented with assign and newenvironment but have just gotten more confused. Help with an elegant solution is appreciated.

The reason for wanting this is that I want a function in the Hmisc package, drawPlot to be able to let the users specify generically named functions as input for building up a series of graphical elements, and I don't want to reserve these generic-type names. E.g.:

d <- drawPlot(Curve(), Points())   # interactively make a curve and
                                   # a set of points
like image 311
Frank Harrell Avatar asked Dec 24 '22 12:12

Frank Harrell


2 Answers

I'm guessing you in fact need something more elaborate than this, but the following does what you've asked for in your supplied example:

f <- function(...) {
   g <- function(x) x ^ 2
   list(eval(substitute(...)))
}

f(g(4))
# [[1]]
# [1] 16

Or, if users may supply one or more function calls, something like this:

f <- function(...) {
   g <- function(x) x ^ 2
   h <- function(x) 100*x
   cc <- as.list(substitute(list(...))[-1])
   res <- list()
   for(i in seq_along(cc)) {
       res[[i]] <- eval(cc[[i]])
   }
   res
}
f(g(4), h(5))
# [[1]]
# [1] 16
# 
# [[2]]
# [1] 500
like image 89
Josh O'Brien Avatar answered Jan 18 '23 06:01

Josh O'Brien


Very similar to this answer but I think maybe more extensible and closer to your original idea:

match.fun_wrapper <- function(...) {
  #   `match.fun` searches in the parent environment of the environment that
  # calls `match.fun`, so this wrapper is a hack to be able to search in
  # the current environment rather than the parent of the current environemnt
  match.fun(...)
}

f <- function(fun, ...) {
  g <- function(x) x ^ 2
  fun <- match.fun_wrapper(substitute(fun))
  fun(...)
}

If you wanted to do away with match.fun, you could also do away with the wrapper hack:

f <- function(fun, ...) {
  g <- function(x) x ^ 2
  fun(...)
}
like image 25
shadowtalker Avatar answered Jan 18 '23 06:01

shadowtalker