Functional Programming with C#: Lazy Calculation

For this demonstration, we'll be creating a lazy-calculated prime number generator based on Mike's code.  First, we'll be needing a utility method to generate a steady stream of numbers (we only need odd numbers after 2)

public static IEnumerable<Int32> Counter(Int32 start, Int32 step, Int32 min, Int32 max)
{
    Int32 current = start;

    while (current > min && current < max)
    {
        yield return current;
        current += step;
    }
}

Next, we'll need a method that will tap into our program flow to perform some processing but not inturrupt the output.

public static IEnumerable<T> PassThrough<T>(this IEnumerable<T> items, Action<T> act)
{
    foreach (T item in items)
    {
        act(item);
        yield return item;
    }
}

These two simple methods will allow us to generate all the prime numbers that can be expressed with an Int32 variable.

public static IEnumerable<Int32> GetPrimes()
{
    List<Int32> found = new List<int>();

    return
        new Int32[] { 2 }
        .Concat(Helpers.Counter(3, 2, 0, Int32.MaxValue))
        .Where(i => !found.Exists(j => i % j == 0))
        .PassThrough(k => found.Add(k));
}

Let's examine what is happening in the code.  First, we'll start with the number 2 and then append all the odd numbers from three through Int32.MaxValue. As each of the numbers is generated and passed through the function chain, we'll check to see if it is evenly divisible by any item in the collection of primes we are accumulating.  If not, it is a new prime and we'll add it to our "found" list and pass it through using the iterator in the "PassThrough()" extension method.

The really cool thing is that we only calculate what's needed when it is neede which is what gives us "lazy calculation" of our primes. As a result, when we consume this method and only request the first ten primes, the minimum amount of computations are be performed to get what we have requested.

static void Main(string[] args)
{
    Helpers
        .GetPrimes()
        .Take(10)
        .ForEach(i => Console.WriteLine(i));

    Console.ReadLine();
}

How we consume the GetPrimes() method is key. We have to be aware of how the algoritm works and be careful not to get the wole list unless we absolutely need it and then if we do, we have to be aware of how generating all the primes performs because it could be quite computationally expensive and we may want to do it asynchronously on another thread (which I'll demonstrate in an upcoming article).

Just so you don't have to hunt... Mike's cool LINQ code to generate primes is here.

Until next time,

Happy coding


Similar Articles