我不声称对此答案具有权威性。相反,这只是我对我读到的关于迭代器和/或我自己的想法的各种讨论的回忆......不幸的是,我没有源列表(很久以前),也不能轻易生成一个(因为即使谷歌搜索也需要有时很多时间)。
StopIteration
StopIteration 这个名字和很多语义都源自python。
PEP 234 对替代方法有一些评论:
hasNext()
Java 的hasNext() 只是一个“奖励”。然而,Java 的 next() 有自己的 StopIteration 风格,称为 NoSuchElementException。
hasNext() 很难实现,如果不是不可能的话,最好用例如以下生成器:
var i = 0;
function gen() {
yield doSomethingDestructive();
if (!i) {
yield doSomethingElse();
}
}
您将如何为此实现hasNext()?您不能简单地“窥视”生成器,因为这实际上会执行 doSomethingDestructive(),而这是您不打算执行的,否则您一开始会调用 next()。
因此,您必须编写一些代码分析器,该分析器必须始终可靠地证明给定代码在以特定状态运行时将始终导致产生或没有产生(停止问题)。因此,即使您可以编写类似的内容,状态仍可能在.hasNext() 和随后的.next() 之间变化。
hasNext() 在以下示例中会返回什么:
var g = gen();
g.next();
if (g.hasNext()) {
++i;
alert(g.next());
}
那时true 将是正确的答案。但是,随后的.next() 会抛出,用户可能很难弄清楚为什么......
你可以告诉人们:“不要在你的迭代器实现中做以下事情”(合同)。哪些人会经常破坏和抱怨。
所以在我看来,hasNext() 构思不周,很容易写出错误的代码。
.MoveNext()
C# 与.next() 几乎相同,只是返回值指示是否实际上存在下一项而不是或缺少异常。
但是有一个重要的区别:您需要将当前项目存储在迭代器本身中,例如C# 中的.Current,这可能会不必要地延长当前项目的生命周期:
var bufferCtor = (function gen() {
for(;;) {
yield new ArrayBuffer(1<<20); // buffer of size 1 megabyte
}
})();
setInterval(function() {
// MoveNext scheme
bufferCtor.MoveNext(); // Not actually Javascript
var buf = bufferCtor.Current; // Not actually Javascript
// vs. StopIteration scheme
var buf = bufferCtor.next();
}, 60000); // Each minute
好的,这个例子有点做作,但我确信在实际用例中,生成器/迭代器会返回一些占用大量空间或占用资源的东西。
在StopIteration 方案中,buf 一旦超出范围就可以进行垃圾回收。
在.MoveNext() 中,在再次调用.next() 之前无法对其进行垃圾回收,因为.Current 仍将持有对它的引用。
结论
在我看来,在所提出的替代方案中,StopIteration 是最不容易出错且最不模糊的方法。而且如果我没记错的话,python 和 es-6 的人也是这么想的。