荷物の中にイテレータの背後にあるものの一般的な考えがあるので、それらの使用に関するいくつかの質問にすでに答えることができます。 実際のイベントに基づいたシナリオは次のとおりです。
大きくて複雑なイテレーターがあり、リファクタリングしたいです。 明確にするために、列挙子が1〜100を2回カウントすることに同意します。 (もちろん、現実の世界では、イテレーターはそれほど単純ではありません。)
IEnumerable<int> CountTo100Twice() { int i; for (i = 1; i <= 100; i++) { yield return i; } for (i = 1; i <= 100; i++) { yield return i; } }
Pragramming 101からわかるように、一般的なコードをサブルーチンに入れて呼び出すことができます。 しかし、これを行うと、コンパイラエラーが発生します。
IEnumerable<int> CountTo100Twice() { CountTo100(); CountTo100(); } void CountTo100() { int i; for (i = 1; i <= 100; i++) { yield return i; } }
何が間違っていますか? 「カウントを100に」をサブルーチンに転送し、CountTo100Twice関数から2回呼び出すにはどうすればよいですか?
先ほど見たように、反復子はルーチンではありません。 たとえば、ステートマシンの代わりにファイバーからイテレータを作成した場合、上記の手法は完全に機能します。 ただし、オートマトンが使用されるため、yield return式は「トップレベル」に配置する必要があります。 それでは、ルーチンをどのように繰り返しますか?
サブルーチン自体を反復子にして、メイン関数から結果を「プル」します。
IEnumerable<int> CountTo100Twice() { foreach (int i in CountTo100()) yield return i; foreach (int i in CountTo100()) yield return i; } IEnumerable<int> CountTo100() { for (i = 1; i <= 100; i++) { yield return i; } }
演習:次のスニペットを検討してください。
foreach (int i in CountTo100Twice()) { // ... }
上記のループでMoveNext()を150回(50回)呼び出すとどうなるかを説明します。 再帰アルゴリズム(ツリートラバーサルなど)に対するこの影響を考慮してください。