【问题标题】:In Draft ECMAScript 6, what's the rationale behind using StopIteration exception to signal the end of iteration?在 ECMAScript 6 草案中,使用 StopIteration 异常来表示迭代结束的基本原理是什么?
【发布时间】:2013-08-20 16:16:09
【问题描述】:

Draft ECMAScript 6 Specification 中,使用StopIteration 异常来表示迭代结束而不是使用专用方法(Java/Scala 中的hasNext 和C# 中的MoveNext)来检查结束的基本原理是什么的迭代。

抛开潜在的性能问题不谈,在我看来,异常不应用于并非真正异常的事情。

【问题讨论】:

  • 对于 ES6 讨论组来说,这是一个更好的问题。但我认为你误解了例外。这不是您需要在代码中关注的事情。这是一个实现细节。
  • @CrazyTrain:你能给我指出一个 ES6 讨论组吗?在我的代码中无需担心它并不会使我失去提出一些问题的权利。
  • 不需要关心你的代码?不会使您的权利无效吗?我不知道你在说什么。当然你可以问别人,但如果你要问理由,你需要问那些合理化的人。反正 StackOverflow 不是一个讨论论坛,所以你的问题在这里放错了地方。

标签: javascript iterator ecmascript-harmony ecmascript-6


【解决方案1】:

我不声称对此答案具有权威性。相反,这只是我对我读到的关于迭代器和/或我自己的想法的各种讨论的回忆......不幸的是,我没有源列表(很久以前),也不能轻易生成一个(因为即使谷歌搜索也需要有时很多时间)。

StopIteration

StopIteration 这个名字和很多语义都源自python

PEP 234 对替代方法有一些评论:

  • 有人质疑异常是否标志着结束 迭代并不太昂贵。的几种替代方案 已提出 StopIteration 异常:特殊值 End 表示结束,一个函数 end() 来测试迭代器是否 完成了,甚至重用了IndexError异常。

    • 一个特殊值的问题是,如果一个序列曾经 包含该特殊值,对该序列的循环将 在没有任何警告的情况下提前结束。如果有经验 以 null 结尾的 C 字符串并没有告诉我们这个问题 可能会导致,想象一下 Python 自省工具的麻烦 将遍历所有内置名称的列表, 假设特殊的 End 值是一个内置名称!

    • 调用 end() 函数需要每次调用两次 迭代。两个电话比一个电话贵得多 加上一个异常测试。尤其是时间紧迫的 for 循环可以非常便宜地测试异常。

    • 重用 IndexError 可能会导致混淆,因为它可能是 真正的错误,将通过结束循环来掩盖 过早的。

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 的人也是这么想的。

【讨论】:

    猜你喜欢
    • 2018-04-12
    • 2016-07-11
    • 2011-10-10
    • 1970-01-01
    • 2013-02-21
    • 2021-03-11
    • 2012-02-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多