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

Kun*_*Ren 3 expression eval r

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

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)
}
Run Code Online (Sandbox Code Playgroud)

做以下经验:

> f4(a)
0
Run Code Online (Sandbox Code Playgroud)

它按预期工作.但如果我们打电话

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
Run Code Online (Sandbox Code Playgroud)

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

MrF*_*ick 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))))
}
Run Code Online (Sandbox Code Playgroud)

然后,如果你做的事情

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)
Run Code Online (Sandbox Code Playgroud)

你会得到

"a1"   "a2"   "a3"   "expr" "FUN"  "val"  "X"  
Run Code Online (Sandbox Code Playgroud)

你可以使用

call.get("a3")
Run Code Online (Sandbox Code Playgroud)

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

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

f2 <- function(expr) {
  a2 <- 2
  f1(expr)
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

否则,您需要使用a更明确地传递表达式 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
Run Code Online (Sandbox Code Playgroud)

  • @KunRen如果你发现这回答了你的问题,你应该点击这个答案旁边的复选标记来接受它并关闭问题. (2认同)