为什么递归的惰性列表会在Scala中烧掉堆栈?

时间:2017-04-21 05:56:50

标签: scala haskell linked-list lazy-evaluation

我在{PMPG上this answer proud haskeller看到了Haskell的这个片段:

x=2:x

我想,"等等,我可以在Scala中做到这一点!"所以我试过了:

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

它已编译,我的控制台打印出一个不错的x: List[Int] = <lazy>。但是这些行中的每一行都会产生StackOverflowException

x take 1
x.head
x(1)
x

基于最后一个,看起来任何使用x的尝试都会试图计算x(尝试在控制台中打印它时发生堆栈溢出)。在这个例子中,斯卡拉的懒惰与哈斯克尔的懒惰有什么不同?这是Scala lazy val的功能还是List类只需要一个完整的尾部?

2 个答案:

答案 0 :(得分:10)

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

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

答案 1 :(得分:1)

嗯,看起来我在制定问题时想出来了。 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的懒惰毕竟是懒惰,如果你让一切都变得懒惰,至少。