对于 envir 参数是 list 或 pairlist 的情况,R eval 有误导性文档

R eval has misleading documentation for the case that the envir argument is list or pairlist

我不确定是否应该在 R-devel 上发布(如果是,请告诉我...)但是 R 的 [=15= 的文档中似乎至少有一个错误] 函数,这对于 R 的非标准评估功能至关重要,或者我错了什么?

> eval   
function (expr, envir = parent.frame(), enclos = if (is.list(envir) || 
    is.pairlist(envir)) parent.frame() else baseenv()) 
.Internal(eval(expr, envir, enclos))
<bytecode: 0x0000000009534280>
<environment: namespace:base>

这部分以及 evalenclos 论点的帮助

> Relevant when envir is a (pair)list or a data frame. Specifies the
enclosure, i.e., where R looks for objects not found in envir. This can be
NULL (interpreted as the base package environment, baseenv()) or an
environment.

表示每当将列表提供给 eval()envir 参数时,enclos 的值(懒惰地)计算为 parent.frame(),因此它应该与另外提供 parent.frame() 相同(但这次是直接提供)。但是,请注意以下两个代码块我只更改了它,不要 return 相同。

> rm(list = ls(all = TRUE))
> f1 <- function(){
   y <- 2
   f2 <- function(){
     eval(quote(y), envir = list())
   }
   f2
 }
> f3 <- f1()
> f3()
[1] 2

> rm(list = ls(all = TRUE))
> f1 <- function(){
   y <- 2
   f2 <- function(){
     eval(quote(y), envir = list(),
          enclos = parent.frame())
   }
   f2
 }
> f3 <- f1()
> f3()
Error in eval(expr, envir, enclos) : object 'y' not found 

所以我能从这里得到的唯一结论是,对于数据框和列表,默认情况下的封闭环境似乎不是 parent.env()。相反,R 似乎正在查看当前的执行环境。这是我能在 ?eval 中找到的唯一相关部分:

> When evaluating expressions in a data frame that has been passed as an
argument to a function, the relevant enclosure is often the caller's 
environment, i.e., one needs eval(x, data, parent.frame()).

这以某种方式表明,应该明确设置 parent.frame()。并且上面指出的行为确实不是 eval() 默认情况下所做的......(至少在这种情况下,当 envir 设置为 list/pairlist 时)。

对我来说这很令人困惑。但是,我不确定我是否弄错了,如果有任何答复或评论,我将不胜感激。

对于这个具体的例子,在第一种情况下,f3的定义是:

f3
#function(){
#     eval(quote(y), envir = list())
#   }
#<environment: 0x02a41584>

默认的 enclos = parent.frame() 参数将在 eval 的评估框架(即 "inside")中进行评估 -- eval 当前环境的父级是当前环境environment() 其包装函数 (f3)。及其创建的包装函数 "remembers" 并正确搜索 y 以找到它。

在第二种情况下,f3定义为:

f3
#function(){
#     eval(quote(y), envir = list(),
#          enclos = parent.frame())
#   }
#<environment: 0x02a4e5e4>

此处,enclos = parent.frame()f3 的求值 environment() 中求值,并且在调用 f3() 时,其父级是 .GlobalEnv,其中没有 y.

作为一个更清晰的示例(但与示例同样嵌套),我们可以考虑:

f0 = function(e) print(e)
f1 = function(e = parent.frame()) f0(e)
fA = function() #returns a function
{
    function() 
    { 
        print(parent.frame())
        print(environment())
        f1() 
    }
}   
fB = function() # returns a function
{
    function() 
    { 
        print(parent.frame())
        print(environment())
        f1(parent.frame()) 
    }
}   

然后调用:

fA()()
#<environment: R_GlobalEnv>  #<- parent of `fA()`'s `environment()`
#<environment: 0x06ff25bc>   #<- current env of `fA()`
#<environment: 0x06ff25bc>   #<- parent of `f1` == current of `fA()`

fB()()
#<environment: R_GlobalEnv>  #<- parent of `fA()`'s `environment()`
#<environment: 0x06fec304>   #<- current env of `fA()`
#<environment: R_GlobalEnv>  #<- parent is `eval`ed as parent of `fA()`'s current

正如 Taz 所说,默认参数和提供的参数之间的区别在 definition 手册中有说明。稍微绕一圈,我们可以通过尝试实现参数的承诺来看到它的实际效果:

首先,一个辅助函数来访问当前参数的承诺:

.ff = inline::cfunction(sig = c(symarg = "symbol", env = "environment", penv = "environment"), body = '
    SEXP arg = findVar(symarg, env), ans = allocVector(VECSXP, 5);
    SET_VECTOR_ELT(ans, 0, PRCODE(arg)); 
    SET_VECTOR_ELT(ans, 1, PRENV(arg));
    SET_VECTOR_ELT(ans, 2, eval(PRCODE(arg), PRENV(arg)));
    SET_VECTOR_ELT(ans, 3, env);
    SET_VECTOR_ELT(ans, 4, penv);
    return(ans);
')

以及我们将跟踪其参数的函数:

ff = function(arg = parent.frame())
{
    ans = setNames(.ff(quote(arg), environment(), parent.frame()), 
                   c("expr", "envir", "val", "cur", "par"))

    cat(sprintf("promise:\n\tcall: '%s'\n\tsearched at: '%s'\n\tfound as: '%s'\ncurrent: '%s'\nparent: '%s'\n%s\n", 
                deparse(ans$expr), capture.output(ans$envir), 
                capture.output(ans$val), capture.output(ans$cur),
                capture.output(ans$par), strrep("-", 40)))

    return(invisible(ans))
}

还有一个简单的例子:

ff()
#promise:
#        call: 'parent.frame()'
#        searched at: '<environment: 0x06fff594>'
#        found as: '<environment: R_GlobalEnv>'
#current: '<environment: 0x06fff594>'
#parent: '<environment: R_GlobalEnv>'
#----------------------------------------

ff(parent.frame())
#promise:
#        call: 'parent.frame()'
#        searched at: '<environment: R_GlobalEnv>'
#        found as: '<environment: R_GlobalEnv>'
#current: '<environment: 0x06fcce20>'
#parent: '<environment: R_GlobalEnv>'
#----------------------------------------

或者更嵌套的情况:

fnest1 = function() ff()
fnest2 = function() ff(parent.frame())

fnest1()
#promise:
#        call: 'parent.frame()'
#        searched at: '<environment: 0x028d1ccc>'
#        found as: '<environment: 0x028d1d20>'
#current: '<environment: 0x028d1ccc>'
#parent: '<environment: 0x028d1d20>'
#----------------------------------------

fnest2()
#promise:
#        call: 'parent.frame()'
#        searched at: '<environment: 0x026866b8>'
#        found as: '<environment: R_GlobalEnv>'
#current: '<environment: 0x0268662c>'
#parent: '<environment: 0x026866b8>'
#----------------------------------------