2010-08-26 10 views
9

Wie kann ich eine endliche Liste von Elementen erstellen, wie ich eine (lazily-evaluated, danke LINQ!) Unendliche Liste erstellen kann, die nur über meine ursprüngliche Liste iteriert?Linq unendliche Liste von gegebenen endlichen Liste

Wenn die erste Liste {1, 2, 3} ist, möchte ich die neue Liste zurückzukehren {1, 2, 3, 1, 2, 3, 1, ...}

Antwort

16

yield return ist ein ziemlich handlicher Operator für dies, obwohl es LINQ spezifisch nicht wirklich erfordert.

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

viel kürzer als meine Version :) –

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

Ist der Aufruf von 'ToList()' notwendig? –

+0

+1: 2 Probleme obwohl: 1. Dies funktioniert nicht mit 'ienum.Infinite(). Infinite()', wenn logisch eine 'Infinite()' Implementierung dies unterstützen sollte. 2. Wenn wir Punkt 1 vernachlässigen, gibt es ein Leistungsproblem: der Enumerator wird immer neu erstellt und entsorgt. Es wäre viel besser, wenn es als For-Schleife neu geschrieben würde, die auf "0" zurückgesetzt wird, wenn sie auf "list.Count" trifft. Die andere Alternative ist, sich auf IEnumerator.Reset() 'zu verlassen, aber ich nehme an, dass es gefährlich ist, da so viele Implementierungen es nicht unterstützen. – Ani

+2

@Ani: Woher wissen Sie, dass es ein Leistungsproblem gibt? Weißt du, ohne empirische Beweise, dass das Erstellen und Zerstören eines Listen-Iterators - eine Struktur, die speziell vom BCL-Team entworfen wurde, um unglaublich schnell zu verteilen und zu disponieren - das * langsamste * in der Anwendung des Benutzers ist? Oder haben Sie umfangreiche sorgfältige Profilerstellung durchgeführt, um festzustellen, dass die Zuweisung und Beseitigung dieser Struktur tatsächlich das größte Leistungsproblem in der Anwendung des Benutzers darstellt? Wenn ja, dann möchte ich diese Daten sehen, damit ich sie an das BCL-Leistungsteam weitergeben kann, danke! –

3

hier, wie ich es habe schließlich getan:

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(); 
      } 
     } 
    } 

Verbrauch:

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

Das Ergebnis:

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

Ich frage mich, ob using() in diesem Fall nützlich ist. –

+1

Das using() ist notwendig - IEnumerator implementiert IDisposable. Bei den meisten Listen kann das Dispose nicht viel bewirken, aber wenn der Enumerator etwas neuartiges wie das Lesen aus einer Datei oder einer Datenbank tut, möchten Sie, dass es entsorgt wird. –

+2

Ich frage mich, ob die Verwendung von Zurücksetzen die beste Option ist, wenn 'Die Methode Zurücksetzen' für die COM-Interoperabilität bereitgestellt wird. Es muss nicht unbedingt implementiert werden; Stattdessen kann der Implementierer einfach eine NotSupportedException auslösen. (Quelle: http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.reset(v=vs.110).aspx) –

2

Eine weitere Option, implementieren 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

Ich bevorzuge diese Implementierung, da es beschreibender für das ist, was Sie wirklich tun: die Liste auf unbestimmte Zeit aufzählen. Es fühlt sich viel schöner an als die Idee eines unendlichen IEnumerable, und wie Ani erwähnte, vermeidet der Gehirn-Exploder, der "ienum.Infinite(). Infinite()" ist – batwad