Ejecución diferida y evaluación entusiasta

¿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) 

Ejecución inmediata

 IEnumerable 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 ejecuta maxIndex 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 miembro Current de IEnumerator y eso es todo.

Costo : grande por adelantado, pequeño durante la enumeración (solo una copia)

Ejecución diferida pero ansiosa

 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; } } 
  • 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ómputo, almacena el resultado en una matriz y Current devolverá el primer valor.
  • Cada llamada posterior a 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)

Ejecución diferida y perezosa

 IEnumerable 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 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í)

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 se IEnumerator instancia de IEnumerator (Para IEnumerable es cuando se llama a GetEnumerator )
  • 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.

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.