Subverting Fizzbuzz
I recently came across this article by Naomi Liu. It’s a really funny satire on technical interviews, functional programming, and over-engineering. You should definitely give it a read.
One thing that stuck with me from the article was the solution to Fizzbuzz. I’ve only ever considered the first “modulus” solution presented in the article, so the second in Haskell was novel, at least to me. I figured this was partly due to language-specific features like lazy evaluation and infinite lists. However, the more I looked at it, the more I wondered if you could actually write a very similar solution in Ruby.
So of course I tried, and here’s what I came up with:
FIZZES = ["", "", "fizz"]
BUZZES = ["", "", "", "", "buzz"]
def fizzbuzzes
FIZZES.cycle.lazy.zip(BUZZES.cycle).map { |*s| s.join }
end
def fizzbuzz(n)
fizzbuzzes.take(n).zip(1..).map do |fb, i|
case fb
in "" then i.to_s
in s then s
end
end.to_a
end
Calling fizzbuzz
gives you an array, like the solution in the article:
fizzbuzz(20)
# => ["1", "2", "fizz", "4", "buzz",
# "fizz", "7", "8", "fizz", "buzz",
# "11", "fizz", "13", "14", "fizzbuzz",
# "16", "17", "fizz", "19", "buzz"]
Frankly, I’m surprised how similar you can get with Ruby. Some functions are
identically named, like Enumerable#cycle
or Enumerable#take
, and do the
same thing. Ruby has ranges, which
use a similar ..
syntax as Haskell. Ruby even has
pattern matching
now.
One big difference is that without Enumerable#lazy
, the Ruby solution gets
stuck in an infinite loop. Ruby enumerators are eager by default, but calling
lazy
does what it says on the tin, making it “lazy”, and fixing the issue.
This is the first time I’ve used lazy
for anything, but It Just Works™.
The Haskell solution is still more elegant in my eyes, but I’m always impressed by Ruby’s flexibility.