Proported to be a classic interview question to filter out people who do not know how to code, although I have never known anyone who actually has been asked to do this.
Given some number Fizz
, if it is divisible by 5 it should be replaced as Buzz
, and if it is divisible by both it should be replaced with FizzBuzz
.
Below are two of the common solutions. Note: They are shown as a function that takes a single integer, and returns string to output as the outer loop to print all the numbers from
One solution is to build up a result of Fizz
and Buzz
and return that (or the integer).
def fizzbuzz(n: int) -> str:
result = ""
if n % 3 == 0:
result += "Fizz"
if n % 5 == 0:
result += "Buzz"
if not result:
result = str(n)
return result
A second solution is to explicitly check the “divisible by % 15
check as 15 is the Least Common Multiple of 3 and 5).
def fizzbuzz(n: int) -> str:
if n % 15 == 0:
return "FizzBuzz"
if n % 3 == 0:
return "Fizz"
if n % 5 == 0:
return "Buzz"
return str(n)
Both the solutions above result in doing a double check. In the “Build it Up” solution, we double check with the if not result
. We are checking if neither the mod
Additionally, we can see how expanding this solution to include more checks, for example FizzBuzzHissHowl
, using the numbers
The question then becomes, can we solve FizzBuzz without doing this double check? As summarized in this write-up of solving FizzBuzz using a domain specific language in Haskell, the interesting part of FizzBuzz is that the control flow required is such that “the default action is executed only if some previous actions were not executed.” That is our double checking above, it is especially clear in the “Build it Up” solution where we only trigger the default of neither of the other cases fired.
def fizzbuzz(n: int) -> str:
def test(d: int, s: str, f: Callable[..., str]) -> Callable[..., str]:
if n % d == 0:
return lambda _: s + f("")
return f
fizz = functools.partial(test, 3, "Fizz")
buzz = functools.parital(test, 5, "Buzz")
return fizz(buzz(lambda s: s))(str(n))
The above solution (modified from this video) solves fizzbuzz without double checking and is easy to extend to more multiples as seen in fizzbuzz.py
. The version here also uses inner functions to make the solution more self-contained.
In our solution, we encode this with functions. At the base case we have our default action lambda s: s
returning the number as is. Then we build up a chain of functions, each one representing a divisibility check. If a check is successful, we return a function that, when called, will add the string representing this check passing (Fizz
, Buzz
, etc.) to the output of the of the calling the function the original check as passed. The critical part of this is that the this call passes the empty string to the function, effectively turning it off if it is the default action. If the check is not successful, the pass in function is return, meaning that if this check doesn’t pass, the default function will eventually be called with the number and execute the base case.
Let’s examine what happens when a number is, not divisible by
Starting with the case of not divisible by either, we have our default action lambda s: s
. When this is called with "2"
we will get our number. First we look at buzz(lambda s: s)
. The check 2 % 5 == 0
happens, and if fails meaning that the default action is returned. This default action is then the one passed to fizz
. Inside fizz
, the check 2 % 3 == 0
also fails. This results in the default action being returned so the final result of fizz(buzz(lambda s: s))
is just lambda s: s
so when we finally call it with str(2)
we get 2 back.
In this case where f
to buzz
. The check for divisibility by fizz
. Inside the fizz
function, the 3 % 3 == 0
check passes. This means the returned function is our new lambda. This lambda will return the string "fizz"
plus the result of calling f
with the empty string. f
is our default action so the return value is ""
(effectively turning off the default action). and the final result is ~”Fizz”~.
When buzz
with the default action return a new function that when called will return "Buzz"
plus the result of the default action called with ""
(which is again means the default is turned off and final result is just a "Buzz"~). This function that will return ~"Buzz"
is passed to fizz~. The ~fizz
check fails returning the buzz returning function and a final call will output ~”Buzz”~.
In the final case where buzz(lambda s: s)
will return a function that outputs "Buzz"~. Then the call to ~fizz
will return a function that returns "Fizz"
to the result of the input function f
called with the empty string. In this case, f
is not the default action but instead one that returns "Buzz"
plus the turned off default action. So the final result when called returns ~”FizzBuzz”~.
By adding extra check functions (in the order you want the words to appear in the output string) to this call chain it is easy to extend FizzBuzz to any number of divisibility checks without an explosion in case logic or extra checks.