2010-04-29 10 views
32

Provo a scrivere una istruzione LINQ che restituisce tutte le possibili combinazioni di numeri (ho bisogno di questo per un test e sono stato ispirato da questo article of Eric Lippert). il prototipo del metodo che io chiamo assomiglia:Generare sequenze di numeri con LINQ

IEnumerable<Collection<int>> AllSequences(int start, int end, int size); 

Le regole sono:

  • tutti tornati collezioni hanno una lunghezza di size
  • valori numerici all'interno di una collezione sono di aumentare
  • ogni numero compreso tra start e end devono essere utilizzati

Quindi chiamando il AllSequences(1, 5, 3) dovrebbe risultare in 10 collezioni, ognuno di dimensioni 3:

1 2 3 
1 2 4 
1 2 5 
1 3 4 
1 3 5 
1 4 5 
2 3 4 
2 3 5 
2 4 5 
3 4 5 

Ora, in qualche modo mi piacerebbe davvero vedere un pura LINQ soluzione. Sono in grado di scrivere una soluzione LINQ non per conto mio, quindi per favore non fare uno sforzo in una soluzione senza LINQ.
miei tentativi finora si è conclusa in un punto in cui devo partecipare a un numero con il risultato di una chiamata ricorsiva del mio metodo - qualcosa come:

return from i in Enumerable.Range(start, end - size + 1) 
     select BuildCollection(i, AllSequences(i, end, size -1)); 

ma non riesco a gestirlo per implementare BuildCollection() su un LINQ base - o anche saltare questa chiamata di metodo. Puoi aiutarmi qui?

+0

@Noldorin, @Fede: Grazie per le grandi risposte - ho sicuramente dare un'occhiata più da vicino i metodi di 'Enumerable' (come' Repeat() 'o' Concat() ') – tanascius

risposta

32

Pensa che ce l'ho.

IEnumerable<List<int>> AllSequences(int start, int end, int size) 
{ 
    if (size == 0) 
     return Enumerable.Repeat<List<int>>(new List<int>(), 1); 

    return from i in Enumerable.Range(start, end - size - start + 2) 
      from seq in AllSequences(i + 1, end, size - 1) 
      select new List<int>{i}.Concat(seq).ToList(); 
} 
+0

'if (size == 0) restituisce new int [] {} .ToList()'? – Spook

13

Qualcosa come il seguente dovrebbe fare il lavoro, penso.

public static IEnumerable<IEnumerable<int>> AllSequences(int start, int end, 
    int size) 
{ 
    return size <= 0 ? new[] { new int[0] } : 
      from i in Enumerable.Range(start, end - size - start + 2) 
      from seq in AllSequences(i + 1, end, size - 1) 
      select Enumerable.Concat(new[] { i }, seq); 
} 

La chiave per la soluzione è la compound from clause, che è piuttosto comodo per trattare con enumerables nidificate.

Si noti che ho modificato leggermente la firma del metodo su IEnumerable<IEnumerable<int>>, poiché questo è più comodo quando si utilizza LINQ (puro). Puoi comunque convertirlo facilmente in un IEnumerable<ICollection<int>> alla fine, se vuoi, comunque.

Fammi sapere se il codice necessita di qualche spiegazione, ma spero che la sintassi LINQ lo renda ragionevolmente chiaro.

Modifica 1: Corretto errore e miglioramento della concisione.

Edit 2: Perché mi annoio e non hanno nulla di meglio da fare (no, non proprio), ho pensato di scrivere un metodo di estensione che calcola le combinazioni di una data lista di elementi, avvalendosi del metodo AllSequences.

public static IEnumerable<IEnumerable<T>> Combinations<T>(this IList<T> source, 
    int num) 
{ 
    return AllSequences(0, source.Count - 1, num).Select(
     seq => seq.Select(i => source[i])); 
} 

Forse non è il modo più efficiente di calcolare le combinazioni, ma è sicuramente un codice piuttosto compatto!

+3

Hai rubato il mio tuono! Stavo solo lavorando fuori questo = D +1 – Tejs

+1

Ho appena eseguito e provoca uno stackoverflow :-(, forse con una dimensione dove> 0, poco prima del secondo da? –

+1

Deve essere 'da seq in AllSequences (i +1, fine, size-1) ' Oltre a questo, bello! Ne ho appena scritto uno, ma il tuo è molto più conciso +1 – tzaman

41
Enumerable.Range(1, 12) 
      .Select(x => (x - 1) + 1); 
+2

non ha risposto alla domanda, ma mi è stato utile, quindi +1 – Kell

+1

Che cosa fa? Il '.Select' è ridondante? –

+0

@MateenUlhaq Sì, sembra non essere necessario. –

Problemi correlati