为什么递归惰性列表会破坏 Scala 中的堆栈?

Why does a recursive lazy list blow the stack in Scala?

我在 Meta PPCG 的 this answer by proud haskeller 中看到了 Haskell 的这个片段:

x=2:x

我想,"wait, I can do that in Scala!" 所以我尝试了:

lazy val x: List[Int] = 2 :: x

它编译了,我的控制台打印了一个漂亮的 x: List[Int] = <lazy>。但是每一行都会导致 WhosebugException:

x take 1
x.head
x(1)
x

基于最后一个,看起来任何使用 x 的尝试都会破坏试图计算 x 的堆栈(要么发生堆栈溢出,要么试图在控制台中打印它) .在这个例子中,Scala 的惰性与 Haskell 的惰性有何不同?这是 Scala 的 lazy val 的特性还是 List class 只需要一个完整的尾巴?

嗯,看来我是在提问题的时候想出来的。 List 似乎比 lazy val 更成问题。为了尝试这个,我做了一个简单的 LazyList 实现:

class LazyList(h: Int, t: => LazyList) {
  val head = h
  lazy val tail = t
}

那我可以做:

lazy val x: LazyList = new LazyList(1, x)
x.head // 1
x.tail.tail.tail.head // 1

所以,Scala 的懒惰毕竟是真正的懒惰,如果你让一切都变得懒惰,至少。

你要的是def x: Stream[Int] = 2 #:: x。这会产生一个 immutable.Stream[Int].

惰性变量仅在需要时才计算,但已完全计算。另一方面,Stream 是惰性值的集合。每个元素仅在需要时才被评估,但整个集合可能永远不会被评估,这就是为什么它可以是无限的。