2013-01-02 6 views
6

Utilizzando il codice di procedere, sono riuscito con successo a produrre un insieme di numeri e shuffle posizionare gli elementi nella matrice:Come fa uso Enumerable.OrderBy keySelector

var randomNumbers = Enumerable.Range(0, 100) 
        .OrderBy(x => Guid.NewGuid()); 

Tutto funziona bene, ma è una specie di gettato un chiave nelle mie opere quando cerco di capire Enumerable.OrderBy. Prendere il seguente codice, ad esempio:

var pupils = new[] 
{ 
    new Person() { Name = "Alex", Age = 17 }, 
    new Person() { Name = "Jack", Age = 21 } 
}; 

var query = pupils.OrderBy(x => x.Age); 

E 'la mia comprensione che sto passando la proprietà Vorrei ordinare in base e presumo che LINQ utilizzerà Comparer<T>.Default per determinare come ordinare la raccolta, se non esplicito IComparer è specificato per il secondo sovraccarico. Non riesco davvero a vedere come si possa applicare una qualsiasi logica ragionevole per mescolare l'array in questo modo. Quindi, in che modo LINQ mi consente di mescolare un array come questo?

+0

Non capisco la tua domanda qui. Nell'esempio sopra, supponendo che l'età sia un int, 'query' conterrà l'elenco ordinato. Quindi, l'array iniziale non è ordinato o mescolato. – ryadavilli

+0

Ho aggiornato il post. Sto parlando del primo esempio di codice. –

+0

Prova Fisher-Yates mischiare nel codice vero però .. Più veloce .. – nawfal

risposta

5

Come fa uso Enumerable.OrderBy keySelector?

Enumerable.OrderBy<T> pigramente restituisce - keySelector non viene chiamato direttamente. Il risultato è un IOrderedEnumerable<T> che eseguirà l'ordine quando enumerato.

Quando enumerato, keySelector viene chiamato una volta per ciascun elemento. L'ordine delle chiavi definisce il nuovo ordine degli elementi.

Ecco un elegante sample implementation.


Così come LINQ fammi mescolo un array come questo?

var randomNumbers = Enumerable 
    .Range(0, 100) 
    .OrderBy(x => Guid.NewGuid()); 

Guid.NewGuid è chiamato per ogni elemento. La chiamata per il secondo elemento può generare un valore superiore o inferiore alla chiamata per il primo elemento.

randomNumbers è un IOrderedEnumerable<int> che produce un ordine diverso ogni volta che viene elencato. KeySelector viene chiamato una volta per elemento ogni volta che viene elencato randomNumbers.

2

Quindi, come fa questo lavoro?

La seguente query utilizza Comparer<Guid>.Default per il confronto.

.OrderBy(x => Guid.NewGuid()) 

Dal momento che ogni GUID generato è praticamente unico (come si sta generando nella clausola OrderBy stesso), si è credere che si stanno ottenendo ordine casuale (che è la comprensione corretta).
Se si esegue nuovamente la query si veda di nuovo (presumibilmente) risultato mescolate come viene generato nuova serie di GUID.

Se si intende utilizzare GUID predefiniti, si vedrà ordine.

Esempio randomNumbers1 e randomNumbers2 hanno stessi valori in seguito.

var randomGuids = Enumerable.Range(0,10).Select (x => Guid.NewGuid()).ToArray(); 

var randomNumbers1 = Enumerable.Range(0, 10).OrderBy(x => randomGuids[x]); 

var randomNumbers2 = Enumerable.Range(0, 10).OrderBy(x => randomGuids[x]); 

che davvero non riesco a vedere come tutto questo logica ragionevole può essere applicato a rimescolare la matrice in questo modo.

È possibile riprodurre in ordine casuale perché non vi è alcun ordine tra gli elementi (GUID nell'esempio). Se utilizzi elementi ordinati, otterrai un output ordinato anziché uno shuffled.

+0

Ora che mi hai dato questo voglio informazioni più approfondite> :) –

+0

@Alex, aggiorna la domanda – Tilak

3

Sei abbastanza vicino per capire come funziona tale rimescolamento .. Nel suo secondo caso

pupils.OrderBy(x => x.Age); 

il Comparer<int>.Default viene utilizzato (i soggetti sono ordinati in base al Age, semplice).

Nel primo caso, Comparer<Guid>.Default viene utilizzato.

Ora come funziona?.

Ogni volta che si esegue Guid.NewGuid() (presumibilmente) viene prodotto un diverso/originale/non duplicato Guid.Ora quando lo fai

var randomNumbers = Enumerable.Range(0, 100).OrderBy(x => Guid.NewGuid()); 

i numeri sono ordinati sulla base dei Guids generati.

Ora, quali sono i numeri di telefono?

Sono interi a 128 bit rappresentati in forma esadecimale. Poiché 2^128 è un numero così grande, le possibilità di generare due Guidi sono molto rare/quasi impossibili. Poiché Guids mostra una sorta di casualità, anche l'ordine sarà casuale.

Come vengono confrontati due Guidi per far rispettare l'ordine?

È possibile confermare in base a un esperimento banale. Fare:

var guids = Enumerable.Range(0, 10).Select((x, i) => 
    { 
     Guid guid = Guid.NewGuid(); 
     return new { Guid = guid, NumberRepresentation = new BigInteger(guid.ToByteArray()), OriginalIndex = i }; 
    }).ToArray(); 

var guidsOrderedByTheirNumberRepresentation = guids.OrderBy(x => x.NumberRepresentation).ToArray(); 
var guidsOrderedAsString = guids.OrderBy(x => x.Guid.ToString()).ToArray(); 

var randomNumbers = Enumerable.Range(0, 10).OrderBy(x => guids[x].Guid).ToArray(); 

//print randomNumbers.SequenceEqual(guidsOrderedByTheirNumberRepresentation.Select(x => x.OriginalIndex)) => false 

//print randomNumbers.SequenceEqual(guidsOrderedAsString.Select(x => x.OriginalIndex)) => true 

Così Comparer<Guid>.Default si basa sulla rappresentazione di stringa del GUID.


parte:

Si dovrebbe usare Fisher-Yates mischiare per la velocità. Può essere

public static IEnumerable<T> Shuffle<T>(this IList<T> lst) 
{ 
    Random rnd = new Random(); 
    for (int i = lst.Count - 1; i >= 0; i--) 
    { 
     int j = rnd.Next(i + 1); 
     yield return lst[j]; 
     lst[j] = lst[i]; 
    } 
} 

O per concisione, potrebbe essere solo (che può essere ancora più veloce di approccio Guid)

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> lst) 
{ 
    Random rnd = new Random(); 
    return lst.OrderBy(x => rnd.Next()); 
}