由於下面的代碼產生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
。另外,流總是在計算它的頭部。除此之外,他們都很好。