January 21, 2010

Functional Patterns for Refactoring in C#

Trying to work on a little filler presentation for my brown-bag sessions at work on a couple of new code smells that can be solved thanks to the small dose of functional programming afforded us by LINQ and C# 3.5 and greater.

I’m working from the refactoring example given in Agile Principles, Patterns, and Practices in C# for the Sieve of Eratosthenes. When this book was published LINQ wasn’t around, and in a 1.x world this is Uncle Bob’s final listing for the problem:

public static int[] GeneratePrimeNumbers(int maxValue)
  {
      if(maxValue < 2)
          return new int[0];
      else
      {
          UncrossIntegersUpTo(maxValue);
          CrossOutMultiples();
          PutUncrossedIntegersIntoResult();
          return result;
      }
  }

This is fine. It reads well. I’ll get into the implementation of some of the methods in a second, but here’s my final implementation of the same method:

public static int[] GeneratePrimeNumbers(int maxValue)
  {
    return 
      Primes
        .TakeWhile(prime => prime <= maxValue)
        .ToArray();
  }

When Generating a Sequence Return IEnumerable<T>

I would like to drop the call to ToArray, but then I would break the interface from the original problem. However, whenever you see a sequence being generated, use IEnumerable<T> unless you need the indexing, and use an iterator if you can get away with it. Take my implementation of Primes for example:

public static IEnumerable<int> Primes
  {
    get
    {
      var remaining = Integers.Where(i => i >= 2);
      while (true)
      {
        int prime = remaining.First();
        yield return prime;
        remaining = remaining.Where(num => num % prime != 0);
      }
    }
  }

There’s no need for any result arrays or other collectors. Just generate the sequence and let the caller tell stop pulling values when they’re ready. This is all about deferred execution and lazy evaluation.

Likewise, my implementation of Integers is:

public static IEnumerable<int> Integers
  {
    get
    {
      int count = 0;
      do
      {
        yield return count++;
      } while (true);
    }
  }

Replace Loops with Filters, and Use Aggregates

Something you ended up doing a lot in 1.x code was writing a function like this (again from Uncle Bob’s original example):

private static int NumberOfUncrossedIntegers()
  {
      int count = 0;
      for (int i = 2; i < crossedOut.Length; i++)
      {
          if (NotCrossed(i))
              count++; // bump count
      }
      return count;
  }
  

Basically, this counts the number of elements in the boolean array crossedOut that are false. In the case of this algorithm, we have to skip the first two. In LINQ this becomes:

private static int NumberOfUncrossedIntegers()
  {
    return crossedOut.Skip(2).Where(x => x == false).Count();
  }
The where statement provides a filter and the count provides the counting. I in-lined the NotCrossed function in the lambda.

Summary

I’m not knocking Uncle Bob’s original implementation. I think it’s groovy. I’m just saying that now that LINQ is around it can let us leverage some pretty powerful syntax to refactor our loops and generators.

No comments:

Post a Comment