5


1

C#はCのイテレータのような種類の列挙子の違いから恩恵を受けるでしょうか?

私は IEnumerator.Reset()`メソッドについて考えていました。 私はそれがCOM相互運用機能のためだけにあることをMSDNドキュメントで読みました。 Cプログラマーとしては、 `Reset`をサポートする IEnumerator`のように見えます。 `Reset`をサポートしていない`は実際にはhttp://www.sgi.com/tech/stl/InputIterator.html [入力反復子]です。

だから私の質問の一部は、この理解は正しいのですか?

私の質問の2番目の部分は、入力イテレータと順方向イテレータの間に区別があればそれはC#で何か利点があるのでしょうか(あるいはあなたが望むなら "列挙子")。 https://stackoverflow.com/questions/807991/why-cant-ienumerators-be-cloned [クローン作成イテレータに関する質問]にあるような、プログラマ間の混乱を解消するのに役立つことはないでしょうか。

編集:フォワードおよび入力イテレータの説明。 入力反復子は、コレクションのメンバを(またはジェネレータ関数または入力ストリームから)一度だけ列挙できることを保証するだけです。 これがまさにIEnumeratorがC#で機能する仕組みです。 もう一度列挙できるかどうかは、 `Reset`がサポートされているかどうかによって決まります。 順方向反復子にはこの制限はありません。 あなたは何度でもメンバーを列挙することができます。

何人かのC#プログラマは、なぜ `IEnumerator`がマルチパスアルゴリズムで確実に使用できないのかを疑いません。 次のような場合を考えます。

void PrintContents(IEnumerator xs)
{
  while (iter.MoveNext())
    Console.WriteLine(iter.Current);
  iter.Reset();
  while (iter.MoveNext())
    Console.WriteLine(iter.Current);
}

このコンテキストで `PrintContents`を呼び出しても問題ありません。

List ys = new List() { 1, 2, 3 }
PrintContents(ys.GetEnumerator());

しかし、以下を見てください。

IEnumerable GenerateInts() {
  System.Random rnd = new System.Random();
  for (int i=0; i < 10; ++i)
    yield return Rnd.Next();
}

PrintContents(GenerateInts());

もし IEnumerator`が Reset`をサポートしていた、言い換えればマルチパスアルゴリズムをサポートしていたのであれば、コレクションを反復するたびにそれは異なったものになります。 これは驚くべき動作であるため、望ましくありません。 この例は少し偽物ですが、現実の世界では起こります(例: ファイルストリームからの読み込み)

5 Answer


3


「リセット」は大きな間違いでした。 私は「リセット」という言葉で狂人たちを呼ぶ。 私の意見では、.NET型システムで "順方向反復子"と "入力反復子"を区別するための正しい方法は、 IEnumerable`と IEnumerator`を区別することです。

https://stackoverflow.com/questions/1468170/why-the-reset-method-on-enumerator-class-mrow-throw-a-notsupportedexception/1468189#1468189 [この回答]も参照してください。ここで、MicrosoftのEric Lippert(非公式な能力、疑いの余地はないが、私の主張は、これは設計ミスであると主張しなければならないよりも彼がより多くの信任状を持つ人であるということだけである。 http://blogs.msdn.com/ericlippert/default.aspx [この素晴らしいブログ]もご覧ください。


2


興味深い質問です。 私の考えでは、もちろんC#は_利益_になります。 しかし、追加するのは簡単ではありません。

Cにはそのはるかに柔軟な型システムのために区別があります。 C#では、(マルチパス反復をサポートするために)順方向反復子を表すために必要な、オブジェクトを複製するための堅牢な汎用方法はありません。 もちろん、これが本当に便利になるためには、双方向およびランダムアクセスの反復子/列挙子もサポートする必要があります。 そして、それらすべてをスムーズに機能させるには、Cテンプレートのように、ある種のアヒルタイピングが本当に必要です。

結局、2つの概念の範囲は異なります。

Cでは、イテレータはある範囲の値について知る必要があるすべてのものを表現することになっています。 一対のイテレータを考えれば、私は元のコンテナを必要としません。 ソート、検索、要素の操作やコピーを好きなだけ実行できます。 元のコンテナーは絵の外です。

C#では、列挙子はそれほど多くのことをするつもりはありません。 究極的には、それらは直線的にあなたがシーケンスを駆け抜けるように設計されています。

`Reset()`に関しては、そもそもそれを追加するのは間違いだったことが広く受け入れられています。 正しく機能し、正しく実装されていれば、列挙子は順方向反復子に類似していると言えますが、一般的には、それを無視して間違いを犯すことをお勧めします。 そして、すべての列挙子は入力反復子にのみ似ています。

残念ながら


1


  • C#の観点から来ると*

IEnumerator`を直接使うことはほとんどありません。 通常、あなたは `IEnumerable`を期待する foreach`ステートメントを行います。

IEnumerable _myCollection;
...
foreach (var item in _myCollection) { /* Do something */ }

あなたは IEnumerator`も迂回しません。 繰り返しが必要なコレクションを渡したい場合は、 `IEnumerable`を渡します。 `IEnumerable`は IEnumerator`を返す単一の関数を持っているので、コレクションを複数回繰り返すのに使うことができます(複数回のパス)。

最初からやり直したいのなら、古いものを捨てて新しいものを取得するだけなので、 IEnumerator`に Reset() `関数は必要ありません。


1


それがどんな能力をサポートすることができて、それがどんな約束をすることができるかについて IEnumerator`に尋ねる手段があるならば、.NET frameworkは非常に有益です。 そのような機能は `IEnumerable`でも役に立ちますが、列挙子の質問をすることができれば、 ReadOnlyCollection`のようなラッパーから列挙子を受け取ることができるコードは、ラッパーを巻き込まずに基礎となるコレクションを改良することができます。

全体を列挙することができ、大きすぎないコレクションの列挙子を考えると、そこから常に同じ項目のシーケンス(具体的には列挙子に残っている項目のセット)をもたらす `IEnumerable`を生成できます。 )その内容全体を配列に読み込み、列挙子を破棄して破棄し、(元の破棄された列挙子の代わりにそれを使用して)列挙子を取得し、それを `ReadOnlyCollection`にラップして返します。 そのようなアプローチは上記の基準を満たすどんな種類の列挙可能なコレクションでも働くでしょうが、それはそれらのほとんどで恐ろしく非効率的でしょう。 列挙子に残りの内容を不変の `IEnumerable`にするように依頼する手段があれば、さまざまな種類の列挙子が指示されたアクションをより効率的に実行できるようになります。


-1


私はそうは思わない。 私は `IEnumerable`をフォワードイテレータと入力イテレータと呼ぶでしょう。 それはあなたが後ろに戻ったり、基になるコレクションを変更することを可能にしません。 `foreach`キーワードが追加されたことで、イテレータはほとんど考えられなくなりました。

意見:入力イテレータ(get each one)との違い 出力イテレータ(それぞれに_を_する)は、フレームワークへの追加を正当化するにはあまりにも簡単です。 また、出力イテレータを実行するには、デリゲートをイテレータに渡す必要があります。 入力イテレータは、C#プログラマにとってはより自然に見えます。

プログラマがランダムアクセスを望んでいる場合には「IList」もあります。