理解のためにScalaの無限ストリームに対してネストされた反復を実行したい場合がありますが、ループの終了条件を指定するのは少し難しい場合があります。このようなことをするためのより良い方法はありますか?
私が念頭に置いているユースケースは、反復している無限のストリームのそれぞれから必要な要素の数を前もって必ずしも知らない場合です(ただし、明らかに、無限の数にはならないことはわかっています)。各ストリームの終了条件は、for式の他の要素の値に複雑な方法で依存する可能性があると想定します。
最初の考えは、 for式のifフィルター句としてストリーム終了条件を書き込もうとすることですが、最初の無限ストリームの反復を短絡する方法がないため、ネストされた無限ストリームをループするときに問題が発生します。これは最終的にOutOfMemoryErrorにつながります。式がmaptomap、flatMap、withFilterメソッドを呼び出す方法を考えると、これが当てはまる理由を理解しています-私の質問は、この種のことを行うためのより良いイディオムがあるかどうかです(おそらく理解にはまったく関係しません)。
今説明した問題を説明するためにやや不自然な例を示すために、次の(非常に単純な)コードを検討して、番号1と2のすべてのペアを生成します。
val pairs = for {
i <- Stream.from(1)
if i < 3
j <- Stream.from(1)
if j < 3
}
yield (i, j)
pairs.take(2).toList
// result: List[(Int, Int)] = List((1,1), (1,2))
pairs.take(4).toList
// 'hoped for' result: List[(Int, Int)] = List((1,1), (1,2), (2,1), (2,2))
// actual result:
// java.lang.OutOfMemoryError: Java heap space
// at scala.collection.immutable.Stream$.from(Stream.scala:1105)
明らかに、この単純な例では、次のように、 ifフィルターを元のストリームのtakeWhileメソッド呼び出しに移動することで、問題を簡単に回避できます。
val pairs = for {
i <- Stream.from(1).takeWhile(_ < 3)
j <- Stream.from(1).takeWhile(_ < 3)
}
yield (i, j)
しかし、質問の目的のために、ストリームの終了条件をストリーム式自体に簡単に移動できない、より複雑なユースケースを想像してみてください。