为什么迭代多个流只迭代第一个元素?

时间:2017-12-15 04:13:15

标签: scala stream

我最近在我的代码中遇到了一个错误,其中迭代多个流导致它们只能通过第一个项目进行迭代。我将我的流转换为缓冲区(我甚至不知道我正在调用的函数的实现返回一个流)并且问题已得到修复。我发现这很难相信,所以我创建了一个最低限度可验证的例子:

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]

感谢。

1 个答案:

答案 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.