ロジックは容赦ないです!IEnumerable
は をサポートしておらずClone
、 が必要なClone
ので、使用しないでくださいIEnumerable
。
もっと正確に言えば、Scheme インタープリターで作業するための基本的な基礎としてそれを使用するべきではありません。代わりに、単純な不変のリンク リストを作成してみませんか?
public class Link<TValue>
{
private readonly TValue value;
private readonly Link<TValue> next;
public Link(TValue value, Link<TValue> next)
{
this.value = value;
this.next = next;
}
public TValue Value
{
get { return value; }
}
public Link<TValue> Next
{
get { return next; }
}
public IEnumerable<TValue> ToEnumerable()
{
for (Link<TValue> v = this; v != null; v = v.next)
yield return v.value;
}
}
このToEnumerable
メソッドは、標準の C# の方法で便利に使用できることに注意してください。
あなたの質問に答えるには:
効率的な「Clone()」メソッドを提供できないIEnumerableオブジェクトの現実的で有用な例を誰かが提案できますか? 「利回り」構造に問題があるということですか?
IEnumerable は、そのデータのために世界中のどこにでも行くことができます。コンソールから行を読み取る例を次に示します。
IEnumerable<string> GetConsoleLines()
{
for (; ;)
yield return Console.ReadLine();
}
これには 2 つの問題があります。第 1 に、Clone
関数を書くのは特に簡単ではありません (そしてReset
意味がありません)。第二に、シーケンスは無限です - これは完全に許容されます。シーケンスは怠惰です。
もう一つの例:
IEnumerable<int> GetIntegers()
{
for (int n = 0; ; n++)
yield return n;
}
これらの両方の例で、受け入れた「回避策」は、使用可能なメモリを使い果たすか、永遠にハングアップするだけなので、あまり役に立ちません。しかし、これらはシーケンスの完全に有効な例です。
C# と F# のシーケンスを理解するには、Scheme のリストではなく、Haskell のリストを見る必要があります。
無限のものはニシンだと思う場合は、ソケットからバイトを読み取るのはどうですか:
IEnumerable<byte> GetSocketBytes(Socket s)
{
byte[] buffer = new bytes[100];
for (;;)
{
int r = s.Receive(buffer);
if (r == 0)
yield break;
for (int n = 0; n < r; n++)
yield return buffer[n];
}
}
ソケットに送信されるバイト数がある場合、これは無限のシーケンスにはなりません。それでも、Clone を作成するのは非常に困難です。コンパイラはどのように IEnumerable 実装を生成して自動的に実行しますか?
クローンが作成されるとすぐに、両方のインスタンスが共有するバッファ システムから動作する必要があります。可能ですが、実際には必要ありません。これは、これらの種類のシーケンスが使用されるように設計されている方法ではありません。シーケンス内の場所を「命令的に」記憶するのではなく、値のように純粋に「機能的に」扱い、フィルターを再帰的に適用します。低レベルcar
/cdr
操作よりも少しクリーンです。
さらなる質問:
私のSchemeインタプリタでIEnumerableを使ってやりたいことがビルトインではなくスキームで実装できるようにするために必要な最低レベルの「プリミティブ」は何だろうか。
私が思う簡単な答えは、Abelson と Sussman、特にストリームに関する部分を調べることです。IEnumerable
リストではなくストリームです。また、マップ、フィルター、アキュムレートなどの特別なバージョンを使用して作業する方法についても説明しています。彼らはまた、セクション 4.2 でリストとストリームを統合するという考えにも触れています。