2015-08-14 49 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。另外,流总是在计算它的头部。除此之外,他们都很好。