Itertools for C# – Cycle and Zip

Continuing our implementation of Python iterator functions in C#, we’ll implement Cycle and Zip this time.

Cycle

IEnumerable Cycle(IEnumerable iterable)

Cycle keeps cycling through the enumerator, that is, it cycles back to the first element when the enumerator is exhausted. You can use it like

IEnumerable<int> source = new List<int>() { 1, 2, 3};
foreach(int element in Itertools.Cycle(source))
{
   Console.WriteLine(element); // prints 1 2 3 1 2 3..
}

The implementation is again very straightforward – all we have to do is wrap the foreach over the iterable in an infinite loop. The outer loop while loop creates a new enumerator for every iteration, as foreach calls GetEnumerator() before looping over the returned enumerator.


public static IEnumerable<T> Cycle<T>(IEnumerable<T> iterable)
{
   while (true)
   {
      foreach (T t in iterable)
      {
          yield return t;
      }
   }
}


The Python cycle function has a slightly different behavior – it caches elements in the iterable the first time around and then iterates over the cache after that. This means that changes to the collection will not be visible to the users of the cycle function, but only if the changes happen after cycle starts returning elements from its cache.

Zip

IEnumerable<T[]> Zip<T>(params IEnumerable<T>[] iterables)

Zip returns an enumerable array of elements, with each array containing one element per input iterator. The first array contains the first element of every iterator, the second array has every second element and so on.

IEnumerable<int> e1 = new List<int>() { 1, 2, 3};
IEnumerable<int> e2 = new List<int>() { 4, 5, 6};
IEnumerable<int[]> zippedValues = Itertools.Zip(e1, e2);
foreach(int[] arr in zippedValues)
{
   Console.WriteLine("{");
   foreach(int val in arr)
       Console.WriteLine(val);
   Console.WriteLine("}");
}


This prints {1,4}, {2,5}, {3,6}

The implementation is slightly more complex.


public static IEnumerable<T[]> Zip<T>(params IEnumerable<T>[] iterables)
{
   IEnumerator<T>[] enumerators = Array.ConvertAll(iterables, (iterable) => iterable.GetEnumerator());

    while (true)
    {
       int index = 0;
       T[] values = new T[enumerators.Length];

       foreach (IEnumerator<T> enumerator in enumerators)
       {
           if (!enumerator.MoveNext())
              yield break;

            values[index++] = enumerator.Current;
       }

       yield return values;
    }
}

The code gets enumerators for all the iterables, moves all enumerators forward, accumulates their current values into an array and yields the array. It does this until any one of the enumerators runs out of elements.

Next time, we’ll look at Tee – an interesting function that also proved to be a little tricky to implement.

Itertools for C#

If you are a C# developer and haven’t taken a look at the itertools python module so far, you owe it to yourself to learn about it. It’s a module with functions that operate on what Python calls “iterables” (IEnumerable in C#). Go ahead, take a look at it before reading on.

This got me interested about implementing similar functions in C#, using IEnumerable<T>. I fired up VS 2008 and started cranking out the code, only to find that most of the itertools functions are already implemented as extension methods on IEnumerable<T> in .NET 3.5. Some of the functions even have the same name, like Repeat and TakeWhile. I found four functions that weren’t directly available as IEnumerable<T> methods and decided to implement them. Let’s take a look at them, one by one.

Chain

IEnumerable<T> Chain<T>(params IEnumerable<T>[] iterables)

Chain returns one flat view of all the enumerators chained together. IEnumerable<T>.Concat does the same thing, but you’ll have to call it multiple times to chain more than one iterator. Chain can be used to do things like


IEnumerable<int> e1 = new List<int>() { 1, 2, 3};
IEnumerable<int> e2 = new List<int>() { 4, 5, 6};
IEnumerable<int> e3 = new List<int>() { 7, 8, 9};

IEnumerable<int> chained = Itertools.Chain(e1, e2, e3);

foreach(int element in chained)
{
Console.WriteLine(element); // prints 1 2 3 4 5 6 7 8 9
}

The implementation is fairly straightforward, thanks to the incredibly useful yield statement.

public static IEnumerable<T> Chain<T>(params IEnumerable<T>[] iterables)
{
   foreach(IEnumerable<T> iterable in iterables)
   {
      foreach (T t in iterable)
      {
         yield return t;
      }
   }
}


Next up is the Cycle method. We’ll see how it works in the next blog post.