¿Podría darme un ejemplo para la ejecución diferida con una evaluación entusiasta en C #?
Leí de MSDN que la ejecución diferida en LINQ puede implementarse con evaluación floja o con ganas. Pude encontrar ejemplos en Internet para la ejecución diferida con evaluación diferida, pero no pude encontrar ningún ejemplo para la ejecución diferida con evaluación entusiasta.
Además, ¿cómo difiere la ejecución diferida de la evaluación perezosa? En mi punto de vista, ambos se ven iguales. ¿Podría darnos algún ejemplo de esto también?
Bellow es mi respuesta, pero también tengo en cuenta que Jon Skeet habló hoy sobre su blog y sobre el hecho de que no está totalmente de acuerdo con el significado de MSDN de “Lazy” ya que MSDN no está realmente claro de qué vagan exactamente cuando usan en ¿Tan perezoso eres? su publicación es una lectura interesante.
Además, Wikipedia asume que se deben mantener tres reglas para la evaluación diferida y el tercer punto no se respeta en MSDN, ya que la expresión se evaluará más de una vez si se llama nuevamente a GetEnumerator
(Por la especificación Reset no se implementa en objetos del enumerador generados utilizando yield
palabra clave y la mayoría de linq lo usa actualmente)
Considerando una función
int Computation(int index)
IEnumerable GetComputation(int maxIndex) { var result = new int[maxIndex]; for(int i = 0; i < maxIndex; i++) { result[i] = Computation(i); } return result; }
Computation
se ejecuta maxIndex
veces GetEnumerator
devuelve una nueva instancia del enumerador sin hacer nada más. MoveNext
pone el valor almacenado en la siguiente celda de Array en el miembro Current
de IEnumerator
y eso es todo. Costo : grande por adelantado, pequeño durante la enumeración (solo una copia)
IEnumerable GetComputation(int maxIndex) { var result = new int[maxIndex]; for(int i = 0; i < maxIndex; i++) { result[i] = Computation(i); } foreach(var value in result) { yield return value; } }
IEnumerable
se crea y se almacena una copia del argumento ( maxIndex
) en ella. GetEnumerator
devuelve una nueva instancia del enumerador sin hacer nada más. MoveNext
ejecuta maxIndex por el método de cómputo, almacena el resultado en una matriz y Current
devolverá el primer valor. MoveNext
pondrá en Current
un valor almacenado en la matriz. Costo : nada por adelantado, grande cuando comienza la enumeración, pequeño durante la enumeración (solo una copia)
IEnumerable GetComputation(int maxIndex) { for(int i = 0; i < maxIndex; i++) { yield return Computation(i); } }
GetEnumerator
devuelve una nueva instancia del enumerador sin hacer nada más. MoveNext
ejecuta una vez el código de Computation
, pone el valor en Current
y deja que la persona que llama actúe inmediatamente sobre el resultado. La mayoría de los linq usan ejecución diferida y lenta, pero algunas funciones no pueden ser tan parecidas a la clasificación.
Costo : nada por adelantado, moderado durante la enumeración (el cálculo se ejecuta allí)
MoveNext
o cuando se IEnumerator
instancia de IEnumerator
(Para IEnumerable
es cuando se llama a GetEnumerator
) MoveNext
pero no se haya hecho nada antes. Paralelo LINQ lo hace un poco diferente ya que el cálculo puede considerarse diferido / perezoso desde el punto de vista de la persona que llama, pero internamente el cálculo de algunos elementos comienza en paralelo tan pronto como comienza la enumeración. El resultado es que si el siguiente valor ya está allí lo obtienes inmediatamente, pero de lo contrario tendrás que esperarlo.
Una forma de evaluar con entusiasmo una ejecución diferida IEnumerable es simplemente convertirla en una matriz utilizando la función .ToArray () de linq.
var evaluated = enumerable.ToArray();
Esto obliga a la evaluación del enumerable completo y luego tienes la matriz para que puedas hacer lo que quieras.