【问题标题】:Scala iterator: "one should never use an iterator after calling a method on it" - why?Scala 迭代器:“在调用方法后永远不应该使用迭代器”——为什么?
【发布时间】:2013-08-24 17:03:15
【问题描述】:

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
}

不幸的是,我在这里没有遵循不安全的想法。有人可以在这里为我解释一下吗?

【问题讨论】:

  • "重用:在调用 drop/take 方法后,应该丢弃它被调用的迭代器,并且只使用返回的迭代器。使用旧的迭代器是未定义的,可能会改变,并且可能也会导致对新迭代器的更改。”

标签: scala iterator


【解决方案1】:

这是一个具体的例子:

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)与固有的命令式和可变概念(如迭代器)混合的直接结果。

【讨论】:

    【解决方案2】:

    val remainder = it.drop(2) 可以这样实现:它创建一个新的包装迭代器,该迭代器保留对原始it 运算符的引用并将其推进两次,以便下次调用remainder.next 时获得第三个元素。但是,如果您在两者之间调用 it.nextremainder.next 将返回第 4 个元素...

    所以你必须引用 remainderit 可能需要调用 next 并执行相同的副作用,这是实现不支持的。

    【讨论】:

    • 谢谢。所以从某种意义上说,像 drop 这样的调用方法会将迭代器的所有权转移给返回的迭代器。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-05
    • 2018-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-23
    • 1970-01-01
    相关资源
    最近更新 更多