为什么递归惰性列表会破坏Scala中的堆栈?
我在Meta PPCG上看到了Haskell的这一片段:为什么递归惰性列表会破坏Scala中的堆栈?,scala,haskell,linked-list,lazy-evaluation,Scala,Haskell,Linked List,Lazy Evaluation,我在Meta PPCG上看到了Haskell的这一片段: x=2:x 我想,“等等,我可以在Scala中做到!”所以我试着: lazy val x:List[Int]=2::x 它编译后,我的控制台打印了一个漂亮的x:List[Int]=。但这些行中的每一行都会导致一个StackOverflowException: x take 1 x.head x(1) x 基于上一个,任何尝试使用x的操作都会破坏试图计算x的堆栈(或者在控制台中打印时发生堆栈溢出)。在这个例子中,Scala的懒惰与Ha
x=2:x
我想,“等等,我可以在Scala中做到!”所以我试着:
lazy val x:List[Int]=2::x
它编译后,我的控制台打印了一个漂亮的x:List[Int]=
。但这些行中的每一行都会导致一个StackOverflowException
:
x take 1
x.head
x(1)
x
基于上一个,任何尝试使用
x
的操作都会破坏试图计算x
的堆栈(或者在控制台中打印时发生堆栈溢出)。在这个例子中,Scala的懒惰与Haskell的懒惰有何不同?这是Scala的lazy val
的一个特性,还是List
类仅仅需要一个完整的尾部?好吧,看起来我在制定问题的时候就解决了这个问题。与lazy val
相比,List
的问题似乎更大。为了尝试这一点,我做了一个简单的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
。这将生成一个不可变的.Stream[Int]
只有在需要时才对惰性变量求值,但它是完全求值的。另一方面,
流是惰性值的集合。每个元素只在需要时进行求值,但可能永远不会对整个集合进行求值,这就是为什么它可以是无限的。不完全如此,您需要返回类型来编译它,但仍然很好。对于lazy vals,它是lazy valx:Stream[Int]=2::x
@BrianMcCutchon,没错。忙着写第二段,却错过了。然而,我认为让流
val变懒没有什么意义。如果是val
,则结果(评估的元素)将被存储。如果是var
,则不是。让它成为一个lazy val
不会给你带来太多的好处。另一个打字错误试图澄清val
流(已记忆)和def
(非var
)流(未记忆)之间的区别。请注意,如果在Haskell中使用严格的列表,则会发生完全相同的情况。@JörgWMittag有趣。我对Haskell知之甚少,但我很惊讶它有一个严格的列表类型。