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:
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:
(*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:
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:
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:
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:
(“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:
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:
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.
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.