你能更清楚地解释R函数运算符中的惰性求值吗?

pet*_*ord 13 r lazy-evaluation environments

如果我创建一个函数如下:

what_is_love <- function(f) {
  function(...) {
    cat('f is', f, '\n')
  }
}
Run Code Online (Sandbox Code Playgroud)

并称之为lapply: funs <- lapply(c('love', 'cherry'), what_is_love)

我得到了意外的输出:

> funs[[1]]()
f is cherry
> funs[[2]]()
f is cherry
Run Code Online (Sandbox Code Playgroud)

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

> f1 <- what_is_love('love')
> f2 <- what_is_love('cherry')
> f1()
f is love
> f2()
f is cherry
Run Code Online (Sandbox Code Playgroud)

是什么赋予了?

我知道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
Run Code Online (Sandbox Code Playgroud)

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

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

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

Browse[1]> environment(out[[1]])$f
[1] "cherry"
Browse[1]> environment(out[[2]])$f
[1] "cherry"
Run Code Online (Sandbox Code Playgroud)

我知道答案是"懒惰评估",但我正在寻找更深入的... f最终如何在两种环境中重新分配?哪里f来的?在这个例子中,R懒惰的评估是如何工作的?

-

编辑:我知道关于懒惰评估和功能的另一个问题,但它只是说答案是"懒惰评估"而没有解释懒惰评估实际上是如何工作的.我正在寻求更大的深度.

MrF*_*ick 16

当你这样做

what_is_love <- function(f) {
  function(...) {
    cat('f is', f, '\n')
  }
}
Run Code Online (Sandbox Code Playgroud)

内部函数为它创建一个外壳f,但是捕获的是,直到你实际使用传递给函数的变量,它仍然是一个"承诺",实际上并没有被评估.如果你想"捕捉"当前的价值f,那么你需要强制评估承诺; 你可以使用这个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 
Run Code Online (Sandbox Code Playgroud)

没有force(),f仍然是列表中两个功能的承诺.在调用函数之前,并且在调用函数时,不会对它进行求值,f该函数将promise计算为最后一个已知值为"cherry"的值.

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

诸如apply函数和Reduce()之类的高阶函数现在强制它们应用的函数的参数,以便消除惰性求值和闭包中的变量捕获之间的不期望的交互.这解决了PR#16093.