Solving FizzBuzz like a sloth
Recently I looked into the beautiful world of functional programming. I started reading Learn you a Haskell and I loved it.
One of the more interesting concepts in Haskell is the idea of non-strict evaluation, which basically means that an expression isn’t evaluated until it is really used.
# In Haskell x would never
# be evaluated because nobody uses x.
x = 4 + 5
puts 4
This concept allows you to work with infinite sets of data.
In the following example x is not evaluated until we request the first 100 elements and even then Haskell only evaluates the first 100 elements, not a single element more.
-- x is a list from 1 up to infinity
x = [1..]
-- take the first 100 elements of x
firstOneHundred = take 100 x
Ruby 2.0.0 introduced the lazy enumerator, which allows us to simulate such a behaviour.
Let’s map every positive integer to its equivalent string
# By calling `lazy` ruby will only evaluate
# the result if we request it
numbers = (1..Float::INFINITY).lazy.map(&:to_s)
# We request an evaluation by taking 100 number
# strings and transforming them to an array
puts numbers.take(100).to_a
Now we have everything you need to solve your typical FizzBuzz problem. But first let’s look how we would normally solve this problem.
We want to map every integer from 1 up to 100 to a string. If the number is divisible by 3 we replace the number string with “Fizz”, if it’s divisible by 5 we replace it with “Buzz”. If the number is divisible by 3 and 5 we replace it with “FizzBuzz”.
numbers = []
(1..100).each do |i|
if(i % 15 == 0)
numbers << "FizzBuzz"
elsif(i % 5 == 0)
numbers << "Buzz"
elsif(i % 3 == 0)
numbers << "Buzz"
else
numbers << i.to_s
end
end
This implementation works, but it’s not particularly nice. If you want to add a third rule like if the number is divisible by 7 replace it with “Bar”, it quickly gets out of hand. And keep in mind that the check for divisible by 3 and 5 has to come first.
Let’s reimplement it: FUNCTIONAL STYLE
First we create two arrays with the words fizz and buzz at the right position.
fizz = ["", "", "Fizz"]
buzz = ["", "", "", "", "Buzz"]
To transform it into an endless cycle we create a lazy enumerator and call the cycle method. Afterwards we can zip the two together.
fizz = ["", "", "Fizz"].lazy.cycle
buzz = ["", "", "", "", "Buzz"].lazy.cycle
# With the map method we transform the array of
# arrays that zip returns into one big array
# of strings.
pattern = fizz.zip(buzz).map(&:join)
Now that we have an endless stream of fizz and buzz, we need some number strings.
numbers = (1..Float::INFINITY).lazy.map(&:to_s)
Ok, let’s put it all together.
fizz = ["", "", "Fizz"].lazy.cycle
buzz = ["", "", "", "", "Buzz"].lazy.cycle
pattern = fizz.zip(buzz).map(&:join)
numbers = (1..Float::INFINITY).lazy.map(&:to_s)
fizzbuzz = numbers.zip(pattern).map(&:max)
puts fizzbuzz.take(100).to_a.join(", ")
Finally we have a functional fizzbuzz that maps every existent positive integer to a number, fizz, buzz or fizzbuzz. This implementation may look more complex but if you need to extend it you will be thankfull for this implementation.
So you see, you don’t have to seek refuge in the functional world of Haskell in order to write in a functional style. Plain old ruby does the job too.