为什么包装函数不能按预期工作?

时间:2014-06-29 06:15:16

标签: r expression eval

这里有四个函数,后者包含了前者。

a <- 0

f1 <- function(expr) {
  a1 <- 1
  eval(expr)
}

f2 <- function(expr) {
  a2 <- 2
  f1(expr)
}

f3 <- function(expr) {
  a3 <- 3
  f2(expr)
}

f4 <- function(expr) {
  a4 <- 4
  f3(expr)
}

执行以下体验:

> f4(a)
0

按预期工作。但是如果我们打电话

  

F4(a4)的       eval(expr)出错:找不到对象'a4'

> f4(a3)
Error in eval(expr) : object 'a3' not found

...

> f2(a2)
Error in eval(expr) : object 'a2' not found

> f2(a1)
Error in eval(expr) : object 'a1' not found

> f1(a1)
Error in eval(expr) : object 'a1' not found

我检查每个函数体的局部环境和父环境f3的父框架是f4的本地环境,......,f1的父元素是{{ 1}}的身体。这是否清楚地解释了为什么会这样?我如何摆脱这个问题,使代码工作的目的是函数调用应该允许后续函数(如f2)找到定义的符号(例如f3)?

1 个答案:

答案 0 :(得分:8)

我强烈建议您花一些时间阅读Advanced R: Environments

首先,当我运行f1(a1)时,我也找不到“对象'a1';当你超越时,不是“1”。

问题是默认情况下,R使用函数的封闭环境解析变量。函数的封闭环境是在定义函数时确定的,而不是在调用函数时确定的。因此,它不会通过调用链来解析变量名称。您可以使用parent.frame()环境显式查找调用父级,但这些环境不会在嵌套函数调用中链接在一起。

get()通过沿着封闭的父环境走上来循环变量的方式相同,您可以创建自己的函数来调用调用环境并查看哪些变量可用。

call.get <- function(val) {
    for(i in 1:sys.nframe()) {
        if (exists(val, envir=sys.frame(i), inherits=F)) {
            return(get(val, envir=sys.frame(i)))
        }
    }
    return(NULL)
}

call.ls <- function(val) {
    vars<-lapply(1:sys.nframe(), function(i) ls(envir=parent.frame(i)))
    return(sort(unique(unlist(vars))))
}

然后,如果您执行类似

的操作
f1 <- function(expr) {
  a1 <- 1
  call.ls()
}

f2 <- function(expr) {
  a2 <- 2
  f1(expr)
}

f3 <- function(expr) {
  a3 <- 3
  f2(expr)
}

f4 <- function(expr) {
  a4 <- 4
  f3(expr)
}

f4(1)

你会得到

"a1"   "a2"   "a3"   "expr" "FUN"  "val"  "X"  

你可以使用

call.get("a3")

从父调用帧中获取其中一个变量。

但另一个问题是,当您调用子函数时,您将触发对expr参数的求值。当你这样做

f2 <- function(expr) {
  a2 <- 2
  f1(expr)
}

评估expr环境中的f2并将结果传递给f1。那时你正在失去评估。通过延迟评估的最简单方法是使用“...”。像

这样的东西
f1 <- function(...) {
    a1 <- 1
    expr<-deparse(substitute(...))
    call.get(expr)
}
f2 <- function(...) {
    a2 <- 2
    f1(...)
}
f2(a1)
# [1] 1
f2(a2)
# [1] 2

否则,您需要使用do.call

更明确地传递表达式
f1 <- function(expr) {
    a1 <- 1
    expr<-deparse(substitute(expr))
    call.get(expr)
}
f2 <- function(expr) {
    expr<-substitute(expr)
    a2 <- 2
    do.call(f1, list(expr))
}

f2(a1)
# [1] 1
f2(a2)
# [1] 2