为什么递归惰性列表会破坏 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
是惰性值的集合。每个元素仅在需要时才被评估,但整个集合可能永远不会被评估,这就是为什么它可以是无限的。
我在 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
是惰性值的集合。每个元素仅在需要时才被评估,但整个集合可能永远不会被评估,这就是为什么它可以是无限的。