14

如果我按如下方式创建函数:

what_is_love <- function(f) {
  function(...) {
    cat('f is', f, '\n')
  }
}

并调用它lapplyfuns <- lapply(c('love', 'cherry'), what_is_love)

我得到意外的输出:

> funs[[1]]()
f is cherry
> funs[[2]]()
f is cherry

但请注意,当您不使用时,情况并非如此lapply

> f1 <- what_is_love('love')
> f2 <- what_is_love('cherry')
> f1()
f is love
> f2()
f is cherry

是什么赋予了?

我知道funs <- lapply(c('love', 'cherry'), what_is_love)可以更完整地写出来:

params <- c('love', 'cherry')
out <- vector('list', length(params))
for (i in seq_along(params)) {
  out[[i]] <- what_is_love(params[[i]])
}
out

但是当我浏览时,我看到这两个函数都有自己的环境:

Browse[1]> out[[1]]
function(...) {
    cat('f is', f, '\n')
  }
<environment: 0x109508478>
Browse[1]> out[[2]]
function(...) {
    cat('f is', f, '\n')
  }
<environment: 0x1094ff750>

但在每一个环境中,f都是一样的......

Browse[1]> environment(out[[1]])$f
[1] "cherry"
Browse[1]> environment(out[[2]])$f
[1] "cherry"

我知道答案是“懒惰的评估”,但我正在寻找更多的深度......f最终如何在两个环境中重新分配?从哪里来f?在这个例子中,R 惰性求值是如何在幕后工作的?

-

编辑:我知道关于惰性求值和函数的另一个问题,但它只是说答案是“惰性求值”,而没有解释惰性求值实际上是如何工作的。我正在寻求更大的深度。

4

1 回答 1

17

当你这样做

what_is_love <- function(f) {
  function(...) {
    cat('f is', f, '\n')
  }
}

内部函数为 创建了一个外壳f,但要注意的是,在您实际使用传递给函数的变量之前,它仍然是“承诺”并且实际上并未被评估。如果要“捕获” 的当前值f,则需要强制对 promise 进行评估;你可以使用这个force()功能。

what_is_love <- function(f) {
  force(f)
  function(...) {
    cat('f is', f, '\n')
  }
}
funs <- lapply(c('love', 'cherry'), what_is_love)

funs[[1]]()
# f is love 
funs[[2]]()
# f is cherry 

没有force(),f在列表中的两个函数中仍然是一个承诺。在您调用该函数之前,它不会被评估,并且当您调用该函数时,promise 被评估为最后一个已知值,f即“cherry”。

正如@MartinMorgran 指出的那样,这种行为在 R 3.2.0 中发生了变化。从发行说明

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

于 2015-04-19T17:42:25.323 回答