¡La lógica es inexorable! IEnumerable
no es compatible con Clone
, y necesita Clone
, por lo que no debe usar IEnumerable
.
O más exactamente, no debe utilizarlo como la base fundamental para trabajar en un intérprete de Scheme. ¿Por qué no hacer una lista vinculada inmóvil trivial en su lugar?
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;
}
}
Tenga en cuenta que el método ToEnumerable
le da el uso conveniente en el directorio C# forma estándar.
Para responder a su pregunta:
Puede alguien sugerir un realista, útil, ejemplo de un objeto IEnumerable que no sería capaz de proporcionar un método "() Clon" eficiente? ¿Es que habría un problema con el constructo "yield"?
Un IEnumerable puede ir a cualquier parte del mundo para sus datos. He aquí un ejemplo que lee líneas desde la consola:
IEnumerable<string> GetConsoleLines()
{
for (; ;)
yield return Console.ReadLine();
}
Hay dos problemas con este: en primer lugar, una función Clone
no sería particularmente fácil de escribir (y Reset
no tendría sentido). En segundo lugar, la secuencia es infinita, lo cual es perfectamente permisible. Las secuencias son flojas.
Otro ejemplo:
IEnumerable<int> GetIntegers()
{
for (int n = 0; ; n++)
yield return n;
}
Para estos dos ejemplos, la "solución" que haya aceptado no sería de mucha utilidad, ya que acaba de agotar la memoria disponible o colgar para siempre. Pero estos son ejemplos perfectamente válidos de secuencias.
Para comprender las secuencias C# y F #, debe consultar las listas en Haskell, no las listas en Scheme.
En caso de que creo la materia infinita es una pista falsa, ¿qué hay de la lectura de los bytes desde un socket:
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];
}
}
Si hay algún número de bytes que se envían por el zócalo, esto no será una secuencia infinita Y sin embargo escribir Clone sería muy difícil. ¿Cómo generaría el compilador la implementación IEnumerable para hacerlo automáticamente?
Tan pronto como se creó un clon, ambas instancias tendrían que funcionar desde un sistema de búfer que compartieron. Es posible, pero en la práctica no es necesario, así no es cómo se diseñan estos tipos de secuencias para su uso. Los trata de manera puramente "funcional", como valores, aplicando filtros recursivamente, en lugar de recordar "imperativamente" una ubicación dentro de la secuencia.Es un poco más limpio que la manipulación de bajo nivel car
/cdr
.
Además pregunta:
Me pregunto, ¿cuál es el nivel más bajo "primitivo (s)" que necesitaría tal que nada de lo que desee hacer con un IEnumerable en mi esquema intérprete podría implementarse en el esquema en lugar de que como un built-in.
La respuesta corta, creo que sería buscar en Abelson and Sussman y en particular the part about streams. IEnumerable
es una secuencia, no una lista. Y describen cómo necesita versiones especiales de mapa, filtro, acumulación, etc. para trabajar con ellos. También tienen la idea de unificar listas y flujos en la sección 4.2.
No estoy seguro de por qué quiere un IEnumerator clonable, pero ¿sería útil un IEnumerable clonable? – Lucas
FYI, se puede clonar. Mi respuesta hace mucho tiempo muestra cómo. En realidad clona el IEnumerator y no los valores creados a partir de él. – BenMaddox