#1,197 – Iterator Can Generate Enumerable of Infinite Length

An iterator can be written to generate an enumerable sequence of infinite length.  The iterator will execute for as long as client code continues to request the next element in the sequence.

Below is a simple iterator that generates prime numbers.  Note that we can still use the iterator, although its implementation includes an infinite loop.  The loop will execute only as many times as needed.

        static void Main(string[] args)
        {
            Console.Write("Enter limit:");
            int limit = int.Parse(Console.ReadLine());

            foreach (int p in AllPrimes())
            {
                if (p > limit)
                    break;
                Console.WriteLine(p);
            }

            Console.ReadLine();
        }

        private static IEnumerable<int> AllPrimes()
        {
            List<int> primesSoFar = new List<int>();
            primesSoFar.Add(2);
            yield return 2;
            primesSoFar.Add(3);
            yield return 3;

            int testPrime = 5;
            while (true)
            {
                bool isPrime = true;
                foreach (int n in primesSoFar)
                {
                    if (testPrime % n == 0)
                    {
                        isPrime = false;
                        break;
                    }
                }

                if (isPrime)
                {
                    primesSoFar.Add(testPrime);
                    yield return testPrime;
                }

                testPrime += 2;
            }
        }

1197-001

Advertisements

#1,196 – Using Fluent-Style Syntax when Chaining Iterators

You can chain iterator code together using a fluent-style syntax if you define extension methods for the corresponding IEnumerable<T> type that you’re using.  In the code below, we chain several iterators together, progressively filtering an IEnumerable<Dog> collection.

    class Program
    {
        static void Main(string[] args)
        {
            foreach (Dog d in AllMyDogs().YoungDogs().HerdingDogs())
            {
                Console.WriteLine(d);
                if (d.Breed == Breed.JackRussell)
                    break;
            }

            Console.ReadLine();
        }

        private static IEnumerable<Dog> AllMyDogs()
        {
            yield return new Dog("Kirby", Breed.BorderCollie, 14);
            yield return new Dog("Jack", Breed.JackRussell, 15);
            yield return new Dog("Ruby", Breed.Mutt, 4);
            yield return new Dog("Lassie", Breed.Collie, 19);
            yield return new Dog("Shep", Breed.Collie, 2);
            yield return new Dog("Foofoo", Breed.Sheltie, 8);
            yield return new Dog("Pongo", Breed.Dalmatian, 4);
            yield return new Dog("Rooster", Breed.WestHighlandTerrier, 1);
        }
    }

    static class DogFilters
    {
        public static IEnumerable<Dog> YoungDogs(this IEnumerable<Dog> dogs)
        {
            foreach (Dog d in dogs)
                if (d.Age < 10)
                    yield return d;
        }

        public static IEnumerable<Dog> HerdingDogs(this IEnumerable<Dog> dogs)
        {
            foreach (Dog d in dogs)
                if ((d.Breed == Breed.BorderCollie) ||
                    (d.Breed == Breed.Collie) ||
                    (d.Breed == Breed.Sheltie))
                    yield return d;
        }
    }

Here’s the output:
1196-001

#1,195 – Iterator Produces Only as Many Elements as Are Needed

When implementing an iterator, you write code that generates a sequence of elements using the yield return statement to return each consecutive element.

The full body of the iterator code may not execute.  Only the code required to return the elements iterated upon is executed.  In the example below, we iterate over a sequence produced by an iterator, using foreach.

Executing this code, only a portion of the AllMyDogs() method is executed, since we exit early out of  the foreach loop.

            static void Main(string[] args)
            {
                foreach (Dog d in AllMyDogs())
                {
                    Console.WriteLine(d);
                    if (d.Breed == Breed.JackRussell)
                        break;
                }

                Console.ReadLine();
            }

            private static IEnumerable<Dog> AllMyDogs()
            {
                Console.WriteLine("* returning Kirby");
                yield return new Dog("Kirby", Breed.BorderCollie, 14);
                Console.WriteLine("* returning Jack");
                yield return new Dog("Jack", Breed.JackRussell, 15);
                Console.WriteLine("* returning Ruby");
                yield return new Dog("Ruby", Breed.Mutt, 4);
                Console.WriteLine("* returning Lassie");
                yield return new Dog("Lassie", Breed.Collie, 12);
                Console.WriteLine("* returning Foofoo");
                yield return new Dog("Foofoo", Breed.Sheltie, 8);
            }

1195-001

#1,194 – Chaining Iterators Together

An iterator can work on an IEnumerable<T>, using one sequence as input and generating a second sequence from the first.

In the example below, we use the output of one iterator as the input for another iterator.

        static void Main(string[] args)
        {
            IEnumerable<Dog> dogs1 = AllMyDogs();

            Console.WriteLine("============");
            foreach (Dog d in dogs1)
                Console.WriteLine(d);

            IEnumerable<Dog> dogs2 = YoungDogs(dogs1);

            Console.WriteLine("============");
            foreach (Dog d in dogs2)
                Console.WriteLine(d);

            IEnumerable<Breed> breeds = BreedsByNamePattern(dogs2, "oo");

            Console.WriteLine("============");
            foreach (Breed b in breeds)
                Console.WriteLine(b);

            Console.ReadLine();
        }

        private static IEnumerable<Dog> AllMyDogs()
        {
            yield return new Dog("Kirby", Breed.BorderCollie, 14);
            yield return new Dog("Jack", Breed.JackRussell, 15);
            yield return new Dog("Ruby", Breed.Mutt, 4);
            yield return new Dog("Lassie", Breed.Collie, 12);
            yield return new Dog("Foofoo", Breed.Sheltie, 8);
            yield return new Dog("Pongo", Breed.Dalmatian, 4);
            yield return new Dog("Rooster", Breed.WestHighlandTerrier, 1);
        }

        private static IEnumerable<Dog> YoungDogs(IEnumerable<Dog> dogs)
        {
            foreach (Dog d in dogs)
                if (d.Age < 10)
                    yield return d;
        }

        private static IEnumerable<Breed> BreedsByNamePattern(IEnumerable<Dog> dogs, string pattern)
        {
            foreach (Dog d in dogs)
                if (d.Name.Contains(pattern))
                    yield return d.Breed;
        }

1194-001

#1,193 – yield Statement and try/catch Blocks

The yield return statement is used when defining an iterator to generate the next element within a sequence (IEnumerable).  You cannot include a yield return statement in any of the following places:

  • within a try block that has a catch clause
  • within a catch block
  • within a finally block

You can include a yield return statement within a try block that only has a finally block.

        private static IEnumerable<int> IntsAndTheirDoubles()
        {
            for (int i = 1; i <= 5; i++ )
            {
                yield return i;
                try
                {
                    // Error: Cannot yield a value in the body of a try block
                    //   with a catch clause
                    yield return 2 * i;
                }
                catch (Exception xx)
                {
                    Console.WriteLine("Uh-oh");
                }
            }
        }

#565 – Using an Iterator to Return A Shuffled Sequence

The System.Linq namespace includes an OrderBy method that makes it easy to reorder an enumerable sequence, based on a particular key.  For a key, we can use a newly generated Guid to achieve a random order.  (See Jeff Atwood’s post on Shuffling).

We can encapsulate this shuffling behavior in a method that returns the resulting shuffled sequence as an IEnumerable. We’ll use an iterator in the body of this method to return the shuffled sequence of elements.

        // Two line shuffle
        static IEnumerable<int> Shuffle(List<int> theList)
        {
            foreach (int next in theList.OrderBy(x => Guid.NewGuid()))
                yield return next;
        }

You can now use this method to iterate through the original sequence in a random order.  You can also view this as moving through a newly shuffled sequence.  Note that the original list is not re-ordered.

            List<int> myList = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

            foreach (int i in Shuffle(myList))
                Console.WriteLine(i);

#564 – Use the Reverse Method to Iterate Backwards through a Collection

You can use the Enumerable<TSource>.Reverse method on any enumerable object, to iterate backwards through its collection.

Because arrays and collections implement the IEnumerable interface, you can use a foreach statement to enumerate through their elements in a forward-only fashion.

            int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

            foreach (int i in nums)
                Console.WriteLine(i);

The default enumerator implemented in types like System.Array and List<T>, however, only allows you to iterate forwards through a collection.  If you instead want to iterate backwards through an array or collection, you can use the Reverse method mentioned above. This method is part of System.Linq and is an extension method that works on any IEnumerable<T> type.

            int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

            foreach (int j in nums.Reverse())
                Console.WriteLine(j);

            List<Dog> myDogs = new List<Dog>();
            myDogs.Add(new Dog("Jack", 17));
            myDogs.Add(new Dog("Kirby", 15));
            myDogs.Add(new Dog("Ruby", 1));

            foreach (Dog d in myDogs.Reverse<Dog>())
                Console.WriteLine(d.Name);