Forum: Ruby FizzBuzz (#126)

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
James G. (Guest)
on 2007-06-07 16:48
(Received via mailing list)
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:

  #!/usr/bin/env ruby -w

  1.upto(100) do |i|
    if i % 5 == 0 and i % 3 == 0
      puts "FizzBuzz"
    elsif i % 5 == 0
      puts "Buzz"
    elsif i % 3 == 0
      puts "Fizz"
    else
      puts i
    end
  end

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 M.'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:

  if n % 15 == 0                           # darrin kirby's code

  # ... 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 G. that shows off both tricks:

  (1..100).each do |x|
   m3 = x.modulo(3) == 0
   m5 = x.modulo(5) == 0

   puts case
     when (m3 and m5) then 'FizzBuzz'
     when m3 then 'Fizz'
     when m5 then 'Buzz'
     else x
   end
  end

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 M. sent in a good example of code that only does each step
once:

  (1..100).each{|i|
    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...
This topic is locked and can not be replied to.