2010-08-26 58 views
9

給定一個有限的元素列表,我如何創建一個(懶惰評估,感謝LINQ!)無限列表,只是不斷迭代我的初始列表?linq無限列表從給定的有限列表

如果最初的名單是{1, 2, 3},我希望新的列表返回{1, 2, 3, 1, 2, 3, 1, ...}

回答

16

yield return是一個相當方便的操作符,雖然它並不真正需要特定的LINQ。

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

比我的版本短很多:) – 2010-08-26 13:59:57

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

是調用'ToList()'有必要嗎? – 2010-08-26 13:55:20

+0

+1: 雖然有兩個問題: 1.當無條件()'在邏輯上是'Infinite()'實現應該支持這個時,這不會與'ienum.Infinite()。 2.如果我們忽略了第1點,就會出現性能問題:枚舉器不斷重新創建和處置。它會更好地被重新定義爲一個for循環,當它碰到list.Count時它會重置爲0。另一種方法是依靠'IEnumerator.Reset()',但我認爲這是很危險的,因爲很多實現不支持它。 – Ani 2010-08-26 14:00:14

+2

@Ani:你怎麼知道有一個性能問題?你知道嗎?沒有經驗證據表明,創建和銷燬列表迭代器是BCL團隊專門設計的一個結構,它非常快速地分配和處理 - 是用戶應用程序中最慢的東西。還是你做了大量仔細的分析工作,以確定這個結構的分配和處理實際上是用戶應用程序中最大的性能問題?如果是這樣,那麼我希望看到這些數據,以便我可以將它傳遞給BCL性能團隊,謝謝! – 2010-08-26 14:12:20

3

下面是我最終做到了:

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

用法:

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

結果:

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

我想知道在這種情況下using()是否有用。 – 2010-08-26 13:52:03

+1

使用()是必要的 - IEnumerator 實現IDisposable。對於大多數列表來說,配置可能沒有太大的作用,但是如果枚舉器做了一些新穎的事情,比如從文件或數據庫中讀取數據,你會希望它被丟棄。 – 2010-08-26 14:04:19

+2

我想知道使用重置是最好的選擇,因爲'重置方法提供了COM互操作性。它不一定需要實施;相反,實現者可以簡單地拋出一個NotSupportedException異常。' (來源:http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.reset(v=vs.110)。aspx) – 2014-05-30 10:04:01

2

另一種選擇,實現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

我更喜歡這個實現,因爲它更加描述了你真正在做什麼:無限列舉列表。它感覺比無限IEnumerable的想法好得多,正如Ani提到的,​​它避免了大腦爆炸,即'ienum.Infinite()。Infinite()' – batwad 2010-08-26 14:06:37