2014-10-31 2 views
7

Sto imparando LINQ utilizzando il 101 LINQ Samples in the MSDN page e mi sono imbattuto in questo codice:Come mappare i parametri lambda in TakeWhile?

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; 

var firstSmallNumbers = numbers.TakeWhile((n, index) => n >= index); 

foreach (var n in firstSmallNumbers) 
{ 
    Console.WriteLine(n); 
} 

Lo scopo di questa funzione è quello di "utilizzare TakeWhile per restituire gli elementi a partire dall'inizio dell'array fino a quando un numero viene colpito che è inferiore alla sua posizione nell'array. "

In che modo esattamente n e index sa quale parametro prendere? (Ad esempio, come fa lo n a sapere che occorrerà 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 e in che modo index sa che farà un incremento di 0, 1, 2, 3 ...)?

+1

Ho cambiato il titolo. Se ciò non significa ciò che intendevi, per favore cambialo. – gunr2171

+0

Sono d'accordo con le tue modifiche, grazie. –

risposta

9

Perché il sovraccarico è definito in questo modo.Da MSDN

public static IEnumerable<TSource> TakeWhile<TSource>(
    this IEnumerable<TSource> source, 
    Func<TSource, int, bool> predicate 
) 

L'argomento predicate è descritto come segue:

Una funzione per testare ogni elemento di origine per una condizione; il secondo parametro della funzione rappresenta l'indice dell'elemento sorgente.

L'argomento TSource è l'elemento e lo int è l'indice. bool è il valore restituito.

quando si scrive (n, index) => ... il n prende il primo parametro (TSource) e index prende il secondo (int).

+1

Quindi stai dicendo che non importa come nomino i parametri? (cioè posso rinominarlo in '(qualcosaElse1, somethingElse2) =>' e funzionerà ancora? –

+3

@CJ Quello è giusto.Si sta definendo un metodo anonimo, i nomi dei parametri sono a tua discrezione. – BradleyDotNET

+3

@CJ Provalo e trova Fuori. Hai già il codice per farlo. – Servy

3

Il primo parametro n è associato al numero sui numeri e il secondo allo index è associato all'indice del numero nella sequenza. In realtà, non importa se li chiami n e index, potresti nominare qualunque cosa. In ogni caso il primo parametro sarebbe associato all'elemento casuale nella sequenza e al secondo parametro con l'indice di questa sequenza.

Come più formalmente Bradley detto sopra la definizione della TakeWhile è il seguente:

public static IEnumerable<TSource> TakeWhile<TSource>(
    this IEnumerable<TSource> source, 
    Func<TSource, int, bool> predicate 
) 

Come si vede da quanto sopra, la TakeWhile è un metodo di estensione definita su tipi che implementa l'interfaccia IEnumerable. Nota ora due cose, i parametri che prendono come input questo metodo e il suo tipo di ritorno.

Restituisce una sequenza di oggetti del tipo di oggetti presenti nella sequenza fornita.

Cosa serve come parametro?

Un predicato. Un predicato è un metodo che accetta alcuni parametri e restituisce true o false. Quali sono i parametri del predicato?

I parametri del predicato sono un elemento TSource e uno int. L'elemento TSource sarebbe l'elemento casuale della sequenza che lo int sarebbe l'indice di questo elemento.

Che cos'è questo (n, index) => n >= index ora?

Questa è un'espressione lambda, che si comporta come un predicato.

Specifficaly, date le variabili chiamate n e index, restituisce true se n>=index, altrimenti restituisce false. Fornendo questa espressione al metodo TakeWhile estensione è come passare c'è un predicato Func<TSource, int, bool>. Quindi ottieni quello che vuoi.

+0

Potrebbe piacere che il downvoter mi spieghi dove mi sbaglio. Grazie in anticipo. – Christos

+0

Non è il downvoter, ma è venuto in una revisione molto precedente della tua risposta. Immagino che non abbiano guardato questa (molto meglio) versione. – BradleyDotNET

+0

@BradleyDotNET ti ringrazio molto per il tuo commento. – Christos

4

In un'espressione lambda, tutto prima di => sono parametri del metodo. A titolo di esempio, l'espressione (n, index) => n >= index lambda può essere riscritta come un metodo simile a questo:

public bool CheckIfValueIsGreaterOrEqualToIndex(int value, int index) 
{ 
    if(value >= index) 
    { 
     return true; 
    } 
    else 
    { 
     return false; 
    } 
} 

Quindi, utilizzando questo metodo, è possibile specificare qualsiasi nome che si desidera i parametri (in questo caso ho usato value invece di n). E invece di lambda si potrebbe usare quel metodo qui:

numbers.TakeWhile(CheckIfValueIsGreaterOrEqualToIndex); 
2

Il metodo migliore sarebbe quello di provare e mettere in atto il proprio semplificata TakeWhile metodo:

public static List<int> MyTakeWhile(this List<int> input, Func<int, int, bool> predicate) 
{ 
    var result = new List<int>(); 
    for (var i = 0; i < input.Count; i++) 
    { 
     if (predicate(input[i], i)) 
      result.Add(input[i]); 
     else 
      break; 
    }; 
    return result; 
} 

Si potrebbe usare un yield return nella vita reale, e restituire uno IEnumerable e, naturalmente, utilizzare i generici anziché int. Ma l'idea è la stessa.

Se si desidera verificare che:

var numbers = new List<int> { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; 
var result = numbers.MyTakeWhile((n, index) => n >= index); 
result.ForEach(Console.WriteLine); 

Dovrebbe dare {5, 4} che è esattamente lo stesso come l'originale TakeWhile.

+0

Anche se questo è un modo ragionevole per imparare, in realtà non aiuta a insegnare all'OP come funzionano le espressioni lambda. – BradleyDotNET