关于迭代器[T] here的Scala文档说明如下:
特别重要的是要注意,除非另有说明,否则在调用方法之后永远不应使用迭代器。两个最重要的例外也是唯一的抽象方法:
next
和hasNext
。
他们还给出了安全和不安全使用的具体示例:
def f[A](it: Iterator[A]) = {
if (it.hasNext) { // Safe to reuse "it" after "hasNext"
it.next // Safe to reuse "it" after "next"
val remainder = it.drop(2) // it is *not* safe to use "it" again after this line!
remainder.take(2) // it is *not* safe to use "remainder" after this line!
} else it
}
不幸的是,我不遵循这里不安全的想法。有人能在这里为我揭开光明的一面吗?
答案 0 :(得分:4)
这是一个具体的例子:
def eleventh[A](xs: Iterator[A]) = {
xs.take(10).toList
xs.next
}
我们可以尝试一下:
scala> eleventh((1 to 100).toList.toIterator)
res0: Int = 11
scala> eleventh((1 to 100).toStream.toIterator)
res1: Int = 11
scala> eleventh(Stream.from(1).toIterator)
res2: Int = 11
看起来很好。但那时:
scala> eleventh((1 to 100).toIterator)
res3: Int = 1
现在(1 to 100).toIterator
与(1 to 100).toList.toIterator
具有相同的类型,但这两者的行为非常不同 - 我们看到实现细节漏掉了API。这是一个非常糟糕的事情,并且是将纯函数组合器(如take
)与迭代器等固有的命令式和可变概念混合的直接结果。
答案 1 :(得分:4)
val remainder = it.drop(2)
可以像这样实现:它创建一个新的包装器迭代器,它保留对原始it
运算符的引用并将其前进两次,以便下次调用remainder.next
时你得到第3个元素。但如果您之间拨打it.next
,remainder.next
将返回第4个元素...
因此,您必须引用可能需要调用remainder
的{{1}}和it
,并执行相同的副作用,但实施不支持。