Saturday, June 18, 2016

FizzBuzz in F# in 99 Characters

I haven't done a whole lot with functional programming for a while. But at NDC Oslo, I got to hang out with the FP folks, and it inspired me to start exploring (and playing) again. I picked up my Digit Recognizer project and got the F# code integrated. (And I've done more with that project since the last article; I'll be writing that up soon.)

So, I've been playing and exploring a bit. And I put together a very compact implementation of FizzBuzz. We'll see why I was headed for "compact" a bit later.

FizzBuzz
FizzBuzz is a pretty simple programming problem. There are only a few rules:
  1. Print the numbers from 1 to 100.
  2. If the number is divisible by 3, print the word "Fizz".
  3. If the number is divisible by 5, print the word "Buzz".
  4. If the number is divisible by both 3 and 5, print "FizzBuzz".
Since this is a pretty simple problem, I've found it as a good way to get started with TDD (in C#). You can check out a video of that here: New Video: TDD Basics with C#.

Here's one way of implementing a method that fulfills the rules:


Why FizzBuzz and F#?
When I've been working with F#, I've been really interested in doing things the "functional way". I figure the more that I can do that, the more I can wrap my brain around the functional concepts that are just beyond my grasp at the moment.

But when I tried to do FizzBuzz in F#, I ran into a roadblock. The implementations that came up with were all very imperative in nature. The C# code above is pretty straightforward: we start with an empty string, append "Fizz" and/or "Buzz" if appropriate, and if we don't append either one, then we print out the original number.

But this deals with a mutable object (the "output" string).

I even talked to some of the functional folks at an even back in November, and one of the suggested solutions used if/elif/else:


This works -- it generates a list with the proper data -- but it felt too much like the code I'd written in C#. I wanted something a bit "more functional" (yes, I know that's not strictly necessary, but I'm still learning and trying to get the concepts and language features).

Help from Elixir
The reason that I'm back on this today is that I just watched the recording of Bryan Hunter's (@bryan_hunter) talk at NDC Oslo: What every Node.js developer needs to know about Elixir. He showed an Elixir implementation of FizzBuzz:


And this gave me a great idea of how I could use pattern matching in F# to do the same thing.

So, I didn't write anything original, but I translated it from Elixir to F# (which is actually a pretty good accomplishment considering my current experience with the language).

Pattern Matching in F#
So, here's the code in F#:


Let's walk through this.

First, the "[1..100]" creates a list consisting of the integers from 1 to 100.

Then we use the pipe-forward operator "|>" to pass that to the "List.map" function. The "map" function will apply a function to each of the elements in our list.

Then we have the function that uses pattern matching to get what we want. The "fun x ->" tells us that we have a function with a parameter called "x". In this case, "x" is the integer that represents the value of a list element.

Then we have the "match" expression. The first part "(x % 3, x % 5)" will create a tuple with 2 values. This uses the modulo operator "%" to get the remainder when we divide by 3 for the first value, and divide by 5 for the second value.

So if "x" is 9, our tuple would be "(0, 4)" since 9 divided by 3 has a remainder of 0, and 9 divided by 5 has a remainder of 4.

After the "with", we have the patterns that we're trying to match.

The first pattern "(0, 0)" tells us that the number is evenly divisible by both 3 and 5 (since the remainders of both after division is 0). So we want to output "FizzBuzz" here.

The next pattern "(0, _)" tells us that the number is evenly divisible by 3 (the first value). For the second value, we have an underscore. This means that we don't care what this number is. Since the number is divisible by 3, we output "Fizz". (Our example above, "(0,4)" would match this pattern.)

The next pattern "(_, 0)" tells us that the number is evenly divisible by 5 (the second value). We don't care what the first value is in this case. Since it's divisible by 5, we output "Buzz".

The last pattern "(_, _)" says that we don't care what the values are. This is a "catch all", and will simply convert the integer to a string.

Since this pattern matching is greedy (meaning the first pattern to match wins), we know that we'll get the output that we want. If we reordered things, then we would get unexpected results.

Here's the output when we run this in the REPL:


Success!

And I liked this implementation much better than using the if/elif/else. The pattern matching seemed like a "better" solution since pattern matching is such a key component of F#. This gave me a chance to use it and explore a bit.

Paring Down to 109 Characters
Since this implementation was fairly compact, I decided to see how small I could make it. So, I took out all of the white space that I could:


This isn't very readable, but it's only 109 characters. This got me within my goal.

What's the goal? Well Mathias Brandewinder (@brandewinder, who I've mentioned many times) wrote a Twitter bot that parses F# and sends back results. He showed me this at the Microsoft MVP Summit last November, and I thought it was pretty cool.

Basically, you just send a tweet to @fsibot, and it will tweet back with the result. (As an aside, Mathias showed me all of the parsing and input sanitation that he has to do to make sure that no one does anything malicious with it. It's quite a challenge.)

Since 109 characters will fit in a tweet, I sent it in:


Success!

The Purpose
Is this world changing? Absolutely not. But it is interesting. It is a bit of a challenge. And it's also a bit of fun. I had a good time exploring, and I learned a bit more about pattern matching.

BTW, I found that I actually have some extra characters. The parentheses around the patterns can be removed:


This removes several more characters (also notice that the last pattern simply has a single underscore -- this still acts as the "catch all").

This brings the total down to 99 characters:


Cool stuff.

I've found a bit of joy in the functional programming that I've been doing recently. I'm definitely going to continue. My travel schedule lightens up a bit in July. That will be a good time for deep dive.

Keep exploring, and find those things that pique your interest and make coding fun.

Happy Coding!

No comments:

Post a Comment