如何在R中的另一个函数内部计算函数调用的参数

时间:2013-09-03 07:43:53

标签: r function arguments lazy-evaluation

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

这是一个简单的例子。我有一个带有一个数字参数的顶级函数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])))
<{1>}中的

,但在我的实际应用中,topfunction是由用户构建的,因此解决方案应该以{{1​​}}甚至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的环境中。

2 个答案:

答案 0 :(得分:4)

这是一个相对简单的问题,但由于您正在进行非常非标准评估,因此您需要创建一个新的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)

我在domain specific languages

的章节中描述了类似的技巧

答案 1 :(得分:2)

myfun中提升lowerfun并修改eval来电,如下所示。制作包时,如果您不导出myfun,则无法直接从R_GlobalEnv访问该包,但仍可以从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)
}

示例运行:

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

关闭主题: myfun仍可通过致电R_GlobalEnvgetFromNamespace("myfun", "mypackage") 访问

myfun

更新

如果您真的希望将lowerfunction保留在topfunction内,为了保留概念点,您需要合并lowerfunctionmc的环境并评估{{ 1}}那里,但我不知道这是否可能(结果是,见@ hadley的回答)。

但是,您可以在评估之前从lowerfunction的环境中复制x的环境中未找到的变量( topfunction)。由于延迟评估,这不会影响内存使用,除非它们被修改。

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)
}

然而,再次,由于您不知道用户将合并到topfunction中的对象,您不能像上面那样硬编码,但必须通过从{{1提取所有名称来完成并通过mc复制它们。这是有可能的,但我建议您省去麻烦并导出assignlowerfunction