我不认为乔希的回答是正确的。
好吧,如果在您的示例中位于调用堆栈上,那将是正确的。但事实并非如此。<-
小回顾:正常的 R 函数评估将参数视为在访问时延迟评估的承诺。这意味着在以下调用中:
foo(bar(baz))
bar(baz)
在内部 进行评估foo
(如果有的话)。因此,如果我们检查内部的调用堆栈bar
,如下所示:
bar = function (x) {
sys.calls()
}
…然后它看起来如下:
[[1]]
foo(bar(baz))
[[2]]
bar(baz)
唉,正如您所指出的,<-
(and =
) 不是一个普通函数,它是一个原始函数 ( BUILTINSXP
)。事实上,它在 R 源码中是这样定义的:
{"<-", do_set, 1, 100, -1, {PP_ASSIGN, PREC_LEFT, 1}},
看一下第四个参数:100
. 此代码之前的注释解释了数字的含义。这是相关部分,解释最左边的数字:
Z=1 表示在调用 ( BUILTINSXP
)之前评估参数
这意味着在分配之前bar(baz)
评估以下调用代码:
`<-`(x, bar(baz))
这就是为什么<-
没有出现在列表中的原因sys.calls()
:它不是当前调用。bar
它在完成评估后被调用。
有一种方法可以解决这个限制:您可以在 R 代码中重新定义<-
/ 。=
如果你这样做,它的行为就像一个普通的 R 函数:
`<-` = function (lhs, rhs) {
name = as.name(deparse(substitute(lhs), backtick = true))
rhs # evaluate expression before passing it to `bquote`, for a cleaner call stack
eval.parent(bquote(base::`<-`(.(name), .(rhs))))
}
但是,请注意,这将对重新定义范围内的每个后续分配产生不可忽略的性能影响<-
:事实上,它使分配大约慢了 1000 倍(!!!)。这通常是不可接受的。