由于下面的代码产生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)
看来迭代每次都被循环覆盖?
由于下面的代码产生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)
看来迭代每次都被循环覆盖?
不,因为迭代器是可变的,所以在第一步追溯(作为展平的一部分)之后它变为空的。换句话说,这第一步产生所有剩余元素之间共享的副作用,因为它们都引用相同的迭代器。因此,在和解期间(见下文),第一个元素将被记忆为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[Iterator[T]]
Vector[Vector[T]]
到之前将其整合(扁平化)协调单子层。
谈迭代器的尺寸:
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
为基础的代码可能永远不会终止(与基于迭代器),它仍然是methematical样的编程问题。而且,与迭代器相比,流会因为记忆而导致OutOfMemory
。另外,流总是在计算它的头部。除此之外,他们都很好。