Scala迭代器:“在调用方法之后永远不应该使用迭代器” - 为什么?

时间:2013-08-24 17:03:16

标签: scala iterator

关于迭代器[T] here的Scala文档说明如下:

  

特别重要的是要注意,除非另有说明,否则在调用方法之后永远不应使用迭代器。两个最重要的例外也是唯一的抽象方法:nexthasNext

他们还给出了安全和不安全使用的具体示例:

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
}

不幸的是,我不遵循这里不安全的想法。有人能在这里为我揭开光明的一面吗?

2 个答案:

答案 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.nextremainder.next将返回第4个元素...

因此,您必须引用可能需要调用remainder的{​​{1}}和it,并执行相同的副作用,但实施不支持。