R 解释一个懒惰的评估怪癖

R 解释一个懒惰的评估怪癖,r,lazy-evaluation,R,Lazy Evaluation,我正在读哈德利·威克汉姆关于Github的书,尤其是。在那里,他给出了一个惰性计算结果的例子,在add/adders函数部分。让我引用这一点: 当使用lappy或循环创建闭包时,此[延迟评估]非常重要: add <- function(x) { function(y) x + y } adders <- lapply(1:10, add) adders[[1]](10) adders[[10]](10) 添加以下目标: adders <- lapply(1:10, fun

我正在读哈德利·威克汉姆关于Github的书,尤其是。在那里,他给出了一个惰性计算结果的例子,在
add/adders
函数部分。让我引用这一点:

当使用lappy或循环创建闭包时,此[延迟评估]非常重要:

add <- function(x) {
  function(y) x + y
}
adders <- lapply(1:10, add)
adders[[1]](10)
adders[[10]](10)
添加以下目标:

adders <- lapply(1:10, function(x)  add(x) )

加法器从R3.2.0开始,这不再是真的

表中的对应行为:

高阶函数,如apply函数和Reduce()now 将参数强制应用于它们应用的函数,以消除 惰性计算和变量捕获之间的不良交互 闭关自守

事实上:

add <- function(x) {
  function(y) x + y
}
adders <- lapply(1:10, add)
adders[[1]](10)
# [1] 11
adders[[10]](10)
# [1] 20

add Ok,让我重新表述一下,看看是否正确。当我们调用
lappy
时,R排序会记住所有10个加法器函数的结构,但不会计算x。当我们调用第一个加法器函数时,R说,啊哈,让我们看看这是什么,从lappy调用中取x,在该点上已经是10,并将第一个被调用的加法器函数计算为10+y。对于其余的加法器函数也一样,使它们都相同。“也许说得很粗略,但这就是它的逻辑吗?我相信是这样的。”哈德利当我调用第一个加法器函数时,拉普拉循环已经结束了。加法器函数在哪里查找x?为什么x=10的值会持续存在?惰性评估实际上是如何工作的?所有十个不同的加法器函数都有十个单独的环境,其中包含
x
。我想他们可能都指向评估之前的某个地方,但指向哪里?父环境中没有
x
。该环境是在首次调用函数时创建的。在lappy循环完成后,此时x变量等于10。所以它们都是一样的。请注意,这个问题的答案从R3.2.0开始发生了变化,请参见下面我的答案。补充@jhin的评论:虽然
lappy()
在最近的R中发生了变化,但是用于
lappy()
所在位置的函数
purr::map()
,仍然像以前的
lappy())
相对于闭包的共享环境。然而,我不会指望
purrr::map()
的这种“时代错误”继续存在,因为它可能会在未来的版本中得到纠正。@jhin实际上,我猜hadley的教程是直接从github构建的,所以在R3.2.0之后阅读它现在是相当奇怪的,因为该版本使该教程中关于惰性评估的整个部分变得毫无意义:
adders
adders2
的输出不再有区别了!
add <- function(x) {
  function(y) x + y
}
adders <- lapply(1:10, add)
adders[[1]](10)
# [1] 11
adders[[10]](10)
# [1] 20