对于 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>
这部分以及 eval
对 enclos
论点的帮助
> 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>'
#----------------------------------------
我不确定是否应该在 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>
这部分以及 eval
对 enclos
论点的帮助
> 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>'
#----------------------------------------