7

我无法理解如何使用嵌套函数调用和参数评估。

这是一个简单的例子。topfunction我有一个带有一个数字参数的顶级函数。在里面topfunction我调用另一个函数lowerfunction,该函数的参数是对定义在里面的函数的调用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) 
}

在里面lowerfunction我用 捕获函数调用match.call,并尝试评估调用。但由于变量x仅在 的环境中定义topfunction,因此评估失败:

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

我知道我可以换线

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

作为

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

topfunction,但在我的实际应用程序中,topfunction是由用户构建的,因此解决方案应该以某种方式发生在关卡中,lowerfunction甚至在myfun关卡中。但是由于他们已经丢失了有关的信息x,我不知道这是否可以实现?

在实际应用中,topfunction使用构建模型lowerfunction并计算其似然性,而 的参数lowerfunction是一个可以包含函数调用的公式,将通过eval. 这些函数仅在lowerfunction. 另外,lowerfunction也可以直接调用,即

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)

因此,添加x到参数列表中的解决方案lowerfunction通常不适用。

所以问题是eval应该myfun从一个环境(包命名空间,或者在这种情况下从环境lowerfunction)获取的定义,并在其他环境中评估参数,myfun即在环境中topfunction

4

2 回答 2

5

这是一个相对简单的问题,但由于您正在进行非常非标准的评估,您需要创建一个新环境并确保可以从该环境访问您需要的所有对象。

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)

我在关于领域特定语言的章节中描述了类似的技术

于 2013-09-03T13:53:53.310 回答
3

Lift myfun out of lowerfun and modify the eval call as below. When making a package, if you do not export myfun it will not be accessible directly from R_GlobalEnv, but it can still be called from within 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)
}

Example run:

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

Off topic: myfun can still be accessed from R_GlobalEnv by calling

getFromNamespace("myfun", "mypackage")

Update

If you really want to keep myfun within lowerfunction, to preserve a conceptual point, you would need to merge the environments of topfunction and lowerfunction and evaluate mc there, but I do not know if that is possible (which it turned out to be, see @hadley's answer).

However, you can copy the variables not found in lowerfunction's environtment (i.e. x) from topfunction's environment prior to evaluation. Thanks to the lazy evaluation, this does not affect memory usage unless they are modified.

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 again, since you do not know what objects the user will incorporate into topfunction, you cannot hard code it as above but must do it by extracting all names from mc and copy them via assign. It is possible, but I recommend you save yourself the trouble and export both lowerfunction and myfun.

于 2013-09-03T08:10:59.077 回答