DryBuzz

Published: Mon 31 July 2017
By EWS

In Blog.

As a follow-on from a previous article I wrote on revisiting the oft-cited FizzBuzz program, a colleague pointed me to an interesting site where one can get to see FizzBuzz written in a whole bunch of different languages, namely: “Rosetta Code”. Rosetta Code describes itself as a “programming chrestomathy” site. I had no idea what chrestomathy meant until I happened to need to write FizzBuzz again … now I do.

In any case, looking at the C# versions, I can't help be a little bothered by the lack of DRY-ness of many of the solutions (probably my as-yet undiagnosed OCD rearing its head again). There are two solutions on the Rosetta Code site which seem DRY, the first one being the following:

class Program
{
    static void Main()
    {
        for (uint i = 1; i <= 100; i++) {
            string s = null;

            if (i % 3 == 0)
                s = "Fizz";

            if (i % 5 == 0)
                s += "Buzz";

            System.Console.WriteLine(s ?? i.ToString());
        }
    }
}

What I mean by DRY (in case you don't already know) is that nothing is repeated, that is, “Fizz” and “Buzz” are only “mentioned” once, 3 and 5 are only mentioned once (for that matter, 15 doesn't count as avoiding multiple “mentions”). There is one other solution in the C# section which does gets a pale green tick for DRY-ness, and also happens to be a one-line solution:

Enumerable.Range(1, 100)
                .Select(a => String.Format("{0}{1}", a % 3 == 0 ? "Fizz" : string.Empty, a % 5 == 0 ? "Buzz" : string.Empty))
                .Select((b, i) => String.IsNullOrEmpty(b) ? (i + 1).ToString() : b)
                .ToList()
                .ForEach(Console.WriteLine);

Both the above solutions do, however still have two if statements; this is something that is shared amongst the vast majority of FizzBuzz implementations and it’s something that one would want to keep if the optimization function is all about code readability.

Coming up with a clever solution to any programming problem (however straightforward) seems to be a badge of honour amongst geeks. I thought I'd throw my hat into the ring with a twist on the now highly-flogged FizzBuzz horse that ups the ante on the DRY-ness factor:

Enumerable.Range(1, 100)
    .Select(i => (n:i, s:string.Join("",
     "35".Select(v => i % (v - 48) == 0 ? "FiBu".Substring(v - 51, 2) + "zz" : "").ToArray()))).ToList()
     .ForEach(t => Console.WriteLine(t.s != "" ? t.s : t.n + ""));

The fact is, Fizz and Buzz both share a couple of z's, so does that count as repetition? Perhaps its pushing things a little. The fact that C# allows one to treat char as int means that we can make things a lot more compact by encapsulating magic numbers (in this case, 3 and 5) as chars.

A quick rundown of the features that make this solution unique:

  • Use of a C# 7 feature to encapsulate data in a compact (obscure?) way, that is value tuples which allow us to (at last) say something like:

    var t = ("The meaning of...", 42);
    

    as opposed to the much clunkier version with-which .NET developers the world-over are, no doubt, very familiar:

    var t = new Tuple<string, int>("The meaning of...", 42);
    
  • The expansion of Fizz and Buzz is made generic by parameterizing the modulus check as opposed to having separate if statements for each one;

  • The value of the magic numbers (3 and 5) are used directly to reference the corresponding magic string, that is “Fi” or “Bu” using a Substring call.

As much as DRY is something I like to work towards, admittedly my solution has sacrificed readability for brevity. Perhaps I should stop looking at Rosetta Code and direct my attention towards code obfuscation sites.

I'm not quite sure if this FizzBuzz thing is completely out of my system, but I am hoping that this will be the last article on the topic.

Comments !

social