【问题标题】:linq infinite list from given finite list来自给定有限列表的 linq 无限列表
【发布时间】:2011-04-04 06:59:46
【问题描述】:

给定一个有限的元素列表,我如何创建一个(延迟评估,感谢 LINQ!)无限列表,它只是不断迭代我的初始列表?

如果初始列表是{1, 2, 3},我希望新列表返回{1, 2, 3, 1, 2, 3, 1, ...}

【问题讨论】:

    标签: c# .net linq lazy-evaluation infinite-sequence


    【解决方案1】:

    yield return 是一个相当方便的运算符,尽管它并不特别需要 LINQ。

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

    【讨论】:

    • 比我的版本短很多:)
    【解决方案2】:
    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);
    

    【讨论】:

    • 有必要拨打ToList()吗?
    • +1: 2 个问题:1. 这不适用于ienum.Infinite().Infinite(),而从逻辑上讲,Infinite() 实现应该支持这一点。 2. 如果我们忽略第 1 点,则会出现性能问题:枚举器不断被重新创建和处理。将它重写为一个 for 循环会更好,当它到达list.Count 时会重置为0。另一种选择是依赖IEnumerator.Reset(),但我认为这很危险,因为很多实现都不支持它。
    • @Ani:你怎么知道有性能问题?如果没有经验证据,您是否知道创建和销毁列表迭代器(BCL 团队专门设计的一种结构,分配和处理速度非常快)是用户应用程序中最慢的事情?或者您是否进行了大量仔细的分析工作以确定该结构的分配和处置实际上是用户应用程序中最大的性能问题?如果是这样,那么我希望查看该数据,以便将其传递给 BCL 绩效团队,谢谢!
    • @Eric Lippert:我们在这里写了一个实用方法,所以我们不知道用户的应用程序!所以我想我们应该归功于用户让方法尽可能地高效,同样的原因,CLR 团队尽可能快地制作列表迭代器 - 他们不知道什么会变慢用户的应用程序,是吗?当然,首先我没有任何“经验证据表明创建和销毁列表迭代器很慢”,所以我的建议可能有误。
    • 更正:应该是 -BCL- 团队。
    【解决方案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}
    

    【讨论】:

    • 我想知道 using() 在这种情况下是否有用。
    • using() 是必要的 - IEnumerator 实现 IDisposable。对于大多数列表,dispose 可能不会做很多事情,但如果枚举器正在做一些新奇的事情,比如从文件或数据库中读取,你会希望它被释放。
    • 我想知道使用重置是否是最佳选择,因为“重置方法是为 COM 互操作性提供的。它不一定需要实施;相反,实施者可以简单地抛出 NotSupportedException。 (来源:msdn.microsoft.com/en-us/library/…
    【解决方案4】:

    另一种选择,实现IEnumerator&lt;T&gt;

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

    【讨论】:

    • 我更喜欢这个实现,因为它更能描述你真正在做什么:无限地枚举列表。这感觉比无限 IEnumerable 的想法要好得多,并且正如 Ani 提到的,避免了 ienum.Infinite().Infinite() 的大脑爆炸器
    猜你喜欢
    • 1970-01-01
    • 2011-01-07
    • 2016-01-06
    • 2013-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-30
    • 1970-01-01
    相关资源
    最近更新 更多