FizzBuzz in Elixir

What is FizzBuzz?

FizzBuzz is one of the most popular code challenges and one I usually solve very early on while learning a new language, however I also solve it from time to time to see how far I have come.

It's also common challenge for interviews. I personally have been asked to solve this in a handful of interviews. I'm not sure it's as common as some people claim it is, but it does come up fairly frequently. Because of this, it's helpful to know a few quick ways to solve it in the languages you work in. So I figured I'd solve FizzBuzz a few ways in elixir.

If you're unfamiliar with the FizzBuzz challenge, here are the rules. Given a number input you need to decide to output one of four options Fizz, Buzz, FizzBuzz, or the number as is.

  1. Fizz if the number is divisible by three
  2. Buzz if the number is divisible by five
  3. FizzBuzz if it's divisible by both three and five
  4. if it is not divisible by any of those just return the number as is.

Simple enough but how you solve this problem can reveal quite a lot about how well you know the language you are writting your solution in.

Let's give it a shot!

My first thought was to do this using a good old fashioned conditional, this is usually the quickest way to solve FizzBuzz in any language.

defmodule FizzBuzz do
  def fizz_buzz(number) do
    cond do
      rem(number, 15) == 0 -> IO.puts("FizzBuzz")
      rem(number, 5)  == 0 -> IO.puts("Buzz")
      rem(number, 3)  == 0 -> IO.puts("Fizz")
      true -> IO.puts(Integer.to_string(number))
    end
  end

  def count_to(number) when is_integer(number) do
    Enum.each(1..number, &fizz_buzz/1)
  end
end

I'm using the kernal method rem, often reffered to as modulo in other languages, to get the remainder. Which will allow us to check if the number is divisible by 3, 5, or 15. We can use 15 here for short hand, for divisible by 3 and 5 evenly. When the remainder is 0 we know that the number we are checking has divided evenly, and should result in a Fizz, Buzz or FizzBuzz.

Not a bad solution to FizzBuzz and it's a perfectly valid solution, however it doesn't feel like we are leveraging all that elixir can do. This feels like a solution we could produce in any language and that's true, which is why it's my go to solution for my first crack at solving FizzBuzz in a new language. Maybe we can change this solution to be a bit more interesting.

So lets try some pattern matching, one of elixir's strongest features is the ability to pattern match. So what does a pattern matching solution look like in elixir?

defmodule FizzBuzz do
  def fizz_buzz(number) do
    case {rem(number, 3), rem(number, 5)} do
      {0,0} -> IO.puts("FizzBuzz")
      {0,_} -> IO.puts("Fizz")
      {_,0} -> IO.puts("Buzz")
      {_,_} -> IO.puts(Integer.to_string(number))
    end
  end

  def count_to(number) when is_integer(number) do
    Enum.each(1..number, &fizz_buzz/1)
  end
end

You can see here I am still using rem for the remainder but I'm setting the results into a tuple before pattern matching on that tuple, this is required since we cannot run functions over the left side of case statement matches. You could make the argument that this isn't a better solution, but I like how conscice this is and it feels good that we are leveraging a core feature of the language.

Alternatively, we could leverage elixir's ability to differentiate between functions of the same name to solve this and it provides a really nice solution. Elixir allows function overloading so we can define a function multiple times in the same module and elixir will figure out which one we are intending to call, this can be done with pattern matching in the arguments or by providing guard clauses when we need to do some processing on the input and check if a certain criteria has been met.

defmodule FizzBuzz do
  def count_to(number) when is_integer(number) do
    Enum.each(1..number, &fizz_buzz/1)
  end
  
  defp fizz_buzz(number) when rem(number, 15) == 0, do: IO.puts("FizzBuzz")
  defp fizz_buzz(number) when rem(number, 5)  == 0, do: IO.puts("Buzz")
  defp fizz_buzz(number) when rem(number, 3)  == 0, do: IO.puts("Fizz")
  defp fizz_buzz(number), do: IO.puts(Integer.to_string(number))
end

As you can see I am again using rem, and I have 4 methods with the same name of fizz_buzz which all accept a number. I have guard clauses on all except the last one. Elixir will operate on this like its a conditional statement, and execute the first one it finds matching. So if rem(number, 15) returns 0 it will choose that option, and so on down the list, in this screnario you must have one function that has no guard clasue to provide a default in case all other clauses fail.

I find this is a very clean solution, and yeilds a fairly readble end result. This solution has one major benefit over the other two, each pathway of code is segregated. So if they need to change they change independently of one another.

Conclusion

Solving FizzBuzz is a great way to explore a language and its capabilities. There are undoubtedly many other solutions to FizzBuzz using elixir, and these are just a few I came up with.