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, ...}

Paul Sweatte
  • 24,148
  • 7
  • 127
  • 265
Cristian Diaconescu
  • 34,633
  • 32
  • 143
  • 233

4 Answers4

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;
       }
    }
}
Julian
  • 33,915
  • 22
  • 119
  • 174
Ryan Brunner
  • 14,723
  • 1
  • 36
  • 52
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);
James Curran
  • 101,701
  • 37
  • 181
  • 258
  • Is the call to `ToList()` necessary? – Matt Greer Aug 26 '10 at 13:55
  • +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. – Ani Aug 26 '10 at 14:00
  • 2
    @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! – Eric Lippert Aug 26 '10 at 14:12
  • 2
    @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. – Ani Aug 26 '10 at 14:20
  • Correction: That should have been -BCL- team. – Ani Aug 26 '10 at 14:54
  • 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) – James Curran Aug 26 '10 at 15:10
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}
Cristian Diaconescu
  • 34,633
  • 32
  • 143
  • 233
  • I'm Wondering if the using() is useful in this case. – Cristian Diaconescu Aug 26 '10 at 13:52
  • 1
    The using() is necessary - IEnumerator 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. – Ryan Brunner Aug 26 '10 at 14:04
  • 2
    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: http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.reset(v=vs.110).aspx) – Andrea Scarcella May 30 '14 at 10:04
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;
        }
    }
Matt Greer
  • 60,826
  • 17
  • 123
  • 123
  • 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()` – batwad Aug 26 '10 at 14:06