c# - biblicas - las 7 dispensaciones ilustradas
EjecuciĆ³n diferida y evaluaciĆ³n entusiasta (2)
¿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?
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.
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 lo que significa perezoso 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)
Ejecución inmediata
IEnumerable<int> GetComputation(int maxIndex)
{
var result = new int[maxIndex];
for(int i = 0; i < maxIndex; i++)
{
result[i] = Computation(i);
}
return result;
}
- Cuando la función se llama
Computation
se ejecutamaxIndex
veces -
GetEnumerator
devuelve una nueva instancia del enumerador sin hacer nada más. - Cada llamada a
MoveNext
pone el valor almacenado en la siguiente celda de Array en el miembroCurrent
deIEnumerator
y eso es todo.
Costo : grande por adelantado, pequeño durante la enumeración (solo una copia)
Ejecución diferida pero ansiosa
IEnumerable<int> 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;
}
}
- Cuando la función se llama una instancia de una clase autogenerada (llamada "objeto enumerable" en la especificación) implementando
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. - La primera llamada a
MoveNext
ejecuta maxIndex por el método de cálculo, almacena el resultado en una matriz yCurrent
devolverá el primer valor. - Cada llamada posterior a
MoveNext
pondrá enCurrent
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)
Ejecución diferida y perezosa
IEnumerable<int> GetComputation(int maxIndex)
{
for(int i = 0; i < maxIndex; i++)
{
yield return Computation(i);
}
}
- Cuando la función se llama lo mismo que ocurre el caso de ejecución perezosa.
-
GetEnumerator
devuelve una nueva instancia del enumerador sin hacer nada más. - Cada llamada a
MoveNext
ejecuta una vez el código deComputation
, pone el valor enCurrent
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í)
Para resumir
- Inmediato significa que el cálculo / ejecución se realiza en la función y finaliza una vez que la función regresa. (Evaluación completamente ansiosa como lo hace la mayoría del código C #)
- Deferido / Ansioso significa que la mayor parte del trabajo se realizará en el primer
MoveNext
o cuando seIEnumerator
instancia deIEnumerator
(ParaIEnumerable
es cuando se llama aGetEnumerator
) - Deferido / Lazy significa que el trabajo se realizará cada vez que se
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.