2015-08-14 50 views
1

由於下面的代碼產生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

不,因爲迭代器是可變的,所以在第一步追溯(作爲展平的一部分)之後它變爲空的。換句話說,這第一步產生所有剩餘元素之間共享的副作用,因爲它們都引用相同的迭代器。因此,在和解期間(見下文),第一個元素將被記憶爲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() 

flattenIterator轉換爲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。另外,流總是在計算它的頭部。除此之外,他們都很好。