Examing the FizzBuzz Problem From a New Perspective
In late May, I graduated from the Flatiron School Data Science program after spending the spring quarantined with my capstone project. Since there was nowhere to go, the project was completed steadily and timely. June was a flurry of job preparation activity: updating my resume, shoring up my LinkedIn profile, practicing answers to interview questions, fine-tuning my elevator pitch, and participating in a mock cultural interview. All had been running smoothly in my quarantine bubble after a mock technical interview. I had fumbled the technical interview and the feeling of failure stung. A lot. Why had I stumbled?!? I knew the material!
My immediate thought was to strengthen my Python coding skills so that the real interviews would go smoother. Deliberate, focused practice would be the key to my success; I googled “Python Interview Problems”, found an article on Medium with questions and their answers, and coded away. My personal challenge was to avoid all Google assistance — code using just my own skill and memory. The fifth question was the notorious “FizzBuzz” problem — write a program that prints the number from 1 to x, for numbers that are a multiple of 2 print “Fizz”, for numbers that are a multiple of 3 print “Buzz”, and for numbers that are multiples of both 2 and 3 print “FizzBuzz”. Sweet, I thought, I’ve done this problem before. I came up with two solutions — one using a FOR loop and one using a WHILE loop. I remembered to use the modulus and to compare the number in the loop to “6” first, otherwise, it is more challenging to capture the “FizzBuzz” event easily. Yes.
Referring back to the article for the author’s assessment and solution, I was floored. I had remembered the modulo but had “bombarded the solution with if statements”. And the author’s solution did not make sense to me. It felt like I was looking at the illusion image of the old woman and could not see the young lady.
Line by line, I needed to learn this new way of solving the FizzBuzz problem. Here is my journey to understanding the more elegant and more pythonic code solution. (Just to be clear, this solution is not mine. It is the work of Julien Kervizic in his Medium article “Python Screening Interview questions for DataScientist” dated May 11, 2019):
def fizzbuzzfn(num) -> str:
mod_2 = (num % 2 == 0)
mod_3 = (num % 3 == 0)
if (mod_2 or mod_3):
return (mod_2 * 'Fizz') + (mod_3 * 'Buzz')
return str(num)print('\n'.join([fizzbuzzfn(x) for x in range(1,51)]))
Starting at the top — on line 1 (def fizzbuzzfn(num) -> str:) was a “->”. I had never seen that before in python code and quickly discarded my no-Google rule; this was going to be all Google learning! The “->” turns out to be a function annotation that is referenced in PEP 3107. Needing a little more explanation, I found this on www.geeksforgeeks.org (sic):
Function annotations are arbitrary python expressions that are associated with various part of functions. These expressions are evaluated at compile time and have no life in python’s runtime environment. Python does not attach any meaning to these annotations. They take life when interpreted by third party libraries, for example, mypy.
I deleted the function annotation, ran the code again and it continued to work correctly. So, the function annotation was truly arbitrary in this case.
On to line 2, what is returned to the variable “mod_2” if a number turns out to be an even? “True” is returned. This was a bit mind-blowing — it never occurred to me to structure an answer using a bool. Line 3 was obviously less mind-blowing.
Next was to approach the solo IF statement on line 4. Why would it be an OR statement and not an AND statement? And what’s up with line 5 and multiplying a string by a True or False? As I learned after playing if mod_2 or mod_3 is “True”, we enter into the If statement and return either “Fizz”, “Buzz” or, if both are True, a “FizzBuzz”. And you can multiple bools by strings. Genius! And since statements after a Return are not executed, the number is not printed out — just a text string.
Line 6 simply returns the number that is not a multiple of 2, 3, or 6. Easy peasy.
Line 7. Ouch — a join() method and list comprehension — which looks a bit confusing. I simply broke it down into manageable steps. At first, just the list comprehension part:
list = ([fizzbuzzfn(x) for x in range(1,11)])print(list)
which returns a list of values:
[‘1’, ‘Fizz’, ‘Buzz’, ‘Fizz’, ‘5’, ‘FizzBuzz’, ‘7’, ‘Fizz’, ‘Buzz’, ‘Fizz’]
Next, try the same but with a join on a ‘-’ :
list = (‘-’.join([fizzbuzzfn(x) for x in range(1,11)]))print(list)
And the big finale of running the code as given (except for a change in the range):
Going through each line methodically and with Google helped me see a completely different style of a solution. Just like when you finally see the young girl in the illusion image.