How to evaluate function call arguments inside another function in R

I'm having trouble understanding how to work with nested function calls and argument evaluations.

Here is a simple example. I have a top-level function topfunction with one numeric argument. Inside the topfunction I call another lowerfunction , whose argument is to call the function defined inside the lowerfunction .

 topfunction<-function(x){ lowerfunction(myfun(first=x[1],second=x[2],third=if(length(x)>2) x[3])) } lowerfunction<-function(mycall){ myfun<-function(first,second=0,third=NULL){ print(first) print(second) print(third) } mc<-match.call(definition = myfun, call = match.call()[[2]]) eval(mc) } 

Inside the lowerfunction I capture a function call with match.call and try to evaluate the call. But since the variable x is defined only in the topfunction environment, the evaluation is not performed:

 topfunction(x=1:3) Error in print(first) : object 'x' not found 

I know that I could change the line

 lowerfunction(myfun(first=x[1],second=x[2],third=if(length(x)>2) x[3])) 

a

 lowerfunction(substitute(myfun(first=x[1],second=x[2],third=if(length(x)>2) x[3]))) 

in topfunction , but in my real application, the topfunction is created by the user, so the solution should happen somehow at the lowerfunction level or even at the myfun level. But since they have already lost information about x , I do not know if this can be done?

In a real application, topfunction builds a model using lowerfunction and calculates its likelihood, while the lowerfunction argument is a formula that can contain function calls that will be evaluated using eval . These functions are defined only within the lowerfunction . In addition, lowerfunction can also be called directly, i.e.

 x<-1:3 lowerfunction(myfun(first=x[1],second=x[2],third=if(length(x)>2) x[3])) # or lowerfunction(myfun(first=x1,second=2) 

Therefore, solutions that add x to the lowerfunction argument list are not applicable at all.

Thus, the problem is that eval must take the definition of myfun from one environment (the package namespace or in this case from the lowerfunction environment) and evaluate the arguments of myfun in another environment i.e. in a topfunction environment.

+5
source share
2 answers

This is a relatively simple problem, but since you are doing a very non-standard assessment, you need to create a new environment and everything so that all the objects you need are accessible from this environment.

 g <- function(x){ f1(f2(x[1], x[2], if(length(x) > 2) x[3])) } f1 <- function(mycall, parent = parent.frame()) { # Parent contains x # New environment that contains f2 and inherits from the parent env <- new.env(parent = parent) env$f2 <- function(first, second = 0,third = NULL) { print(first) print(second) print(third) } # More idiomatic way of getting unevaluated expression expr <- substitute(mycall) eval(expr, env) } g(1:3) 

My chapter describes similar methods in domain languages

+4
source

Raise myfun from lowerfun and change the call to eval as shown below. When you create a package, if you do not export myfun , it will not be available directly from R_GlobalEnv , but it can still be called from lowerfun .

 topfunction <- function(x){ lowerfunction(myfun(first=x[1], second=x[2], third=if(length(x)>2) x[3])) } lowerfunction<-function(mycall){ mc <- match.call(definition = myfun, call = match.call()[[2]]) eval(mc, envir=parent.frame()) } myfun <- function(first, second=0, third=NULL){ print(first) print(second) print(third) } 

Execution Example:

 > topfunction(1:3) [1] 1 [1] 2 [1] 3 

You can still disable the myfun theme from R_GlobalEnv by calling

 getFromNamespace("myfun", "mypackage") 

Update

If you really want to keep myfun inside a lowerfunction in order to keep the conceptual point, you need to combine the topfunction and lowerfunction and evaluate mc there, but I don't know if this is possible (which turned out to be, see @hadley answer).

However, you can copy variables not found in the lowerfunction environmenttment (i.e. x ) from the topfunction environment before evaluating. Thanks to a lazy assessment, this does not affect memory usage unless changed.

 lowerfunction<-function(mycall){ myfun <- function(first, second=0, third=NULL){ print(first) print(second) print(third) } mc <- match.call(definition = myfun, call = match.call()[[2]]) x <- get("x", parent.frame()) eval(mc) } 

However, since you do not know what objects the user will include in the topfunction , you cannot hardcode it as described above, but you must do this by extracting all the names from mc and copying them through assign . It is possible, but I recommend that you save problems and export both lowerfunction and myfun .

+2
source

Source: https://habr.com/ru/post/953031/


All Articles