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

为什么递归惰性列表会破坏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

我在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的懒惰与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知之甚少,但我很惊讶它有一个严格的列表类型。