2010-08-26 13 views
9

Dato un elenco finito di elementi, come posso creare una lista infinita (ponderata, grazie LINQ!) Che continua semplicemente a scorrere il mio elenco iniziale?lista infinita linq da una lista finita data

Se l'elenco iniziale è {1, 2, 3}, voglio che la nuova lista per tornare {1, 2, 3, 1, 2, 3, 1, ...}

risposta

16

yield return è un operatore abbastanza utile per questo, anche se in realtà non richiede LINQ in particolare.

IEnumerable<int> GetInfiniteSeries(IEnumerable<int> items) { 
    while (true) { 
     foreach (var item in items) { 
      yield return item; 
     } 
    } 
} 
+1

molto più corto della mia versione :) –

6
IEnumerable<T> Infinite(this IEnumerable<T> ienum) 
{ 
    List<T> list = ienum.ToList(); 
    while (true) 
     foreach(var t in list) 
      yield return t; 
} 



foreach(int i in Enumerable.Range(1,3).Infinite()) 
     Console.WriteLine(i); 
+0

è la chiamata a 'ToList()' necessario? –

+0

+1: 2 problemi però: 1. Questo non funzionerà con 'ienum.Infinite(). Infinite()', quando logicamente un'implementazione 'Infinite()' dovrebbe supportare questo. 2. Se trascuriamo il punto 1, c'è un problema di prestazioni: l'enumeratore continua a essere ricreato e smaltito. Sarebbe molto meglio che fosse riscritto come un ciclo for che si reimposta su '0' quando raggiunge' list.Count'. L'altra alternativa è fare affidamento su 'IEnumerator.Reset()', ma suppongo che sia pericoloso poiché molte implementazioni non lo supportano. – Ani

+2

@Ani: come fai a sapere che c'è un problema di prestazioni? Sai senza prove empiriche che creare e distruggere un iteratore di elenchi - una struttura progettata specificamente dal team BCL per essere incredibilmente veloce da allocare e smaltire - è la cosa * più lenta * nell'applicazione dell'utente? Oppure hai svolto un lavoro di profilazione accurato per determinare che l'allocazione e lo smaltimento di questa struttura sono in effetti il ​​problema di prestazioni più grande nell'applicazione dell'utente? Se è così allora mi piacerebbe vedere quei dati in modo che io possa passarlo al team di prestazioni BCL, grazie! –

3

ecco come ho fatto alla fine:

public static IEnumerable<T> AdNauseam<T>(this IEnumerable<T> i_list) 
    { 
     using(var etor = i_list.GetEnumerator()) 
     { 
      while(true) 
      { 
       while(etor.MoveNext()) 
       { 
        yield return etor.Current; 
       } 
       etor.Reset(); 
      } 
     } 
    } 

Usage:

var list = new[] {1, 2, 3} 
var infinite = list.AdNauseam().Take(10); 

Il risultato:

{1, 2, 3, 1, 2, 3, 1, 2, 3, 1} 
+0

Mi chiedo se l'uso di() sia utile in questo caso. –

+1

L'uso() è necessario - IEnumerator implementa IDisposable. Per la maggior parte degli elenchi, il dispose potrebbe non fare molto, ma se l'enumeratore stava facendo qualcosa di nuovo come leggere da un file o da un database, lo si vorrebbe smaltire. –

+2

Mi chiedo se l'utilizzo di Reset sia l'opzione migliore dato che "Il metodo Reset è fornito per l'interoperabilità COM. Non ha necessariamente bisogno di essere implementato; invece, l'implementatore può semplicemente lanciare una NotSupportedException. ' (fonte: http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.reset(v=vs.110).aspx) –

2

Un'altra opzione, attuare IEnumerator<T>:

public class InfiniteEnumerator<T> : IEnumerator<T> 
    { 
     private IList<T> _items; 
     private int _index = -1; 

     public InfiniteEnumerator(IList<T> items) 
     { 
      if (items == null) 
      { 
       throw new ArgumentNullException("items"); 
      } 
      _items = items; 
     } 

     public T Current 
     { 
      get { return _items[_index]; } 
     } 

     public void Dispose() 
     { 

     } 

     object System.Collections.IEnumerator.Current 
     { 
      get { return _items[_index]; } 
     } 

     public bool MoveNext() 
     { 
      if (_items.Count == 0) 
      { 
       return false; 
      } 

      _index = (_index + 1) % _items.Count; 
      return true; 
     } 

     public void Reset() 
     { 
      _index = -1; 
     } 
    } 
+1

Preferisco questa implementazione in quanto è più descrittiva di ciò che stai facendo realmente: enumerare l'elenco indefinitamente. È molto più bello dell'idea di un IEnumerable infinito e, come ha accennato Ani, evita l'esplosione di cervello che è "ienum.Infinite(). Infinite()" – batwad

Problemi correlati