产生多个迭代器,然后压扁不按预期工作

时间:2015-08-14 15:23:54

标签: scala

如下代码产生50 Iterator [String]。扁平此集合应该返回50而不是1?

  val v1: Iterator[String] = List("1").toIterator

  val l: Iterator[String] = (for (a <- 1 to 50) yield v1).flatten.toIterator

  println(l.size)

每次循环时都会覆盖迭代?

1 个答案:

答案 0 :(得分:0)

不,因为迭代器是可变的,所以在颠换的第一步(作为展平的一部分)之后,它变为空的。换句话说,第一步产生所有剩余元素之间共享的副作用,因为它们都引用相同的迭代器。因此,在对帐期间(见下文),第一个元素被记忆为Vector(1),而其他元素则变为空向量。你的(for (a <- 1 to 50) yield v1).flatten实际上返回Vector(1),所以再次将它转换为迭代器的步骤是多余的,因为之前的迭代器已经实现了。即使没有flatten,只需转换toVector即可显示:

scala> val v1: Iterator[String] = List("1").toIterator
v1: Iterator[String] = non-empty iterator

scala> val v = (for (a <- 1 to 50) yield v1).toVector
v: Vector[Iterator[String]] = Vector(non-empty iterator, non-empty iterator, non-empty iterator, ...

scala> v.head.toVector
res16: Vector[String] = Vector(1)

scala> v.head.toVector
res20: Vector[String] = Vector()

scala> v.tail.head.toVector
res17: Vector[String] = Vector()

flatten必须将Iterator转换为Vector,因为它需要Vector[Vector[T]]Vector[Iterator[T]]调整monadic图层以进行合并(展平)。

谈论迭代器的大小:

scala> val v1: Iterator[String] = List("1").toIterator
v1: Iterator[String] = non-empty iterator

scala> v1.size
res18: Int = 1

scala> v1.size
res19: Int = 0

作为结论,结果Vector(1).toIterator的大小显然是1(但仅限第一次)。

一般来说,“悖论”的发生只是因为迭代器是可变的,所以它不是Scala的纯函数(数学)用法 - 这意味着结果可能与正常的逻辑期望不同(但仍然是正确的)。命令符合功能性O_o。

P.S。您可以使用Stream而不是迭代器来部分避免由副作用引起的问题。他们仍然懒惰但在创作过程中记住元素:

scala> val v1: Stream[String] = List("1").toStream
v1: Stream[String] = Stream(1, ?)

scala> val l: Stream[String] = (for (a <- 1 to 50) yield v1).flatten.toStream
l: Stream[String] = Stream(1, ?)

scala> l.size
res21: Int = 50

基于Stream的代码可能永远不会终止(与基于迭代器的代码相同),这仍然是类数学编程的问题。而且,与迭代器相比,由于记忆,流可能会导致OutOfMemory。此外,流始终计算其头部。除此之外,他们还不错。