我有时发现自己想在 Scala 中对无限流执行嵌套迭代以进行理解,但指定循环终止条件可能有点棘手。有没有更好的方法来做这种事情?
我想到的用例是我不一定预先知道我正在迭代的每个无限流需要多少元素(但显然我知道它不会是无限的)。假设每个流的终止条件可能以某种复杂的方式依赖于 for 表达式中其他元素的值。
最初的想法是尝试将流终止条件编写为for表达式中的if过滤器子句,但是在循环嵌套无限流时会遇到麻烦,因为无法短路第一个无限流上的迭代,最终导致 OutOfMemoryError。我理解为什么会这样,考虑到表达式如何映射到map、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)
但是为了这个问题的目的,想象一个更复杂的用例,其中流终止条件不能轻易移动到流表达式本身。