为什么这个迭代器无限循环?

Jac*_*nig 4 scala pass-by-name mutability

我正在尝试链接Iterators

var it = Iterator(1)
it.next
it = Iterator(2) ++ it
it.next
it.hasNext
Run Code Online (Sandbox Code Playgroud)

hasNext正如您在此处看到的那样无限循环:https : //scastie.scala-lang.org/qbHIVfsFSNO5OYmT4pkutA

如果您运行它并在无限循环时检查堆栈,它会在内容中循环:

        at scala.collection.Iterator$ConcatIterator.merge(Iterator.scala:213)
        at scala.collection.Iterator$ConcatIterator.advance(Iterator.scala:197)
        at scala.collection.Iterator$ConcatIterator.hasNext(Iterator.scala:227)
Run Code Online (Sandbox Code Playgroud)

(此堆栈来自 Scala 2.12.11,但 Scastie 链接在 中显示相同的行为2.13.2)。

我知道在调用方法之后永远不应该使用迭代器,但这似乎对我有用。使用varto 指向“当前”迭代器并将其更改为指向一个新的迭代器,该迭代器附加上一个迭代器的剩余部分。

以下稍微修改确实有效:

        at scala.collection.Iterator$ConcatIterator.merge(Iterator.scala:213)
        at scala.collection.Iterator$ConcatIterator.advance(Iterator.scala:197)
        at scala.collection.Iterator$ConcatIterator.hasNext(Iterator.scala:227)
Run Code Online (Sandbox Code Playgroud)

Scastie 链接:https ://scastie.scala-lang.org/1X0jslb8T3WIFLHamspYAg

这向我表明,以某种方式损坏的版本正在创建一个附加自身的迭代器。关于这里发生了什么的任何提示?

Kol*_*mar 6

++方法的参数Iterator按名称传递。++返回一个 new Iteratorit它只存储一个返回的函数,但在您尝试使用附加元素之前不会调用它。

因此,++尝试仅在您调用 时评估参数it.hasNext,但到那时it已经重新定义为 的结果++,因此它最终尝试附加it到自身。

换句话说,vars 和 by-name 参数不能一起工作。

所以不要将Iterator方法结果重新分配给同一个变量,而是给它们新的名字:

val it = Iterator(1)
it.next
val it2 = Iterator(2) ++ it
it2.next
it2.hasNext
Run Code Online (Sandbox Code Playgroud)

  • @JackKoenig是的,通过名称传递创建了一个闭包,并且闭包确实看到了范围内“var”的更改。例如:`def make(): () => Int = { var i = 0; def 结果() = { i += 1; 我 }; 结果 }; val f = make(); println(f(), f(), f())` 将打印 `(1,2,3)` (4认同)