> mthadley_

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.