2017-04-21 34 views
3

我看到this answer這個片段哈斯克爾通過proud haskeller對元PPCG:「等一下,我能做到這一點在斯卡拉」爲什麼遞歸懶惰列表在Scala中打擊堆棧?

x=2:x 

我想,所以,我想:

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類只需要一個完整的尾巴?

回答

10

你想要的是def x: Stream[Int] = 2 #:: x。這產生immutable.Stream[Int]

只有在需要時才評估延遲變量,但已對其進行全面評估。另一方面,A Stream是一組惰性值。每個元素只在需要時才被評估,但整個集合可能永遠不會被評估,這就是爲什麼它可以是無限的。

+0

不是,你需要它的返回類型來編譯,但仍然不錯。對於懶惰的vals,它是'lazy val x:Stream [Int] = 2#:: x'。 –

+0

@BrianMcCutchon,對吧。忙於撰寫第二段,錯過了。然而,我會爭辯說,使'Stream'val懶惰是沒有意義的。如果它是'val',那麼記錄結果(被評估的元素)。如果它是'var',他們不是。讓它成爲一個「懶惰的val」並不會給你帶來太多的收益。 – jwvh

+0

糟糕。另一個錯誤嘗試澄清'被記憶的'val'流和'未被記憶的'def'(而不是'var')流之間的區別。 – jwvh

1

好吧,看起來我在制定問題時想通了。 Listlazy 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的懶惰畢竟是真懶,如果你做的一切懶惰,至少。

+0

請注意,如果您在Haskell中使用嚴格的列表,則會發生完全相同的事情。 –

+0

@JörgWMittag有趣。我知道Haskell很少,但我很驚訝它有一個嚴格的列表類型。 –