2010-03-25 25 views
10

¿Podría darme un ejemplo para la ejecución diferida con evaluación entusiasta en C#?Ejecución diferida y evaluación entusiasta

Leí en 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?

Respuesta

1

Una forma de evaluar con impaciencia una ejecución diferida IEnumerable es simplemente convertirla en una matriz mediante la función .ToArray() de linq.

var evaluated = enumerable.ToArray(); 

Esto fuerza la evaluación de todo el enumerable y luego tiene la matriz que puede hacer lo que quiera con.

+0

Ho Joel Martinez, muchas gracias por su respuesta. –

+0

¡No tan rápido! Esto no funcionará para datos relacionales. –

+0

¿Qué tienen que ver los datos relacionales con la evaluación entusiasta de un ienumerable? incluso si lo ienumerable está en contra de algo como linq2sql o ef, iterará todos los registros en la memoria (lo que le permite cerrar la conexión db antes). –

43

Bellow es mi respuesta, pero también tenga en cuenta que Jon Skeet habló sobre esto hoy en 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é perezoso exactamente significa cuando lo usan en Just how lazy are you ? su publicación para una lectura interesante.

Además Wikipedia suponer que tres normas deben mantenerse para la evaluación perezosa y tercer punto no se respeta en el sentido de MSDN que la expresión se evaluará más de una vez si GetEnumerator se llama de nuevo (Por la especificación de reinicio no se ha implementado en el empadronador objetos generados usando la palabra clave yield y la mayoría de LINQ utilizarlo actualmente)


Teniendo en cuenta 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 se invoca la función se ejecuta ComputationmaxIndex veces
  • GetEnumerator devuelve una nueva instancia del empadronador no hacer nada más.
  • Cada llamada a MoveNext pone el valor almacenado en la siguiente celda de la matriz en el miembro Current de IEnumerator y eso es todo.

Costo: Big adelantado, Pequeño durante la enumeración (sólo una copia)

diferido pero la ejecución ansiosos

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 de auto generada (llamado "objeto enumerable" en la especificación) que implementa IEnumerable se crea y se almacena una copia del argumento (maxIndex).
  • 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 y Current devolverá el primer valor.
  • Cada llamada subsiguiente a MoveNext pondrá en Current un valor almacenado en la matriz.

Costo: nada por adelantado, Big cuando el inicio enumeración, Pequeño durante la enumeración (sólo una copia)

diferido y ejecución perezoso

IEnumerable<int> GetComputation(int maxIndex) 
{ 
    for(int i = 0; i < maxIndex; i++) 
    { 
     yield return Computation(i); 
    } 
} 
  • Cuando la función se llama el lo mismo que sucede con el caso de ejecución perezosa.
  • GetEnumerator devuelve una nueva instancia del enumerador sin hacer nada más.
  • Cada llamada a MoveNext ejecutar una vez el código Computation, poner el valor en Current y dejar 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 las de ordenación.

Costo: nada por adelantado, moderado durante la enumeración (el cálculo se ejecuta allí)

En resumen

  • inmediata significa que el cálculo/la ejecución se realiza en la función y terminó una vez que la función regreso. (Totalmente eager evaluación ya que la mayoría código C# hace)
  • diferido/Eager quiere decir que la mayor parte del trabajo se realizará en la primera MoveNext o cuando se crea la instancia IEnumerator (Para IEnumerable es cuando GetEnumerator se llama)
  • diferido/Lazy significa que el trabajo se realizará cada vez que se llame a MoveNext pero nada antes.

Parallel LINQ lo hace un poco diferente que podría ser considerado el cálculo diferido/Lazy desde el punto de vista de la persona que llama pero internamente el cálculo de un número de elementos comienzan en paralelo, tan pronto como comience 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.

+0

VirtualBlackFox -> muchas gracias ... esto es tan excelente explicación que nunca he visto (al menos en este tema :)) ..... pero todavía no estoy claro con su ejemplo de "diferido pero ansioso" evaluación "..., cuando usamos la palabra clave de retorno de yeild, se vuelve floja ... entonces, ¿por qué lo dices como" evaluación entusiasta "... podrías explicar esto? además, ¿cómo difiere la ejecución diferida de la evaluación perezosa?tanto la ejecución diferida como la evaluación diferida son las mismas? –

+0

La descripción que agregué debajo de cada ejemplo muestra la diferencia: en este caso diferido significa que la ejecución se realizará al enumerar no al llamar a la función. Cuando esté ansioso, incluso si sucede más tarde, todo el cálculo se realizará al mismo tiempo (cuando se llama por primera vez a GetNext), mientras que la evaluación lenta implica que cada llamada GetNext hace una parte del trabajo. –

+4

El mejor ejemplo para la diferencia es "Seleccionar" y "Revertir": ambos son diferidos, la fuente enumerable solo es almacenada por la llamada al método. Select es flojo ya que cada vez que se invoca GetNext en el resultado, se invoca GetNext en la fuente, incluso funciona con fuentes infinitas y si la fuente realiza un cálculo complejo, la carga se distribuirá. Por el contrario, la inversión es deseosa, necesita recorrer toda la fuente enumerable antes de devolver su primer elemento, si la fuente realiza cálculos complejos, todos se ejecutarán en la primera llamada GetNext al resultado. –

Cuestiones relacionadas