Die Logik ist unerbittlich! IEnumerable
unterstützt Clone
nicht, und Sie benötigen Clone
, Sie sollten also nicht IEnumerable
verwenden.
Oder genauer gesagt, Sie sollten es nicht als grundlegende Grundlage für die Arbeit an einem Scheme-Interpreter verwenden. Warum nicht stattdessen eine triviale unveränderliche verknüpfte Liste erstellen?
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;
}
}
Beachten Sie, dass die ToEnumerable
Methode Sie die bequeme Nutzung in der Standard-C# nachgibt.
Um Ihre Frage zu beantworten:
Kann mir jemand eine realistische, nützlich, beispielsweise eines IEnumerable Objekt vorschlagen, die bieten eine effiziente „Clone()“ Methode nicht in der Lage sein würde? Gibt es ein Problem mit das "Yield" -Konstrukt?
Ein IEnumerable kann überall in der Welt für seine Daten gehen. Hier ist ein Beispiel, die Linien von der Konsole lautet:
IEnumerable<string> GetConsoleLines()
{
for (; ;)
yield return Console.ReadLine();
}
Es gibt zwei Probleme: erstens eine Clone
Funktion wäre nicht besonders einfach zu schreiben (und Reset
wäre sinnlos). Zweitens ist die Reihenfolge unendlich - was absolut zulässig ist. Sequenzen sind faul.
Ein weiteres Beispiel:
IEnumerable<int> GetIntegers()
{
for (int n = 0; ; n++)
yield return n;
}
beide Für diese Beispiele der „Abhilfe“ Sie akzeptiert haben würde nicht viel nützen, weil es nur den verfügbaren Speicher für immer erschöpfen würde oder aufzuhängen. Aber das sind absolut gültige Beispiele für Sequenzen.
Um C# - und F # -Sequenzen zu verstehen, müssen Sie Listen in Haskell betrachten, nicht Listen in Scheme.
Falls Sie denken, Sie die unendliche Sachen ein roter Hering ist, wie etwa die Bytes von einem Socket zu lesen:
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];
}
}
Wenn es eine bestimmte Anzahl von Bytes ist, die Buchse herabgesandt wird, wird dies nicht sein unendliche Sequenz. Und es wäre sehr schwierig, Clone dafür zu schreiben. Wie würde der Compiler die IEnumerable-Implementierung generieren, um dies automatisch zu tun?
Sobald ein Klon erstellt wurde, müssten beide Instanzen nun von einem gemeinsamen Puffersystem aus arbeiten. Es ist möglich, aber in der Praxis wird es nicht benötigt - so werden diese Sequenzen nicht verwendet. Sie behandeln sie rein "funktional", wie Werte, und wenden Filter rekursiv auf sie an, anstatt sich "zwingend" an einen Ort innerhalb der Sequenz zu erinnern.Es ist ein wenig sauberer als Low-Level car
/cdr
Manipulation.
Weitere Frage:
Ich frage mich, was ist das niedrigste Niveau "primitive (s)" Ich müsste so dass alles, was ich kann mit einem IEnumerable in meinen Scheme-Interpreter tun will könnte im Schema eher als als eingebaut implementiert werden.
Die kurze Antwort würde ich in Abelson and Sussman und insbesondere the part about streams suchen. IEnumerable
ist ein Stream, keine Liste. Und sie beschreiben, wie Sie spezielle Versionen von Map, Filter, Accumulate usw. benötigen, um mit ihnen zu arbeiten. Sie kommen auch auf die Idee der Vereinigung von Listen und Streams in Abschnitt 4.2.
Ich bin mir nicht sicher, warum Sie einen klonbaren IEnumerator möchten, aber wäre dies eine klonbare IEnumerable-Hilfe? – Lucas
FYI, Es kann geklont werden. Meine Antwort vor langer Zeit zeigt, wie. Es klont tatsächlich den IEnumerator und nicht die daraus erstellten Werte. – BenMaddox