FizzBuzz kata in F#: stage 1 by Mark Seemann
In previous posts I've walked through the Bank OCR kata in F#. In this post, I will do the same for the first stage of the very simple FizzBuzz kata. This is a very simple kata, so if you already know F#, there will be nothing new to see here. On the other hand, if you've yet to be exposed to F#, this is a good place to start - I'll attempt to walk you through the code assuming that you don't know F# yet.
Unit test #
Since I developed the solution using Test-Driven Development, I started by writing a single Parameterized Test:
[<Theory>] [<InlineData(1, "1")>] [<InlineData(2, "2")>] [<InlineData(3, "Fizz")>] [<InlineData(4, "4")>] [<InlineData(5, "Buzz")>] [<InlineData(6, "Fizz")>] [<InlineData(7, "7")>] [<InlineData(8, "8")>] [<InlineData(9, "Fizz")>] [<InlineData(10, "Buzz")>] [<InlineData(11, "11")>] [<InlineData(12, "Fizz")>] [<InlineData(13, "13")>] [<InlineData(14, "14")>] [<InlineData(15, "FizzBuzz")>] [<InlineData(16, "16")>] [<InlineData(17, "17")>] [<InlineData(18, "Fizz")>] [<InlineData(19, "19")>] [<InlineData(20, "Buzz")>] let FizzBuzzReturnsCorrectResult number expected = number |> FizzBuzz |> should equal expected
This test uses xUnit.net data theories to provide a set of test data in the form of an integer as input and an expected string.
The number input variable is piped to the FizzBuzz function, using F#'s pipe operator |>. This is just another way of writing
The pipe operator simply takes the data being piped and uses it as the last input parameter to the function being piped. In this case, the number integer variable is the data being piped, so it's used as the last input parameter to the FizzBuzz function, which only takes a single paramter.
The result of invoking the FizzBuzz function is a string. This result is again piped to the should method, which is defined by the FsUnit module. The should method is an assertion function that takes three input parameters. The first two parameters are supplied as part of the function invokation as equal expected, but since the pipe operator is being used, the third and final parameter value is the result of invoking the FizzBuzz function.
In all, the test states that when the FizzBuzz function is called with number, the result should be equal to the expected string.
The FizzBuzz implementation is really simple:
let FizzBuzz number = match number with | i when i % 3 = 0 && i % 5 = 0 -> "FizzBuzz" | i when i % 3 = 0 -> "Fizz" | i when i % 5 = 0 -> "Buzz" | _ -> number.ToString()
All it does is to use pattern matching against the number input argument. In all cases except the last one, the value of number is matched against any number, but with a condition. The first condition is that the number should be divible by both 3 and 5. If this is the case, the result to the right of the -> operator is returned from the function ("FizzBuzz").
The last line of the match block uses an underscore as the match pattern. This is a catch-all pattern that's being triggered if none of the other patterns are matched. In this case, the number input argument is converted to a string and returned.
Printing all lines #
The kata requires me to print the output for all numbers from 1 to 100. The astute reader may have noticed that the FizzBuzz function doesn't do that - it only converts a single integer to a string. However, printing all numbers fizzed and buzzed is easy:
[1..100] |> List.map FizzBuzz |> List.reduce (sprintf "%s\r\n%s")
The first line defines a list of numbers from 1 to 100. The next line pipes this list of integers into the List.map function, which applies the FizzBuzz function to each integer. The output of this function call is another list of strings ["1"; "2"; "Fizz"; "4"; "Buzz"; etc.]. This list of strings is piped into the List.reduce function, which in this case uses the sprintf function to concatenate the strings and add a line break after each element, so that it formats correctly.
The List.reduce function applies a function to pairwise elements in a list in order to produce a new element of the same type. Consider the beginning of the list of strings ["1"; "2"; "Fizz"; "4"; "Buzz"; etc.]. The List.reduce function starts with "1" and "2" and applies a function in order to produce a new string from those two strings. That function is the sprintf function, which is similar to the more well-known String.Format method in the BCL. In this case, the template is to take the two strings and insert a line break between them. Thus, when applied to "1" and "2", the result is
(notice the line break). Now, the List.reduce function takes that string and the next string in the list ("Fizz") and applies the funtion again, giving this result:
1 2 Fizz
It now takes this string and the next value ("4") and applies the sprintf function once more, etc. This is how the final list is being printed.
In a future post I'll walk you through stage 2 of the kata.