2012-04-05 10 views
6

In LINQ Where è un operatore di streaming. Dove-come OrderByDescending è un operatore non di streaming. AFAIK, un operatore di streaming raccoglie solo l'elemento successivo che è necessario. Un operatore non di streaming valuta l'intero flusso di dati contemporaneamente.Come si differenzia un operatore di streaming dall'esecuzione differita?

Non riesco a vedere la rilevanza della definizione di un operatore di streaming. Per me, è ridondante con la Deferred Execution. Prendi l'esempio in cui ho scritto un'estensione personalizzata e l'ho consumata usando l'operatore where e orderby.

public static class ExtensionStuff 
{ 
    public static IEnumerable<int> Where(this IEnumerable<int> sequence, Func<int, bool> predicate) 
    { 
     foreach (int i in sequence) 
     { 
      if (predicate(i)) 
      { 
       yield return i; 
      } 
     } 
    } 
} 

    public static void Main() 
    { 
     TestLinq3(); 
    } 

    private static void TestLinq3() 
    { 
     int[] items = { 1, 2, 3,4 }; 

     var selected = items.Where(i => i < 3) 
          .OrderByDescending(i => i); 

     Write(selected); 
    } 



    private static void Write(IEnumerable<int> selected) 
    { 
     foreach(var i in selected) 
      Console.WriteLine(i); 
    } 

In entrambi i casi, Where deve valutare ogni elemento per determinare quali elementi soddisfano la condizione. Il fatto che produca sembra diventare rilevante solo perché l'operatore ottiene un'esecuzione differita.

Quindi, qual è l'importanza degli operatori di streaming?

+5

Provatelo ancora con circa 2 miliardi di pollici in 'articoli'. – cHao

+2

@cHao o una sequenza infinita o una sequenza derivata da un flusso di rete aperto. –

+0

[Esempio più specifico] (http://codereview.stackexchange.com/a/9777/8246) –

risposta

11

Ci sono due aspetti: velocità e memoria.

L'aspetto della velocità diventa più evidente quando si utilizza un metodo come .Take() per consumare solo una parte del set di risultati originale.

// Consumes ten elements, yields 5 results. 
Enumerable.Range(1, 1000000).Where(i => i % 2 == 0) 
    .Take(5) 
    .ToList(); 

// Consumes one million elements, yields 5 results. 
Enumerable.Range(1, 1000000).Where(i => i % 2 == 0) 
    .OrderByDescending(i => i) 
    .Take(5) 
    .ToList(); 

Poiché il primo esempio utilizza solo gli operatori in streaming prima della chiamata a Take, solamente finisce per cedere valori da 1 a 10 prima Take fermate valutazione. Inoltre, un solo valore viene caricato in memoria alla volta, quindi si ha un ingombro di memoria molto ridotto.

Nel secondo esempio, OrderByDescending non è in streaming, quindi al momento Take prende il primo elemento, l'intero risultato passato attraverso il filtro Where deve essere inserito in memoria per l'ordinamento. Questo potrebbe richiedere molto tempo e produrre un grande ingombro di memoria.

Anche se non si utilizza Take, il problema di memoria può essere importante. Ad esempio:

// Puts half a million elements in memory, sorts, then outputs them. 
var numbers = Enumerable.Range(1, 1000000).Where(i => i % 2 == 0) 
    .OrderByDescending(i => i); 
foreach(var number in numbers) Console.WriteLine(number); 

// Puts one element in memory at a time. 
var numbers = Enumerable.Range(1, 1000000).Where(i => i % 2 == 0); 
foreach(var number in numbers) Console.WriteLine(number); 
2

Il fatto che cede sembra diventare rilevanti solo perché i guadagni operatore differito esecuzione.

Quindi, qual è l'importanza degli operatori di streaming?

I.e. non è possibile elaborare sequenze infinite con i metodi di estensione buffering/non-streaming - mentre è possibile "eseguire" una tale sequenza (finché non si interrompe) bene usando solo i metodi di estensione streaming.

Prendiamo ad esempio questo metodo:

public IEnumerable<int> GetNumbers(int start) 
{ 
    int num = start; 

    while(true) 
    { 
     yield return num; 
     num++; 
    } 
} 

È possibile utilizzare Where bene:

foreach (var num in GetNumbers(0).Where(x => x % 2 == 0)) 
{ 
    Console.WriteLine(num); 
} 

OrderBy() non avrebbe funzionato in questo caso, dal momento che avrebbe dovuto elencare in modo esaustivo i risultati prima di emettere un numero singolo

+1

Nitpick, ma è infinito, è solo che ogni risultato non è unico. Finirà per traboccare, ma si avvolge, quindi può traboccare un numero infinito di volte. (Finché non ci si trova in un blocco di spunta.) Si potrebbe avere 'while (true) yield return 4;' se lo si desidera. – Servy

+0

Aah è giusto ;-) – BrokenGlass

2

Giusto per essere espliciti; nel caso in cui hai menzionato non c'è alcun vantaggio sul fatto che dove i flussi, dal momento che il orderby succhia l'intera cosa in ogni caso.Vi sono tuttavia momenti in cui vengono utilizzati i vantaggi dello streaming (altre risposte/commenti hanno fornito esempi), pertanto tutti gli operatori LINQ eseguono lo streaming al meglio delle proprie capacità. Ordina stream per quanto può, che sembra non essere molto. Dove i flussi sono molto efficaci.

+0

Ci sono operatori LINQ il cui streaming si trova da qualche parte tra 'Where' e' OrderBy'? –

+0

@ ZevSpitz Suppongo che dipenderà dal modo in cui definisci i tuoi termini. 'SkipWhile' potrebbe qualificarsi, a seconda di come si definisce la quantità di" streaming ". – Servy

+0

[MSDN] (https://msdn.microsoft.com/en-us/library/mt693095.aspx) definisce lo streaming come in grado di analizzare i risultati uno alla volta (ad esempio 'Select'), mentre il non streaming richiede tutti i valori per valutare il primo valore (ad es. 'OrderBy'). Con questa definizione, perché "SkipWhile" dovrebbe essere meno streaming di "Select"? La prima iterazione non richiede tutti i valori. –

Problemi correlati