如下代码产生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)
每次循环时都会覆盖迭代?
答案 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
。此外,流始终计算其头部。除此之外,他们还不错。