简体   繁体   English

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

[英]Why wrapper functions do not work as expected?

Here are four functions, the latter ones wraps the former ones. 这里有四个函数,后者包含前者。

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

Do the following experienments: 做以下经验:

> f4(a)
0

which works as expected. 它按预期工作。 But if we call 但如果我们打电话

f4(a4) Error in eval(expr) : object 'a4' not found 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

I inspect the local environment and parent environment of each function body f3 's parent frame is f4 's local environment, ... , f1 's parent is f2 's body. 我检查每个函数体的局部环境和父环境f3的父框架是f4的局部环境,..., f1的父元素是f2的主体。 Is it a clear explanation why this happens? 这是否清楚地解释了为什么会这样? And how can I get rid of this problem to make the code work for the purpose that the function call should allow subsequent functions (like f3 ) to find the defined symbols (eg a4 )? 我如何摆脱这个问题,使代码工作的目的是函数调用应该允许后续函数(如f3 )找到定义的符号(例如a4 )?

I strongly recommend you spend some time reading Advanced R: Environments . 我强烈建议您花一些时间阅读Advanced R:Environments

First of all, when I run f1(a1) I get "object 'a1' not found" as well; 首先,当我运行f1(a1)我也找不到“对象'a1'; not "1" as you get above. 当你超越时,不是“1”。

The issue is that by default R resolves variables using the enclosing environment of a function. 问题在于,默认情况下,R使用函数的封闭环境解析变量。 The enclosing environment of a function is determined when the function is defined, not when the function is called. 函数的封闭环境是在定义函数时确定的,而不是在调用函数时确定的。 Therefore it doesn't go up the call chain to resolve variable names. 因此,它不会通过调用链来解析变量名称。 You can explicitly look in a calling parent with the parent.frame() environment, but these environments do not chain together in nested function calls. 您可以使用parent.frame()环境显式查看调用父parent.frame() ,但这些环境不会在嵌套函数调用中链接在一起。

In the same way that get() will loop up a variable by walking up the enclosing parent environments, you can make your own function to walk up the calling environments and see which variables are available. 就像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))))
}

Then if you do something like 然后,如果你做的事情

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)

You will get 你会得到

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

and you can use 你可以使用

call.get("a3")

to get one of those variables from a parent calling frame. 从父调用帧中获取其中一个变量。

But another problem you have is you are triggering evaluation of the expr argument when you call the sub-function. 但另一个问题是,当您调用子函数时,您将触发对expr参数的求值。 When you do 当你这样做

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

That evaluates expr in the f2 environment and passes the result to f1 . 它在f2环境中计算expr并将结果传递给f1 You are losing the evaluation at that point. 那时你正在失去评估。 The easiest way to pass through a lazy-evaluation is to use "...". 通过延迟评估的最简单方法是使用“...”。 Something like 就像是

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

Otherwise you need to more explicitly pass the expression with a do.call 否则,您需要使用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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM