【问题标题】:Understanding lazy evaluation in R理解 R 中的惰性求值
【发布时间】:2025-06-13 12:30:02
【问题描述】:

我试图了解 R 中的惰性求值是如何工作的。它仅适用于函数参数的评估吗?因为我理解,例如

f <- function(x = x, y = x*2) {
  c(x, y)
}

f(2)
[1] 2 4

但在其他语言中,例如Haskell,惰性求值意味着函数调用只有在实际使用时才会被求值。所以我希望这样的事情会立即运行:

g <- function(x) {
  y <- sample(1:100000000)
  return(x)
}

g(4)

但它清楚地评估了 sample 调用,即使它的结果没有被使用。

有人可以准确解释这是如何工作的,或者指出我详细解释的方向吗?

类似问题:

Question with similar wording, but different problem

【问题讨论】:

    标签: r function haskell lazy-evaluation


    【解决方案1】:

    正如您已经发现的那样,R 并不使用一般意义上的惰性求值。但是 R 确实提供了该功能,如果您需要它,可以通过函数 delayedAssign() 如下所示:

    > system.time(y <- sample(1E8))
       user  system elapsed 
      7.636   0.128   7.766 
    > system.time(length(y))
       user  system elapsed 
          0       0       0 
    system.time(delayedAssign("x", sample(1E8)))
       user  system elapsed 
      0.000   0.000   0.001 
    > system.time(length(x))
       user  system elapsed 
      7.680   0.096   7.777 
    

    如您所见,y 被立即评估,因此确定y 的长度根本不需要时间。另一方面,x 在创建时不会被评估,delayedAssign() 只返回一个评估x 的承诺,并且只有当我们真正需要x 的值时,在这种情况下才能确定它的长度,x 被评估。

    表达式是放在函数中还是在全局环境中执行都没有关系,因此在示例中将表达式封装在函数中并没有真正添加任何内容,这就是我排除它的原因.但是,如果您想确定,请尝试:

    a.f <- function(z) { delayedAssign("x", sample(1E8)); return(z+1) }
    system.time(a.f(0))
       user  system elapsed 
          0       0       0 
    

    【讨论】:

    • 我指的是 Hadley Wickham 的书,他在书中提到:“默认情况下,R 函数参数是惰性的,只有在实际使用时才会评估它们”。那么最新版本不是这样吗?
    • 请注意,问题包括“它仅适用于函数参数的评估吗?”这句话。我的回答与威克姆的说法并不矛盾。