61

我正在阅读 Hadley Wickhams 在 Github 上的书,尤其是关于惰性评估的这一部分add/adders在那里,他在函数部分给出了惰性求值后果的例子。让我引用这段话:

在使用 lapply 或循环创建闭包时,此 [惰性求值] 很重要:

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

x 在您第一次调用其中一个加法器函数时被懒惰地评估。至此,循环完成,x 的最终值为 10。因此,所有加法器函数都将在其输入上加 10,这可能不是您想要的!手动强制评估解决了这个问题:

add <- function(x) {
  force(x)
  function(y) x + y
}
adders2 <- lapply(1:10, add)
adders2[[1]](10)
adders2[[10]](10)

我似乎不明白那一点,那里的解释很少。有人可以详细说明那个特定的例子,并解释那里发生了什么吗?我对“此时,循环完成,x的最终值为10”这句话感到特别困惑。什么循环?什么最终值,在哪里?一定是我想念的简单的东西,但我只是看不到它。提前非常感谢。

4

2 回答 2

57

从 R 3.2.0 开始,这不再适用!

更改日志中的相应行显示:

高阶函数(例如 apply 函数和 Reduce())现在强制将参数传递给它们应用的函数,以消除惰性求值和闭包中变量捕获之间的不良交互。

事实上:

add <- function(x) {
  function(y) x + y
}
adders <- lapply(1:10, add)
adders[[1]](10)
# [1] 11
adders[[10]](10)
# [1] 20
于 2015-04-22T03:01:50.197 回答
36

目标:

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

是创建一个add函数列表,第一个添加 1 到其输入,第二个添加 2,等等。惰性求值导致 R 等待真正创建加法器函数,直到您真正开始调用函数。问题是,在创建第一个加法器函数后,循环x增加lapply,以 10 的值结束。当您调用第一个加法器函数时,惰性求值现在构建函数,得到 的值x。问题是原来x的不再等于一,而是lapply循环结束时的值,即10。

因此,惰性求值导致所有加法器函数等到lapply循环完成后才真正构建函数。然后他们用相同的值(即 10)构建他们的函数。Hadley 建议的解决方案是强制x直接评估,避免惰性评估,并获得具有正确x值的正确函数。

于 2013-04-21T10:11:25.313 回答