2017-04-21 15 views
3

私はメタPPCGにproud haskellerによってthis answerにハスケルのこのスニペットを見た:再帰的な遅延リストがScalaでスタックを吹き飛ばしてしまうのはなぜですか?

x=2:x 

私は「私はScalaでそれを行うことができ、待って!」、と思いましただから私は試してみました:それはコンパイルされ、私のコンソールが素敵x: List[Int] = <lazy>を印刷

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

。しかし、StackOverflowExceptionに次の行の結果の各:xを使用する試みがxを計算しようとしているスタックを吹く(どちらかというか、スタックオーバーフローがそれを印刷しようとして起こるように、最後の1に基づいて

x take 1 
x.head 
x(1) 
x 

は、それが見えますコンソールで)。この例では、Scalaの怠惰とHaskellの怠惰はどう違うのですか?これはScalaのlazy valの機能ですか、Listクラスは完全なテールを必要としていますか?

答えて

10

希望するものはdef x: Stream[Int] = 2 #:: xです。これによりimmutable.Stream[Int]が生成されます。

遅延型変数は必要な場合にのみ評価されますが、完全に評価されます。一方、Streamは、遅延値の集合です。各要素は必要なときにのみ評価されますが、コレクション全体が評価されることはないため、無限になる可能性があります。

+0

まだ、あなたはそれをコンパイルするための戻り値の型が必要ですが、それでもいいです。怠け者のvalでは '' lazy val x:Stream [Int] = 2#:: x'です。 –

+0

@BrianMcCutchon、そうです。 2番目のパラグラフを作成してそれを逃した。しかし、私は、「ストリーム」の価値を怠ってしまう点はほとんどないと主張しています。それが 'val'ならば、結果(評価された要素)はメモされます。それが 'var'なら、そうではありません。それを「怠け者のヴァル」にすることは、あなたをあまり買わない。 – jwvh

+0

memoizedされた 'val'ストリームと' mem'されていない 'def'(' var'ではなく)ストリームとの違いを明確にしようとするもう一つのタイプミスです。 – jwvh

1

まあ、私は質問を定式化しながらそれを考え出したようです。 lazy valよりもListの方が問題が多いようです。

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の怠惰は、すべての後に本当に怠け者でこれを試してみるために、私はシンプルなLazyList実装を行いました。

+0

ハスケルで厳格なリストを使用した場合、まったく同じことが起こったことに注意してください。 –

+0

@JörgWMittag興味深い。私はHaskellをほとんど知っていませんが、厳密なリストタイプがあることに驚いています。 –

関連する問題