linq infinite list from given finite list
Asked Answered
G

4

10

Given a finite list of elements, how can I create a (lazily-evaluated, thanks LINQ!) infinite list that just keeps iterating over my initial list?

If the initial list is {1, 2, 3}, I want the new list to return {1, 2, 3, 1, 2, 3, 1, ...}

Garner answered 26/8, 2010 at 13:46 Comment(0)
F
18

yield return is a fairly handy operator for this, although it doesn't really require LINQ specifically.

IEnumerable<int> GetInfiniteSeries(IEnumerable<int> items) {
    while (true) {
       foreach (var item in items) { 
           yield return item;
       }
    }
}
Fedora answered 26/8, 2010 at 13:51 Comment(1)
a lot shorter than my version :)Garner
C
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);
Comedic answered 26/8, 2010 at 13:49 Comment(6)
Is the call to ToList() necessary?Jaunty
+1: 2 issues though: 1. This will not work with ienum.Infinite().Infinite(), when logically an Infinite() implementation should support this. 2. If we neglect point 1, there is a performance issue: the enumerator keeps getting recreated and disposed. It would be much better it were rewitten as a for-loop that resets to 0 when it hits list.Count. The other alternative is to rely on IEnumerator.Reset(), but I suppose it is dangerous since so many implementations don't support it.Offstage
@Ani: How do you know there is a performance issue? Do you know without empirical evidence that creating and destroying a list iterator - a struct specifically designed by the BCL team to be incredibly fast to allocate and dispose - is the slowest thing in the user's application? Or did you do extensive careful profiling work to determine that allocation and disposal of this struct is in fact the biggest performance issue in the user's application? If so then I would like to see that data so that I can pass it on to the BCL performance team, thanks!Epithelium
@Eric Lippert: We're writing a utility method here, so we don't know the user's application! So I suppose we owe to it to the user to make the method as performant as we can, for the same reason that the CLR team makes list iterators as fast as -they- can - they don't know what's going to be slow in the user's app, do they? Of course, I don't have any 'empirical evidence that creating and destroying a list iterator is slow' in the first place, so I might be wrong with my recommendation.Offstage
Correction: That should have been -BCL- team.Offstage
My purpose of the ToList() was to avoid calling Reset. (I had a brian-fart, and thought the method Ryan uses in his answer would call Reset instead of repeatedly calling GetEnumerator)Comedic
G
3

Here's how I've done it eventually:

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

The result:

{1, 2, 3, 1, 2, 3, 1, 2, 3, 1}
Garner answered 26/8, 2010 at 13:50 Comment(3)
I'm Wondering if the using() is useful in this case.Garner
The using() is necessary - IEnumerator<T> implements IDisposable. For most lists, the dispose may not do much, but if the enumerator was doing something novel like reading from a file or a database, you would want it to be disposed.Fedora
I wonder whether using Reset is the best option given that 'The Reset method is provided for COM interoperability. It does not necessarily need to be implemented; instead, the implementer can simply throw a NotSupportedException.' (source: msdn.microsoft.com/en-us/library/…)Contagious
J
3

Another option, implement 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;
        }
    }
Jaunty answered 26/8, 2010 at 13:54 Comment(1)
I prefer this implementation as it's more descriptive of what you're really doing: enumerating the list indefinitely. It feels much nicer than the idea of an infinite IEnumerable, and as Ani mentioned, avoids the brain-exploder that is ienum.Infinite().Infinite()Paries

© 2022 - 2024 — McMap. All rights reserved.