tags:

views:

84

answers:

4

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

+6  A: 
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
Is the call to `ToList()` necessary?
Matt Greer
+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
@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
@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
Correction: That should have been -BCL- team.
Ani
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
+3  A: 

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}
Cristi Diaconescu
I'm Wondering if the using() is useful in this case.
Cristi Diaconescu
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.
Ryan Brunner
+7  A: 

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

IEnumerable<int> GetInfiniteSeries(IEnumerable<int> initialList>) {
    while (true) {
       foreach (var item in initialList) { 
           yield return item;
       }
    }
}
Ryan Brunner
a lot shorter than my version :)
Cristi Diaconescu
+2  A: 

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