(理論的に)無限量のアイテムを返す列挙子をどのように設計しますか?
-
10-07-2019 - |
質問
次のようなコードを書いています:
public IEnumerable<T> Unfold<T>(this T seed)
{
while (true)
{
yield return [next (T)object in custom sequence];
}
}
明らかに、このメソッドは決して戻りません。 (C#コンパイラはこれを黙って許可しますが、R#は警告 <!> quot; Functionが<!> quot; を返すことはありません。)
一般的に言って、列挙を停止する方法を提供せずに、無限数のアイテムを返す列挙子を提供するのは悪い設計ですか?
このシナリオに関する特別な考慮事項はありますか?え?パフォーマンス?他の落とし穴?
常に終了条件を指定する場合、どのオプションがありますか?例:
- 包括的または排他的境界を表すタイプTのオブジェクト
- a
Predicate<T> continue
(TakeWhile
と同様) - カウント(
Take
と同様) - ...
Take(...)
の後にTakeWhile(...)
/ Unfold(...)
を呼び出すユーザーに依存する必要がありますか? (既存のLinqの知識を活用するため、おそらく好ましいオプションです。)
コードがそのまま(汎用)またはこのパターンの特定の実装として、パブリックAPIで公開される場合、この質問に異なる回答をしますか?
解決
メソッドが反復を終了しないことを非常に明確に文書化する限り(もちろん、メソッド自体は非常に速く戻ります)、それで問題ないと思います。確かに、それはいくつかのアルゴリズムをずっときれいにすることができます。重要なメモリ/パフォーマンスへの影響はないと思います-<!> quot;高価な<!> quot;イテレータ内のオブジェクト、その参照がキャプチャされます。
APIを悪用する方法は常にあります。ドキュメントが明確である限り、問題ないと思います。
他のヒント
<!> quot;一般的に言って、それは悪いデザインですか? を返す列挙子を提供する 無限のアイテム、なし 列挙を停止する方法を提供しますか?<!> quot;
コードのコンシューマーは、列挙の停止を常にすることができます(たとえば、breakまたはその他の手段を使用して)。列挙子が無限のシーケンスを返す場合、列挙子のクライアントが列挙を壊さないように強制されているわけではありません。実際には、クライアントによって完全に列挙されることが保証されている列挙子を作成することはできません。
ユーザーの呼び出しに依存すべきか Take(...)/ TakeWhile(...)after 展開(...)? (多分好ましい オプション、既存の Linqの知識。)
はい、ドキュメントで列挙子が返すことを明確に指定し、無限のシーケンスと列挙の破壊が呼び出し元の責任である限り、すべてが問題ないはずです。
無限のシーケンスを返すことは悪い考えではありません。関数型プログラミング言語は長い間それを行ってきました。
ジョンに同意します。コンパイラは、メソッドを、現在の値(つまり、Currentプロパティを介して返される値)への参照を保持する単純なステートマシンを実装するクラスに変換します。コードを簡素化するために、このアプローチを数回使用しました。メソッドの動作を明確に文書化すると、正常に機能するはずです。
パブリックAPIで無限列挙子を使用しません。私自身も含めたC#プログラマーは、foreachループにも慣れています。これは、.NET Frameworkと一貫性があります。 Enumerable.RangeおよびEnumerable.Repeatメソッドが引数を取り、Enumerable内のアイテムの数を制限する方法に注目してください。 Microsoftは、Enumerable.Repeat(<!> quot; <!> quot;)。Take(10)の代わりにEnumerable.Repeat(<!> quot; <!> quot ;, 10)を使用して、無限列挙とI設計の選択を遵守します。