Some thoughts on FizzBuzz
-------------------------
markus schnalke
2015-05-03
The task is easy:
Write a program that prints the numbers from 1 to 100. But
for multiples of three print "Fizz" instead of the number
and for the multiples of five print "Buzz". For numbers
which are multiples of both three and five print "FizzBuzz".
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! !
! !
! If you've never implemented FizzBuzz, !
! then stop reading here and implement it first! !
! !
! !
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.
.
.
.
Michi told me about FizzBuzz and said that the guys at university
wanted to do this as a test at the beginning of the semester with an
undergraduate students class. They just wanted to see how they would
manage the task. I found this idea wonderful.
Before I studies the FizzBuzz problem, however, I needed to implement
it myself. This first implementation is the critical one. You can
never restore the same situation again. Hence, I ensured to avoid any
further information about FizzBuzz. I also haven't thought much about
the problem before I started to implement it. (Michi and I have only
discussed the general topic of doing such tests with students.)
Well, then one morning, I had some spare time and went for the
challenge. I chose to implement it in awk (because awk is a favorite
programming language for me).
This was the result:
BEGIN {
for (i=1; i<=100; i++) {
if (i%3==0) {printf("Fizz"); x++}
if (i%5==0) {printf("Buzz"); x++}
if (!x) {printf("%d", i)}
printf("\n")
x=0;
}
}
Actually, this is the result after my third try. First, I accidently
inverted the modulo calculation (if (i%3) ...) -- pretty stupid, yes.
Even more stupid was the try to solve the error quickly: if (!i%3)
... (wrong precedence!). Well, trial'n'error is just too tempting
when having the computer at hand!
Nonetheless, I like my solution, even now, that I thought more about
the problem. My code is not perfect but pretty good, I think.
In the days since then, I made some more thoughts on the problem.
This is the essence:
1) The problem sounds easier than it is.
2) The problem is more staightforward than it sounds.
3) There is a stong temptation to implement it as compact as
possible, because the description seems to imply a compact
solution.
4) The key point is to realize that we need to handle four cases:
- by 3 and by 5
- by 3
- by 5
- neither by 3 nor by 5
If one discovers this and implements these four explicit cases,
then the solution is pretty straightforward. (Just care for the
order of the cases!)
5) One should resist the strong temptation to implement the
program with only three cases. It's the most dangerous source
of error (for all those who get the for-loop right). It's much
more difficult to write an elegant solution with three cases,
and those solutions are likely more difficult to understand.
6) You succeed if you use an idiomatic for-loop and handle the
four cases separately.
7) Don't try to be clever!
The most straight-forward and thus foolproof solution is IMO:
BEGIN {
for (i=1; i<=100; i++) {
if (i%15==0) {
print "FizzBuzz"
} else if (i%3==0) {
print "Fizz"
} else if (i%5==0) {
print "Buzz"
} else {
print i
}
}
}
It contains only one tricky part: the modulo 15. Although it is
difficult to see this possibility in the first place (at least for
non-mathematicians), it is easy to understand it when reading the
code. (One might even argue, that the problem description ``multiples
of both three and five'' is unnecessarily complicated compared to
``multiples of fifteen''. For mathematicians, however, there exists
no semantic difference between the two description.)
An improved version of my three-case-solution from above (inspired
by the Wikipedia example) is:
BEGIN {
for (i=1; i<=100; i++) {
s=""
if (i%3==0) {s="Fizz"}
if (i%5==0) {s=s "Buzz"}
if (!s) {s=i}
print s
}
}
Conclusion:
FizzBuzz (hopefully) teaches that it is crucial to abstract the real
problem from the problem description.
It shows the temptation to give it a quick shot, which is assumed to
succeed, thus skipping the development of a structured problem model.
All those who take the time to develop this structured problem model
have the best basis to get the code right ... not only in this
academic example but especially in their real work.
(The undergraduate's widespread problem of getting the counting loop
right is a different problem that comes from their lack of knowing
language idioms. Using an idiomatic for-loop ensures that it iterates
exactly 100 times.)