Over-solving FizzBuzz

A FizzBuzz problem is a quick litmus test designed to identify incompetent developers. These tiny coding exercises are designed to be easy and fast to implement using fundamental programming know-how. If you have a good grasp of programming, you should be able to solve these problems in a few minutes without much hassle.

Imran Ghory initially coined the term and came up with the most well known puzzle:

Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz”.

 

The run of the mill correct solution is something like:

 

   1: static void FizzBuzzClassic()
   2: {
   3:
   4:     for (int x = 1; x <= 100; x++)
   5:     {
   6:
   7:         string WorkingLineContents = string.Empty;
   8:
   9:         if (x % 5 != 0 && x % 3 != 0)
  10:         {
  11:             //Neither a multiple of 3 or 5
  12:             WorkingLineContents = x.ToString();
  13:         }
  14:         else
  15:         {
  16:             //Multiple of three ("Fizz"), five ("Buzz"), or both ("FizzBuzz")
  17:             WorkingLineContents = (x % 3 == 0) ? "Fizz" : string.Empty;
  18:             WorkingLineContents += (x % 5 == 0) ? "Buzz" : string.Empty;
  19:         }
  20:
  21:         Console.WriteLine(WorkingLineContents);
  22:
  23:     }
  24:
  25:     Console.ReadKey();
  26:
  27: }

If you toss readability to the wind and want to scrawl out a nearly-one-line* solution by syntactically abusing the ternary operator, it would look something like this:

   1: static void FizzBuzzLessReadable()
   2: {
   3:
   4:     Enumerable.Range(1,100).ToList().ForEach(
   5:                 n => Console.WriteLine(
   6:                     (n%3==0) ? (n%5==0) ? "FizzBuzz" : "Fizz"
   7:                          : (n%5==0) ? "Buzz" : n.ToString()
   8:             ));
   9:
  10:     Console.ReadKey();
  11:
  12: }

(*One semicolon at least. Also: replace Enumerable.Range & Lambda with a ‘for’ loop if you can’t use Framework 3.5)

 

Yes, it’s depressing that there are still developers in our trade that are unable to create a working for loop in under ten minutes. However, this is an easy topic and has already been beaten to death again and again.

Instead of discussing developer competence, I wanted to take the simple problem and enumerate through interesting ways of solving it that do not involve a for loop with hard coded logic. These solutions illustrate interesting patterns that can be used to solve other more difficult problems.

I hacked together a few fun answers to the problem in my free time that leverage the LINQ extension methods to create something that is more dynamic. I humbly present to you two horribly overly engineered FizzBuzz solutions:

 

Solution 1 – Combined Dynamically Generated Lists

The correct output involves four constituent parts: Fizz, Buzz, FizzBuzz, and the number. If you think carefully, you could imagine the problem as four parallel lists layered on top of each other. Something like:

list-graph

Each list is responsible for providing the correct value at each index. The solution to the problem is just all four lists flattened (combined) into one. I put together a generic extension method that does just that:

   1: private static IEnumerable<T> JoinByIndex<T>(this List<IEnumerable<T>> Lists)
   2: {
   3:
   4:     if (Lists == null)
   5:         throw new ArgumentNullException("Lists", "Lists is null.");
   6:
   7:     var Cache = new List<IEnumerator<T>>();
   8:     Lists.ForEach(l => Cache.Add(l.GetEnumerator()));
   9:
  10:     while (Cache.All(e => e.MoveNext() == true) == true)
  11:     {
  12:         yield return Cache.First(e => e.Current != null).Current;
  13:     }
  14:
  15: }

All this does is squish a set of lists into one resulting master list. The value for each index of the resulting list is the first non-null value found at the same index in the source lists (all of the bold values in the table above).

Continuing to work backwards, the missing ingredient is now the source lists. We could have flat lists that have been pre-calculated up to a certain point, but that wouldn’t be very dynamic, plus it would be a lot of extra typing. Instead, I built a (generic) dynamic list generator that takes in an algorithm to stipulate the list value at each point, and uses the iterator block pattern to have the values calculated on demand. It’ll work up to int.MaxValue(2,147,483,647) but you could easily extend that if needed by switching to unsigned integers or another data type.

Here is the dynamic list implementation:

   1: static IEnumerable<T> DynamicList<T>(Func<int, T> Generator)
   2: {
   3:
   4:     if (Generator == null)
   5:         throw new ArgumentNullException("Generator", "Generator is null.");
   6:
   7:     for (int x = 1; x <= int.MaxValue; x++)
   8:     {
   9:         yield return Generator(x);
  10:     }
  11:
  12: }

 

To create the dynamic lists, all we need to do is feed in the algorithm that dictates what the list value should be for each index. Example:

   1: IEnumerable<string> FizzList =
   2:                 DynamicList<string>((n) => (n % 3 == 0 && n % 5 != 0) ? "Fizz" : null);

(“For each index divisible by three, but not divisible by five, the value is Fizz”)

 

Now we have all of the tools we need to build our dynamic list solution. All that’s needed is to create the four dynamic lists, combine them using JoinByIndex(), Take the first 100 results, generate the list, and output each item to the console. Here’s what that looks like:

   1: static void FizzBuzzIEnumerables()
   2: {
   3:
   4:     (new List<IEnumerable<string>>
   5:         {
   6:             DynamicList((n) => (n % 3 == 0 && n % 5 != 0) ? "Fizz" : null),
   7:             DynamicList((n) => (n % 5 == 0 && n % 3 != 0) ? "Buzz" : null),
   8:             DynamicList((n) => (n % 5 == 0 && n % 3 == 0) ? "FizzBuzz" : null),
   9:             DynamicList((n) => (n % 5 != 0 && n % 3 != 0) ? n.ToString() : null)
  10:         })
  11:             .JoinByIndex()
  12:             .Take(100)
  13:             .ToList()
  14:             .ForEach(
  15:                 s => Console.WriteLine(s)
  16:                     );
  17:
  18:     Console.ReadKey();
  19:
  20: }

An important item to note is that none of the list index values are calculated unless they are requested or explicitly converted ToList() such as on line 13.

 

Solution 2 – List of Functions (Bonus: Closures & Currying)

Similar to the first solution, this method simply defines a set of algorithms for each constituent part and then uses the fist matching one at each index. By eliminating IEnumerable as the representation of the output we can save a great deal of work while keeping the same fundamental idea:

   1: using FBProcessor = System.Func<int, string>;

(…)

   1: static void FizzBuzzListOfFunctions()
   2: {
   3:
   4:     var Processors = new List<FBProcessor>()
   5:     {
   6:         (n) => (n % 3 == 0 && n % 5 != 0) ? "Fizz" : null,
   7:         (n) => (n % 5 == 0 && n % 3 != 0) ? "Buzz" : null,
   8:         (n) => (n % 5 == 0 && n % 3 == 0) ? "FizzBuzz" : null,
   9:         (n) => (n % 5 != 0 && n % 3 != 0) ? n.ToString() : null,
  10:     };
  11:
  12:     System.Func<int, FBProcessor> MatchingProcessor =
  13:         (n) => Processors.Find(p => p(n) != null);
  14:
  15:     Enumerable.Range(1, 100).ToList().ForEach(
  16:         n => Console.WriteLine(MatchingProcessor(n)(n)));
  17:
  18:     Console.ReadKey();
  19:
  20: }

This implementation uses closures (“Processors” reference in the MatchingProcessor lambda) and currying (see MatchingProcessor return type – FBProcessor – a function) to be as succinct as possible.

 

So What?

The fun part about these solutions is how easy they are to modify. They could even be modified dynamically at runtime. You could feed in more lists or functions without a problem. If you had a similar problem on a much larger scale, the syntax of lists or lists of functions would probably be a lot more readable than a set of if-then statements.

 

Additionally, since the lists or lists of functions are only evaluated until a matching one is found, you could tune the performance by reordering. If you encapsulated the FBProcessor functions into objects that kept track of how many positive matches were made you could (quite easily) automatically reorder them at runtime.

Your email address will not be published. Required fields are marked *

*