0

Draft ECMAScript 6 Specificationでは、専用メソッド ( Java/Scala およびC#) を使用して反復の終了を確認するStopIteration代わりに、例外を使用して反復の終了を通知する根拠は何ですか。hasNextMoveNext

潜在的なパフォーマンスの問題はさておき、私の意見では、実際には例外ではないものに例外を使用するべきではありません。

4

1 に答える 1

2

私はこの答えで権威があるとは主張しません。代わりに、イテレータや自分の考えについて読んださまざまな議論の記憶に過ぎません... 残念ながら、ソースのリストはありません (かなり前のことです)。時には多くの時間)。

StopIteration

名前と多くのセマンティクスはpythonStopIterationに由来します。

PEP 234には、代替アプローチに関するいくつかのコメントがあります。

  • 反復の終了を通知する例外が高すぎないかどうかは疑問視されています。StopIteration 例外のいくつかの代替案が提案されています: 特別な値 End で終了を通知し、関数 end() で反復子が終了したかどうかをテストし、IndexError 例外を再利用することさえあります。

    • 特別な値には、シーケンスにその特別な値が含まれている場合、そのシーケンスのループが警告なしに途中で終了するという問題があります。null で終わる C 文字列の経験から、これが引き起こす可能性のある問題が分からない場合は、特別な End 値が組み込みの名前で!

    • end() 関数を呼び出すには、反復ごとに 2 回の呼び出しが必要です。2 回の呼び出しは、1 回の呼び出しと例外のテストよりもはるかにコストがかかります。特にタイム クリティカルな for ループは、非常に安価に例外をテストできます。

    • IndexError を再利用すると混乱を招く可能性があります。これは、ループを途中で終了することによってマスクされる本物のエラーである可能性があるためです。

hasNext()

JavahasNext()は単なる「おまけ」です。ただし、 Java のnext()には、独自の種類のStopIterationcalled があり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()ですが、戻り値は例外の代わりに実際に次の項目があったかどうか、または例外がないことを示しています。

ただし、重要な違いが 1 つあります。現在のアイテムをイテレータ自体 (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 

OK、その例は少し不自然ですが、ジェネレーター/イテレーターが多くのスペースを話したり、リソースを占有したりする何かを返す実際のユースケースがあると確信しています。

StopIterationスキームでは、buf範囲外になるとすぐにガベージコレクションできます。では、が参照を保持しているため、 が再度呼び出される.MoveNext()までガベージ コレクションできません。.next().Current

結論

私の意見では、提示された代替案のStopIterationうち、エラーが発生しにくく、あいまいさが最も少ないアプローチです。私の記憶が正しければ、python と es-6 の人々も同じことを考えていました。

于 2013-09-13T16:36:10.953 に答える