我最近在我的代码中遇到了一个错误,其中迭代多个流导致它们只能通过第一个项目进行迭代。我将我的流转换为缓冲区(我甚至不知道我正在调用的函数的实现返回一个流)并且问题已得到修复。我发现这很难相信,所以我创建了一个最低限度可验证的例子:
def f(as: Seq[String], bs: Seq[String]): Unit =
for {
a <- as
b <- bs
} yield println((a, b))
val seq = Seq(1, 2, 3).map(_.toString)
f(seq, seq)
println()
val stream = Stream.iterate(1)(_ + 1).map(_.toString).take(3)
f(stream, stream)
打印其输入的每个组合的函数,并使用Seq [1,2,3]和Stream [1,2,3]调用。
seq的结果是:
(1,1)
(1,2)
(1,3)
(2,1)
(2,2)
(2,3)
(3,1)
(3,2)
(3,3)
流的结果是:
(1,1)
我只能在迭代多个生成器时复制它,迭代单个流似乎工作正常。
所以我的问题是:为什么会发生这种情况,我怎样才能避免这种故障呢?也就是说,在每次多生成器迭代之前没有使用.toBuffer
或.to[Vector]
?
感谢。
答案 0 :(得分:2)
您使用for-comprehension(在yield中使用println
)的方式有点奇怪,可能不是您想要做的。如果您真的只想打印条目,那么只需使用foreach
即可。这会强制像Stream
这样的懒惰序列,即
def f_strict(as: Seq[String], bs: Seq[String]): Unit = {
for {
a <- as
b <- bs
} println((a, b))
}
您使用f
获取奇怪行为的原因是Streams
是懒惰的,并且元素仅根据需要计算(然后记忆)。由于您从未使用由Stream
创建的f
(必定因为您的f
返回Unit
),因此只会计算头部(这就是您的原因)看到单个(1, 1)
。)如果你让它返回它生成的序列(它将具有类型Seq[Unit]
),即
def f_new(as: Seq[String], bs: Seq[String]): Seq[Unit] = {
for {
a <- as
b <- bs
} yield println((a, b))
}
然后,您将获得以下行为,这有助于阐明正在发生的事情:
val xs = Stream(1, 2, 3)
val result = f_new(xs.map(_.toString), xs.map(_.toString))
//prints out (1, 1) as a result of evaluating the head of the resulting Stream
result.foreach(aUnit => {})
//prints out the other elements as the rest of the entries of Stream are computed, i.e.
//(1,2)
//(1,3)
//(2,1)
//...
result.foreach(aUnit => {})
//probably won't print out anything because elements of Stream have been computed,
//memoized and probably don't need to be computed again at this point.