There has been some debate on the proper ways to screen programmers you intend to hire. A common theory is that you really need to have the programmer write some code for you to accurately gauge their skill. Exactly what to have them write is another debate, but the blogosphere has recently been abuzz with this question as a screener:
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
Pretend you've just walked into a job interview and been hit with this question. Solve it as you would under such circumstances for this week's Ruby Quiz.
The discussion around this quiz touched on what the interviewer is actually looking for when they ask this question. Most people seem to agree that at least one goal is to establish that you are capable of writing some code.
The other item we assume the interviewers are searching for is some degree of cleverness. The interesting thing is that we don't seem to agree on the amount of cleverness. Some feel you should provide a no-nonsense solution, solving the problem just as you would if it was a small part of your work for the day. Others feel it's better to let your style flare a bit and show off unusual approaches to solving the problem. The solutions covered both approaches very well.
I'll start with my own, just because I can explain my thinking behind it. Here's the code:
1.upto(100) do |i|
if i % 5 == 0 and i % 3 == 0
elsif i % 5 == 0
elsif i % 3 == 0
The interesting part of this solution is what happened in my head. When I began to code it up and realized the combined test would need to come first, I experienced a brief moment of doubt in my logic and considered writing some tests. After a calming breath, I convinced myself that this problem was easy enough to not worry about them. I was confident I was right, though this problem is about the upper limit of what I am comfortable doing without tests and I might have caved-in easier under the pressure of a real interview. For a nice test-driven solution do look over Jason Merrill's response to the quiz.
There's nothing tricky in my code, of course. I walk the numbers and perform divisibility tests to decide what to print. Many solutions took a very similar approach with minor variations. One such variation was to change the first divisibility check to:
# ... or ...
when num.dividable_by?(3*5): "fizzbuzz" # Rene Koning's code
My opinion was that the two test approach was a more self-documenting way to code it up, but I found Rene's solution to be a good balance.
Looking back on my code now that I've examined over 70 solutions, I'm painfully aware of how much duplication there is in it. I repeat the divisibility tests and the calls to puts() in almost every branch. A lot of solutions DRYed that code up. Two common ways to do it were to pre-calculate the divisibility tests and to move puts() outside of the if/case statements. Here's some code from Bill Guindon that shows off both tricks:
m3 = x.modulo(3) == 0
m5 = x.modulo(5) == 0
when (m3 and m5) then 'FizzBuzz'
when m3 then 'Fizz'
when m5 then 'Buzz'
You can see we're down to a single call to puts() here and The results of the tests are cached for convenient reuse. I think that's an improvement.
We do still have duplication here too though, on a smaller scale. First, the test results are still checked multiple times. That's not as bad as redoing the whole test, but it would be nice if it wasn't needed. More importantly, the 'Fizz' and 'Buzz' Strings are repeated in the 'FizzBuzz' String. Obviously, there is still room for DRYing things up.
Daniel Martin sent in a good example of code that only does each step once:
x = ''
x += 'Fizz' if i%3==0
x += 'Buzz' if i%5==0
puts(x.empty? ? i : x);
Here the output is built up piece by piece. A divisible by three check adds the 'Fizz' and divisible by five the 'Buzz' If the number happens to be divisible by both they will be combined to yield 'FizzBuzz' From there, a quick check is needed to see if the String is still empty?(), meaning that we should output the digit instead.
It's short code, easy to follow, and it doesn't give me any doubts about it's functionality. I think it's a solid approach to take.
Of course, if you're a fan of clever, don't miss MenTaLguY's lambda calculus solution.
My thanks to everyone who blew the quiz record out of the water time around! I'll be looking for more boring problems now just because staying on top of the submissions was a real challenge. ;)
Tomorrow we will show just how versatile Ruby programmers can be and weave a few blankets...