6

私はこれに似たコードを書いています:

public IEnumerable<T> Unfold<T>(this T seed)
{
    while (true)
    {
        yield return [next (T)object in custom sequence];
    }
}

明らかに、このメソッドは決して戻りません。(C#コンパイラはこれを黙って許可しますが、R#は「関数が戻らない」という警告を表示します。)

一般的に言って、列挙を停止する方法を提供せずに、無限の数のアイテムを返す列挙子を提供することは悪い設計ですか?

このシナリオについて特別な考慮事項はありますか?Mem?パフォーマンス?他の落とし穴?

常に終了条件を指定する場合、どのオプションがありますか?例えば:

  • 包括的または排他的な境界を表すタイプTのオブジェクト
  • a Predicate<T> continueTakeWhileそうです)
  • カウント(Takeそうです)
  • ..。

Take(...)/TakeWhile(...)後に呼び出すユーザーに依存する必要がありますUnfold(...)か?(既存のLinqの知識を活用しているため、おそらく推奨されるオプションです。)

コードがそのまま(一般的)またはこのパターンの特定の実装としてパブリックAPIで公開される場合、この質問に別の方法で答えますか?

4

4 に答える 4

7

メソッドが反復を終了しないことを非常に明確に文書化する限り(もちろん、メソッド自体は非常に迅速に戻ります)、それで問題ないと思います。確かに、それはいくつかのアルゴリズムをはるかにきれいにすることができます。メモリ/パフォーマンスに重大な影響があるとは思いませんが、イテレータ内で「高価な」オブジェクトを参照すると、その参照がキャプチャされます。

APIを悪用する方法は常にあります。ドキュメントが明確である限り、問題ないと思います。

于 2008-10-13T11:33:20.017 に答える
6

「一般的に言えば、列挙を停止する方法を提供せずに、無限の数のアイテムを返す列挙子を提供するのは悪い設計ですか?」

コードのコンシューマーは、いつでも列挙を停止できます (ブレークなどの手段を使用)。列挙子が無限のシーケンスを返した場合、それは列挙子のクライアントが何らかの理由で列挙を中断しないように強制されているという意味ではありません。実際には、クライアントによって完全に列挙されることが保証されている列挙子を作成することはできません。

Unfold(...) の後に Take(...) / TakeWhile(...) を呼び出すユーザーに依存する必要がありますか? (既存のLinqの知識を活用するため、おそらく好ましいオプションです。)

はい、ドキュメントで列挙子が返すことと無限のシーケンスと列挙の中断が呼び出し元の責任であることを明確に指定している限り、すべて問題ありません。

無限シーケンスを返すことは悪い考えではありません。関数型プログラミング言語は長い間それを行ってきました。

于 2008-10-13T11:47:18.337 に答える
2

私はジョンに同意します。コンパイラは、現在の値 (つまり、Current プロパティを介して返される値) への参照を保持する単純なステート マシンを実装するクラスにメソッドを変換します。コードを簡素化するために、このアプローチを数回使用しました。メソッドの動作を明確に文書化すれば、問題なく動作するはずです。

于 2008-10-13T12:03:17.273 に答える
0

パブリック API では無限列挙子を使用しません。私を含め、C# プログラマーは foreach ループに慣れすぎています。これは、.NET Framework とも一致します。Enumerable.Range および Enumerable.Repeat メソッドが引数を取り、Enumerable 内のアイテムの数を制限する方法に注目してください。Microsoft は、Enumerable.Repeat(" ").Take(10) の代わりに Enumerable.Repeat(" ", 10) を使用して無限の列挙を回避することを選択しました。私は彼らの設計上の選択に従います。

于 2008-10-13T12:15:20.713 に答える