2016-03-30 11 views
2

StackOverflowErrorで、なぜこのコードスニペットの実行結果をスロー:Scalaのストリーム計算はStackOverflowErrorが

lazy val primes: Stream[Int] = 2 #:: Stream.from(3, 2) filter { pc => 
    primes.takeWhile(x => x * x <= pc) forall (p => pc % p != 0) 
} 
primes.take(5).last 

このコードスニペットだけで正常に動作している間(filter前にドットを参照してください):

lazy val primes: Stream[Int] = 2 #:: Stream.from(3, 2).filter { pc => 
    primes.takeWhile(x => x * x <= pc) forall (p => pc % p != 0) 
} 
primes.take(5).last 

答えて

5

括弧を行いますここで実行の順序がより明確になります。 primesの以下の2つの定義は、それぞれのOPの対応するものと同等です。

// fails with stack overflow 
lazy val primes: Stream[Int] = (2 #:: Stream.from(3, 2)) filter { pc => 
    primes.takeWhile(x => x * x <= pc) forall (p => pc % p != 0) 
} 

// succeeds 
lazy val primes: Stream[Int] = 2 #:: (Stream.from(3, 2).filter { pc => 
    primes.takeWhile(x => x * x <= pc) forall (p => pc % p != 0) 
}) 

さて、最初の問題は何ですか?最初にストリーム(2 #:: Stream.from(3, 2))を作成し、それをフィルタリングすることによって定義されます。

primes.head 

実際には、スタックオーバーフローも発生します。次のようになります。

  1. headprimesの最初の要素にアクセスしようとしました。
  2. 最初の要素2は、filter述語に対してチェックする必要があります。
  3. 述語をチェックするには、再帰的にprimesにアクセスする必要があります。
  4. 最初の要素をprimesとし、filter述部を実行する必要があります。2です。スタックオーバーフローにつながる
  5. 繰り返し手順3

...。 Stream2)の頭部がフィルタリングされていないため2が実際に存在しないかであるかどうかを確認するために、その段階で何の再帰はありませんので、

第二の例では、この問題に悩まされません。すなわち、第2の例では、2Streamheadであることが明らかである。最初の例では、Streamheadは、filterをチェックして計算する必要がありますが、そのためには、無限回帰ループでそれ自体を参照してください。