Fizz buzz is a group word game for children to teach them about division. Players take turns to count incrementally, replacing any number divisible by three with the word “fizz”, and any number divisible by five with the word “buzz”.
Play. Players generally sit in a circle. The player designated to go first says the number “1”, and each player counts one number in turn. However, any number divisible by three is replaced by the word fizz and any divisible by five by the word buzz. Numbers divisible by both become fizz buzz. A player who hesitates or makes a mistake is eliminated from the game.
For example, a typical round of fizz buzz would start as follows:
1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, Fizz Buzz, 16, 17, Fizz, 19, Buzz, Fizz, 22, 23, Fizz, Buzz, 26, Fizz, 28, 29, Fizz Buzz, 31, 32, Fizz, 34, Buzz, Fizz, …
Other Uses. Fizz buzz has been used as an interview screening device for computer programmers. Creating a list of the first hundred fizz buzz numbers is a trivial problem for any computer programmer, so interviewers can easily sort out those with insufficient programming ability.
Can they? Q: Can you code a program for the fizz buzz word game that prints the first hundred numbers?
A: Why not (re)use the (open source, free) fizzbuzzer library? ;-) - Don’t reinvent the wheel or the feedbuzzer!
$ fizzbuzz
1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16, 17,
Fizz, 19, Buzz, Fizz, 22, 23, Fizz, Buzz, 26, Fizz, 28, 29, FizzBuzz, 31, 32,
Fizz, 34, Buzz, Fizz, 37, 38, Fizz, Buzz, 41, Fizz, 43, 44, FizzBuzz, 46, 47,
Fizz, 49, Buzz, Fizz, 52, 53, Fizz, Buzz, 56, Fizz, 58, 59, FizzBuzz, 61, 62,
Fizz, 64, Buzz, Fizz, 67, 68, Fizz, Buzz, 71, Fizz, 73, 74, FizzBuzz, 76, 77,
Fizz, 79, Buzz, Fizz, 82, 83, Fizz, Buzz, 86, Fizz, 88, 89, FizzBuzz, 91, 92,
Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz
Task
Write a program that prints the integers from 1 to 100 (inclusive).
But:
- for multiples of three, print Fizz (instead of the number)
- for multiples of five, print Buzz (instead of the number)
- for multiples of both three and five, print FizzBuzz (instead of the number)
Lookup pre-calculated (“hard coded”) constants. Fast. Faster. Fastest.
def fizzbuzz
[1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14,
"FizzBuzz", 16, 17, "Fizz", 19, "Buzz", "Fizz", 22, 23, "Fizz", "Buzz", 26,
"Fizz", 28, 29, "FizzBuzz", 31, 32, "Fizz", 34, "Buzz", "Fizz", 37, 38,
"Fizz", "Buzz", 41, "Fizz", 43, 44, "FizzBuzz", 46, 47, "Fizz", 49, "Buzz",
"Fizz", 52, 53, "Fizz", "Buzz", 56, "Fizz", 58, 59, "FizzBuzz", 61, 62,
"Fizz", 64, "Buzz", "Fizz", 67, 68, "Fizz", "Buzz", 71, "Fizz", 73, 74,
"FizzBuzz", 76, 77, "Fizz", 79, "Buzz", "Fizz", 82, 83, "Fizz", "Buzz", 86,
"Fizz", 88, 89, "FizzBuzz", 91, 92, "Fizz", 94, "Buzz", "Fizz", 97, 98,
"Fizz", "Buzz"]
end
def fizzbuzz
(1..100).map do |n|
if n % 3 == 0 && n % 5 == 0
"FizzBuzz"
elsif n % 3 == 0
"Fizz"
elsif n % 5 == 0
"Buzz"
else
n
end
end
end
Or use zero?
and n % 15
(3x5) for n % 5 && n % 3
:
def fizzbuzz
(1..100).map do |n|
if (n % 15).zero? then "FizzBuzz"
elsif (n % 3).zero? then "Fizz"
elsif (n % 5).zero? then "Buzz"
else n
end
end
end
Or use case/when/else
:
def fizzbuzz
(1..100).map do |n|
case
when n % 3 == 0 && n % 5 == 0 then "FizzBuzz"
when n % 3 == 0 then "Fizz"
when n % 5 == 0 then "Buzz"
else n
end
end
end
Or use next
with if
-clause:
def fizzbuzz
(1..100).map do |n|
next "FizzBuzz" if n % 3 == 0 && n % 5 == 0
next "Fizz" if n % 3 == 0
next "Buzz" if n % 5 == 0
n
end
end
class Fixnum
def to_fizzbuzz
if self % 3 == 0 && self % 5 == 0
"FizzBuzz"
elsif self % 3 == 0
"Fizz"
elsif self % 5 == 0
"Buzz"
else
self
end
end
end
def fizzbuzz
(1..100).map { |n| n.to_fizzbuzz }
end
class Fizznum
def initialize(n)
@n = n
end
def fizzbuzz?() @n % 3 == 0 && @n % 5 == 0; end
def fizz?() @n % 3 == 0; end
def buzz?() @n % 5 == 0; end
def val
if fizzbuzz? then "FizzBuzz"
elsif fizz? then "Fizz"
elsif buzz? then "Buzz"
else @n
end
end
end
def fizzbuzz
(1..100).map{ |n| Fizznum.new(n).val }
end
FIZZ = 'Fizz'
BUZZ = 'Buzz'
def divisible_by?(numerator, denominator)
numerator % denominator == 0
end
def divisible_by_3?( numerator )
divisible_by?( numerator, 3 )
end
def divisible_by_5?( numerator )
divisible_by?( numerator, 5 )
end
def fizzbuzz
(1..100).map do |n|
fizz = divisible_by_3? n
buzz = divisible_by_5? n
case
when fizz && buzz then FIZZ + BUZZ
when fizz then FIZZ
when buzz then BUZZ
else n
end
end
end
module FizzBuzz
def self.enumerator
Enumerator.new do |yielder|
(1..100).each do |n|
yielder << case
when n % 3 == 0 && n % 5 == 0 then "FizzBuzz"
when n % 3 == 0 then "Fizz"
when n % 5 == 0 then "Buzz"
else n
end
end
end
end
end
def fizzbuzz
FizzBuzz.enumerator.first( 100 )
end
Or fetch every value one at a time with next
:
def fizzbuzz
e = FizzBuzz.enumerator
[e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next,
e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next,
e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next,
e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next,
e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next,
e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next,
e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next,
e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next,
e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next,
e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next, e.next]
end
require "bigdecimal"
module FizzBuzz
def self.enumerator
Enumerator::Lazy.new(1..BigDecimal::INFINITY) do |yielder, n|
yielder << case
when n % 3 == 0 && n % 5 == 0 then "FizzBuzz"
when n % 3 == 0 then "Fizz"
when n % 5 == 0 then "Buzz"
else n
end
end
end
end
def fizzbuzz
FizzBuzz.enumerator.take( 100 ).force # or first( 100 ) is always eager by default
end
def fizzbuzz
(1..100).map do |n|
case [n % 3 == 0, n % 5 == 0]
when [true, true] then "FizzBuzz"
when [true, false] then "Fizz"
when [false, true] then "Buzz"
else n
end
end
end
require "noaidi"
def fizzbuzz
(1..100).map do |n|
Noaidi.match [n % 3, n % 5] do |m|
m.(0, 0) { "FizzBuzz" }
m.(0, Fixnum) { "Fizz" }
m.(Fixnum, 0) { "Buzz" }
m.(Fixnum, Fixnum) { n }
end
end
end
require "dry-matcher"
FizzBuzz = Dry::Matcher.new(
fizzbuzz: Dry::Matcher::Case.new(
match: -> value { value[0] == 0 && value[1] == 0 },
resolve: -> value { "FizzBuzz" } ),
fizz: Dry::Matcher::Case.new(
match: -> value { value[0] == 0 },
resolve: -> value { "Fizz" } ),
buzz: Dry::Matcher::Case.new(
match: -> value { value[1] == 0 },
resolve: -> value { "Buzz" } ),
other: Dry::Matcher::Case.new(
match: -> value { true },
resolve: -> value { value[2] } ))
def fizzbuzz
(1..100).map do |n|
FizzBuzz.([n % 3, n % 5, n]) do |m|
m.fizzbuzz { |v| v }
m.fizz { |v| v }
m.buzz { |v| v }
m.other { |v| v }
end
end
end
describe FizzBuzz do
describe "number is divisible" do
it "divisible by" do
divisible_by?(15,3).must_equal true
end
it "divisible by 3" do
divisible_by_3?(15).must_equal true
end
it "divisible by 5" do
divisible_by_5?(15).must_equal true
end
end
describe "number is fizzbuzz" do
before do
@result = fizzbuzz
end
it "returns 'Fizz' for multiples of 3" do
@result[3-1].must_equal "Fizz"
end
it "returns 'Buzz' for multiples of 5" do
@result[5-1].must_equal "Buzz"
end
it "returns 'FizzBuzz' for multiples of 3 and 5" do
@result[15-1].must_equal "FizzBuzz"
end
it "returns the passed number if not a multiple of 3 or 5" do
@result[1-1].must_equal 1
end
end
end
And here’s another fizzbuzz algorithm with a classic for
loop
and using divisible_by?
and case/when/else
clauses:
def divisible_by?(numerator, denominator)
numerator % denominator == 0
end
def divisible_by_3?( numerator )
divisible_by?( numerator, 3 )
end
def divisible_by_5?( numerator )
divisible_by?( numerator, 5 )
end
def fizzbuzz
result = []
for n in 1..100 do
result << case
when divisible_by_3?(n) && divisible_by_5?(n) then "FizzBuzz"
when divisible_by_3?(n) then "Fizz"
when divisible_by_5?(n) then "Buzz"
else n
end
end
result
end
Or use asserts for testing:
class TestFizzBuzz < Minitest::Test
def setup
@result = fizzbuzz
end
def test_fizz_multiples_of_3
assert_equal "Fizz", @result[3-1]
end
def test_buzz_for_multiples_of_5
assert_equal "Buzz", @result[5-1]
end
def test_fizzbuzz_for_multiples_of_3_and_5
assert_equal "FizzBuzz", @result[15-1]
end
def test_number_if_not_multiple_of_3_or_5
assert_equal 1, @result[1-1]
end
end
And here’s yet another fizzbuzz algorithm with a classic for
loop
and classic if/elsif/else
-clauses:
def fizzbuzz
result = []
for n in 1..100 do
result << if n % 3 == 0 && n % 5 == 0 then "FizzBuzz"
elsif n % 3 == 0 then "Fizz"
elsif n % 5 == 0 then "Buzz"
else n
end
end
result
end
Or use an all-in-one assert as used for testing all the fizzbuzz algorithms :-) in this repo:
FIZZBUZZES_1_TO_100 =
[1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14,
"FizzBuzz", 16, 17, "Fizz", 19, "Buzz", "Fizz", 22, 23, "Fizz", "Buzz", 26,
"Fizz", 28, 29, "FizzBuzz", 31, 32, "Fizz", 34, "Buzz", "Fizz", 37, 38,
"Fizz", "Buzz", 41, "Fizz", 43, 44, "FizzBuzz", 46, 47, "Fizz", 49, "Buzz",
"Fizz", 52, 53, "Fizz", "Buzz", 56, "Fizz", 58, 59, "FizzBuzz", 61, 62,
"Fizz", 64, "Buzz", "Fizz", 67, 68, "Fizz", "Buzz", 71, "Fizz", 73, 74,
"FizzBuzz", 76, 77, "Fizz", 79, "Buzz", "Fizz", 82, 83, "Fizz", "Buzz", 86,
"Fizz", 88, 89, "FizzBuzz", 91, 92, "Fizz", 94, "Buzz", "Fizz", 97, 98,
"Fizz", "Buzz"]
class TestFizzBuzz < Minitest::Test
def test_fizzbuzz
assert_equal FIZZBUZZES_1_TO_100, fizzbuzz
end
end
def fizzbuzz_engine(range, factors)
range.map do |n|
result = ""
factors.each do |(name, predicate)|
result << name if predicate.call(n)
end
result == "" ? n : result
end
end
def fizzbuzz
fizzbuzz_engine( 1..100, [["Fizz", -> n { n % 3 == 0 }],
["Buzz", -> n { n % 5 == 0 }]])
end
Or with default configuration settings:
FIZZBUZZ_DEFAULT_RANGE = 1..100
FIZZBUZZ_DEFAULT_FACTORS = [["Fizz", -> n { n % 3 == 0 }],
['Buzz', -> n { n % 5 == 0 }]]
def fizzbuzz_engine(range=FIZZBUZZ_DEFAULT_RANGE, factors=FIZZBUZZ_DEFAULT_FACTORS)
range.map do |n|
result = ""
factors.each do |(name, predicate)|
result << name if predicate.call(n)
end
result == "" ? n : result
end
end
def fizzbuzz
fizzbuzz_engine
end
def fizzbuzz
fizzy = [nil, nil, :Fizz].cycle
buzzy = [nil, nil, nil, nil, :Buzz].cycle
(1..100).map do |n|
"#{fizzy.next}#{buzzy.next}"[/.+/] || n
end
end
Tip: Use <1
for a shorter form of ==0
.
66 Bytes
def fizzbuzz
(1..100).map{|n|s="";n%3<1&&s+="Fizz";n%5<1&&s+="Buzz";s[/.+/]||n}
end
65 Bytes
def fizzbuzz
(1..100).map{|n|n%3<1?(n%5<1?"FizzBuzz":"Fizz"):(n%5<1?"Buzz":n)}
end
64 Bytes
def fizzbuzz
(1..100).map{|n|n%3*n%5<1?(n%3<1?"Fizz":"")+(n%5<1?"Buzz":""):n}
end
62 Bytes
def fizzbuzz
(1..100).map{|n|n%15<1?"FizzBuzz":n%5<1?"Buzz":n%3<1?"Fizz":n}
end
58 Bytes
def fizzbuzz
(1..100).map{|n|"#{[:Fizz][n%3]}#{[:Buzz][n%5]}"[/.+/]||n}
end
And the winner is…
54 Bytes
def fizzbuzz
(1..100).map{|n|n%3<1&&x="Fizz";n%5<1?"#{x}Buzz":x||n}
end
or
def fizzbuzz
(1..100).map{|n|["%sBuzz"%x=["Fizz"][n%3]][n%5]||x||n}
end
Note: This chapter was written by Tom Dalling first published on the Ruby Pigeon website.
I know. FizzBuzz has been done to death. But I want to use it as a familiar base upon which we can explore some of the common tradeoffs involved in writing and maintaining software. In this article, I’ll show multiple Ruby implementations of FizzBuzz, all designed to achieve different goals, and discuss the implications of each.
FizzBuzz is a simple programming task, used in software developer job interviews, to determine whether the job candidate can actually write code. It was invented by Imran Ghory, and popularized by Jeff Atwood.
Here is a description of the task:
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 print “FizzBuzz”.
It’s very well known in software development circles. There are multiple implementations in every language, joke implementations, and plenty of articles discussing its usefulness during the hiring process.
Let’s kick things off with a super simple, straight-forward implementation. This implementation gives the correct results, and there is nothing clever about it.
1.upto(100) do |i|
if i % 3 == 0 && i % 5 == 0
puts 'FizzBuzz'
elsif i % 3 == 0
puts 'Fizz'
elsif i % 5 == 0
puts 'Buzz'
else
puts i
end
end
Now let’s start applying some common software development practices to it.
I’m fairly certain that when Dijkstra descended from Mt Sinai, DRY was inscribed on one of his stone tablets. Also known as “Single Source of Truth,” DRY is universally accepted as a pillar of good software design. It involves removing redundancy and duplication from our code.
Let’s apply DRY to the naïve implementation above. The sources of duplication that immediately pop out to me are:
i % 3 == 0
and i % 5 == 0
both appear twiceputs
appears four timesAfter removing those, our implementation looks like this:
1.upto(100) do |i|
fizz = (i % 3 == 0)
buzz = (i % 5 == 0)
puts case
when fizz && buzz then 'FizzBuzz'
when fizz then 'Fizz'
when buzz then 'Buzz'
else i
end
end
This implementation has a few advantages. If we wanted to replace puts
with
something else, now we only have to change it in a single place instead of four.
In the naïve example, if we were to add an additional case to the case
statement, we might
have forgotten to use puts
, but that’s not a problem here. Also, if the
definition of when to Fizz or Buzz changes – for example, if it should
Fizz on multiples of seven, instead of three – then we only need to change
one value instead of two. In summary, DRY is reducing the likelihood of
introducing bugs while updating the code.
But why stop there? I can still see duplication. The i % _ == 0
pattern
appears twice, and the string literals 'Fizz'
and 'Buzz'
are duplicated
inside the 'FizzBuzz'
literal. Let’s fix those up too.
FIZZ = 'Fizz'
BUZZ = 'Buzz'
def divisible_by?(numerator, denominator)
numerator % denominator == 0
end
1.upto(100) do |i|
fizz = divisible_by?(i, 3)
buzz = divisible_by?(i, 5)
puts case
when fizz && buzz then FIZZ + BUZZ
when fizz then FIZZ
when buzz then BUZZ
else i
end
end
Now, if the “Fizz” or “Buzz” strings need to be changed, we’ve got that covered. We’re also covered if we want to change the way we test whether a number is divisible by another number. I don’t know why we would ever need to change that, but the option is there.
By extracting the use of the modulo operator (%
) into its own function, we’ve made the code more self-documenting.
If someone else were to read the code and they didn’t understand how the modulo operator worked, they could work it out based on the function name.
All these changes are aimed at insulating ourselves from bugs caused by changing the code in the future. If you need to change something that exists in multiple places, there is always the possibility that we will forget to change one of those places.
We’re not insulated from all changes, however. What if some pointy-haired suit forces us to add a “Zazz” for multiples of seven? What if we have to handle an arbitrary number of Fizzes and Buzzes and Zazzes? Maybe the users want to define their own list of FizzBuzz values, with different output strings and different multiples.
Let’s level up the implementation by removing the hard-coded constants and turning them into parameters. Here are the parameters that seem reasonable to me:
The new parameterized implementation looks like this:
def fizzbuzz(range, triggers)
range.each do |i|
result = ''
triggers.each do |(text, divisor)|
result << text if i % divisor == 0
end
puts result.empty? ? i : result
end
end
fizzbuzz(1..100, [
['Fizz', 3],
['Buzz', 5],
])
Standard FizzBuzz doesn’t fit the needs of your users? No problem. Now you can BlizzBlazz from -50 to -20, or WigWamWozzleWumpus from 10 to 10,000,000.
We’ve introduced a new concept: triggers. A trigger is the pairing of a divisor and an output string. There is no official name for this pairing, due to FizzBuzz being a synthetic problem as opposed to a real-world problem, but it’s not that uncommon. We create abstract models of data and processes, and these models contain things that need to be named. Often times there is a pre-existing name we can use, but sometimes not. Note that this concept is completely absent from previous implementations.
The divisible_by?
function was removed, because the modulo operation only
happens in a single place now. It’s already DRY, so we can inline it.
The triggers
parameter is an array. This is important because it’s called
“FizzBuzz”, not “BuzzFizz”. Ordering matters here. We’re using an array to
indicate that “Fizz” must come before “Buzz” in the situation were both are
triggered.
Interestingly, since hashes are ordered in Ruby, the triggers
parameter could
have also been a hash like this:
triggers = {
3 => 'Fizz',
5 => 'Buzz',
}
Hash ordering guarantees were introduced in Ruby 1.9. Prior to that, ordering was undefined.
In most programming languages, the elements of a hash are unordered, for performance reasons. That is, when iterating over the key/value pairs, the order is unpredictable. Ruby, however, guarantees that key/value pairs are ordered by insertion.
This implementation is actually more DRY than the last one. We can now see that
“fizz” and “buzz” are kind of duplicates of each other. Now that they are combined
into the triggers
array, we can get rid of the FIZZ
and BUZZ
constants, and
also the fizz
and buzz
variables, from the previous implementation.
There are more potential parameters than just range
and triggers
. What if
we wanted to “Zazz” on all numbers less than 10? Our current implementation is
not flexible enough to handle that change. We can, however, accommodate this
change by parameterizing the “divisible by” condition.
def fizzbuzz(range, triggers)
range.each do |i|
result = ''
triggers.each do |(text, predicate)|
result << text if predicate.call(i)
end
puts result.empty? ? i : result
end
end
fizzbuzz(1..100, [
['Fizz', ->(i){ i % 3 == 0 }],
['Buzz', ->(i){ i % 5 == 0 }],
['Zazz', ->(i){ i < 10 }],
])
This implementation gives the following (truncated) output:
Zazz
Zazz
FizzZazz
Zazz
BuzzZazz
FizzZazz
Zazz
Zazz
FizzZazz
Buzz
11
Fizz
13
14
FizzBuzz
16
The definition of a “trigger” has changed. The divisor has been replaced with a predicate.
The predicate is a lambda, which returns true
or false
to indicate whether the trigger applies to a given number.
Once you start passing functions (e.g. lambdas/blocks) as parameters to other functions, there are
a lot of things that can be parameterized. At the moment, the output
text from multiple triggers are combined by simple string concatenation,
but we could have a function parameter that controls how the strings
are combined. We could replace the puts
with a function parameter
that controls what happens to the results. I’m going to stop parameterizing
at this point, just to keep this article shorter, but you get the idea.
FP is so hot right now. All the cool kids are doing it.
In all seriousness, I do believe that programming in a functional style produces better software. This isn’t an article about the merits of FP, so let’s just make the assumption that it’s something we aspire to, for the sake of brevity.
The last implementation is sort of written in a functional style already. We’ve got a higher-order function (a function that takes a function parameter) and we’re using lambdas (anonymous functions).
We are mutating a string using the <<
operator, though. In FP, we try to
avoid mutation in favour of using immutable values. However this is the least
troublesome type of mutation. We’re only mutating local state, and then we
return the string and forget about it. Local scope is like Las Vegas: what
happens in local scope, stays in local scope. Nobody saw us mutating the
string, so nobody has to know. I think that everyone accepts that this sort of
temporary local mutation is totally fine, except maybe Haskell zealots.
The glaring FP faux pas in the current implementation is that the fizzbuzz
function has side effects. Specifically, it prints out text every time it is
called. If we get rid of the side effects, we will have a pure function,
which is something that we always strive for when writing in a functional
style. Here is an implementation that returns the output instead of printing
it:
def fizzbuzz(range, triggers)
range.map do |i|
result = ''
triggers.each do |(text, predicate)|
result << text if predicate.call(i)
end
result == '' ? i : result
end
end
puts fizzbuzz(1..100, [
['Fizz', ->(i){ i % 3 == 0 }],
['Buzz', ->(i){ i % 5 == 0 }],
['Zazz', ->(i){ i < 10 }],
])
The key difference is that range.each
has been changed into range.map
,
which converts the range into an array of outputs that is then returned.
Instead of printing each value with puts
, we just puts
the whole
array returned from the fizzbuzz
function.
The output is the same as the previous implementation, but now we have a pure function, with all the benefits that pure functions bring.
We can take the functional style even further:
def fizzbuzz(range, triggers)
range.map do |i|
parts = triggers.select{ |(_, predicate)| predicate.call(i) }
parts.empty? ? i : parts.map(&:first).join
end
end
puts fizzbuzz(1..100, [
['Fizz', ->(i){ i % 3 == 0 }],
['Buzz', ->(i){ i % 5 == 0 }],
['Zazz', ->(i){ i < 10 }],
])
Again, this implementation produces the same output. However, the string
mutation has been removed, along with the procedural-style triggers.each
loop. The new implementation uses select
to filter the triggers,
another map
, and join
(a kind of reduction). Map, filter and reduce
are the bread and butter of functional programming. This is probably
overkill, however, considering that fizzbuzz
was already a pure function beforehand.
We’ve come a long way from the naïve implementation, but we can go further.
What if we needed to generate terabytes of output? Like, instead of calculating
pi to the billionth digit, we want to calculate FizzBuzz to the billionth
output. Currently, the fizzbuzz
function returns an array containing all
output, but we will run out of memory if we try to make a multi-terabyte array.
Plus, we can’t start printing output until the whole array is made. We
obviously have to stop generating and returning the whole array.
In this implementation, we generate a single output value, print it, throw it away, then repeat.
def fizzbuzz(start, triggers)
Enumerator.new do |yielder|
i = start
loop do
parts = triggers.select{ |(_, predicate)| predicate.call(i) }
i_result = parts.size > 0 ? parts.map(&:first).join : i
yielder.yield(i_result)
i += 1
end
end
end
enumerator = fizzbuzz(1, [
['Fizz', ->(i){ i % 3 == 0 }],
['Buzz', ->(i){ i % 5 == 0 }],
['Zazz', ->(i){ i < 10 }],
])
loop { puts enumerator.next }
Instead of returning an array, the fizzbuzz
function now returns an instance
of Enumerator
. This particular Enumerator
is an infinite enumerator
– that is, it will keep generating output values forever.
The yielder.yield
call contains magic that stops the
infinite loop from hanging the application. Every time that enumerator.next
is called, the enumerator will generate and return the next output value in the
sequence. The loop
on the last line keeps printing the output values forever.
Ruby can handle arbitrarily large integers, so this will
literally run until the i
variable is a single number so big that it won’t
fit in memory. That’s a number so big that we can consider it infinite, for all
practical purposes.
This is the concept of “laziness” in software design. Think of it like a demotivated employee. They sit there doing nothing until you ask them for something, then they do the minimum amount of work necessary to give you what you asked for.
Although there is still some functional-style code at the core, this isn’t very
functional anymore. Enumerators are stateful, and every call to next
is
mutating the enumerator. This implementation is still DRY and parameterized,
though.
This FizzBuzz implementation is super flexible now - some might say too flexible. We’ve got to open source it. This powerful, reusable functionality needs to be made available to everyone.
But we can’t just dump it on GitHub. What about documentation, namespacing, and tests? Currently, it’s just a script that prints out results in an infinite loop.
The last step – the last step I’m going to demonstrate in this article, at least – is to polish the code for consumption by other developers. This should include tests and usage documentation, but I’m not going to show those here.
Here is the ultimate FizzBuzz implementation:
module FizzBuzz
DEFAULT_RANGE = 1..100
DEFAULT_TRIGGERS = [
['Fizz', ->(i){ i % 3 == 0 }],
['Buzz', ->(i){ i % 5 == 0 }],
]
##
# Makes an array of FizzBuzz values for the given range and triggers.
#
# @param range [Range<Integer>] FizzBuzz integer range
# @param triggers [Array<Array(String, predicate)>] An array of [text, predicate]
# @return [Array<String>] FizzBuzz results
#
def self.range(range=DEFAULT_RANGE, triggers=DEFAULT_TRIGGERS)
enumerator(range.first, triggers).take(range.size)
end
##
# Makes a FizzBuzz value enumerator, starting at the given integer, for the
# given triggers.
#
# @param start [Integer] The first integer to FizzBuzz
# @param triggers [Array<Array(String, predicate)>] An array of [text, predicate]
# @return [Enumerable] Infinite sequence of FizzBuzz results, starting with `start`
#
def self.enumerator(start=DEFAULT_RANGE.first, triggers=DEFAULT_TRIGGERS)
Enumerator.new do |yielder|
i = start
loop do
parts = triggers.select{ |(_, predicate)| predicate.call(i) }
i_result = parts.size > 0 ? parts.map(&:first).join : i.to_s
yielder.yield(i_result)
i += 1
end
end
end
end
And here is some example usage code, with example output:
FizzBuzz.range(1..5)
#=> ["1", "2", "Fizz", "4", "Buzz"]
FizzBuzz.range(1..5, [['Odd', &:odd?]])
#=> ["Odd", "2", "Odd", "4", "Odd"]
e = FizzBuzz.enumerator
e.next #=> "1"
e.next #=> "2"
e.next #=> "Fizz"
e.next #=> "4"
e.next #=> "Buzz"
Everything is inside a module called FizzBuzz
. It’s an anti-pattern for
third party code to pollute the global namespace, so we want to tuck everything
underneath a single namespace to be a good citizen.
All the code that prints the output is gone now. That’s not really code worth sharing. The reusable part is the output generation.
The fizzbuzz
function has been renamed to enumerator
, which better describes
its purpose.
All parameters are now optional. Maybe users want plain-old vanilla FizzBuzz without all the fancy bells and whistles, so we provide sensible defaults. Users of this implementation don’t even have to learn what a “trigger” is if they just want standard FizzBuzz output.
There is a new function called range
that returns an array, like older
implementations used to. You can’t know what other peoples use case will be,
so it’s not nice to force everyone to use the enumerator API if they don’t need
it. The range
function is a “convenience” function that provides a simpler
interface to a more complicated API. If you want to get all gang-of-four, you
could call it a “facade.” It uses an enumerator under the hood, to keep things
DRY.
One subtle difference is the to_s
call. In previous implementations, the
output would either be a string or an integer. This is somewhat of a no-no.
We’ve just been printing the output values, and in Ruby you can print integers
or strings and everything works fine. But what if a user of the library writes
some code that only works for strings? It will crash on the integers. To avoid
confusion, this implementation converts integers to strings before returning
them. Now output values are always strings, so nobody has to account for two
different types.
All functions have documentation for parameters and return values. There should
also be a separate document that contains examples of how to use the library,
such as a README.md
file.
The last step is to write a gemspec and a rakefile, to build, tag, and publish versions of the gem.
Let’s reflect on the journey we have just taken. You may have thought that I was demonstrating how to improve the code in every iteration. Or maybe you thought that I thought I was improving the code. Not so. Every iteration increased the complexity, and cost time. That’s not what we want. We want functionality without bugs, and we want it as cheaply as possible.
The first implementation uses these methods: upto
, %
, ==
, puts
. It has
about 10 expressions/statements.
The final implementation uses these methods: range
, enumerator
, first
,
take
, size
, select
, call
, map
, first
, join
, to_s
, yield
, +
,
==
. It has about 20 expressions/statements. It has higher-order functions and
lambdas. It introduces the concept of triggers, and infinite enumerators. It
also comes with a bunch of documentation, tests, and other supporting files. On
top of that, it doesn’t even print out the results – that part is left up
to the user of the library.
The final implementation represents a veritable explosion of complexity. Complexity bad. More code means more bugs, slower maintenance, and a steeper learning curve for other developers. We could have taken it further, too. There are plenty more ways to parameterize the code. We could have done performance optimisation, added concurrency, etc. Soon enough, you’re implementing a DefaultFizzBuzzUpperLimitParameter, several levels deep into an inheritance hierarchy.
I guess what I’m trying to get at here is the essence of YAGNI: You Ain’t Gonna Need It. YAGNI is up there with DRY, in terms of importance. If you’re not certain that you need it, then YAGNI. But isn’t it better to add the flexibility now, so we save time later when the change request comes in? YAGNI.
Adding flexibility isn’t free. It costs time, and it adds unnecessary complexity to the codebase. Complexity is like a mortgage – you’re going to be paying interest on it. You’ve probably heard of the term “technical debt” before.
When we choose the quick but inflexible implementation, we’re usually saving time. If a change request comes in later, we can spend that saved time implementing the more complex solution. If the change request doesn’t come in, then we win! We’re laughing all the way to the chronological bank.
Does that mean that the final and most complex implementation is a waste of time? Usually, but not always. Maybe you truly need to generate a few terabytes of FizzBuzz output. A simple implementation is not going to cut it, in that case. Maybe you have strange requirements which make all that parameterization necessary. Maybe it’s going to be used by lots of other developers in lots of other projects. The implementation that you choose really depends on your exact requirements.
But we must be honest with ourselves about our requirements. Do we really need terabytes of boutique FizzBuzz output? Really? Is it written in stone somewhere? Can we start with a simpler implementation, and extend it if it becomes necessary? In the absence of a definitive answer, lean towards YAGNI.
Note: This chapter was written by Paweł Świątkowski.
Tom Dalling recently posted a blog post about playing with FizzBuzz in Ruby, to get a grasp of different approaches to problem solving. I decided to create a small addition to this topic: how to do FizzBuzz in Ruby using pattern matching techniques.
Wait, pattern matching in Ruby? Yes. As you may know, I have written a gem called Noaidi which is an attempt to get pattern matching goodies in Ruby. Even though some of reader were not convinced about this being beneficial for the code, here are more examples.
If you want to know more about Noaidi itself (I’m not going to explain it in detail here), read one of the previous posts: A Quest for Pattern Matching in Ruby or Refactoring Rails with Noaidi.
In the first step we are going to rewrite a solution number two from Tom’s post:
1.upto(100) do |i|
fizz = (i % 3 == 0)
buzz = (i % 5 == 0)
puts case
when fizz && buzz then 'FizzBuzz'
when fizz then 'Fizz'
when buzz then 'Buzz'
else i
end
end
How? For each i
we are going to create a pair: i mod 3
and i mod 5
and pass it to the matcher. If both are 0
, we have a FizzBuzz; if only first one is zero, we have a Fizz, etc.
1.upto(100).each do |i|
results = [i % 3, i % 5]
Noaidi.match(results) do |m|
m.(0, 0) { puts 'FizzBuzz' }
m.(0, Fixnum) { puts 'Fizz' }
m.(Fixnum, 0) { puts 'Buzz' }
m.(Fixnum, Fixnum) { puts i }
end
end
This is quite simple and nicely looking. There are some drawback, though:
Fixnum
to match any number. This is not really that bad,
but having a way to denote “anything, I don’t care” would be a bit nicer.
This is on Noaidi’s roadmap.i
in the last match. This prevents us from compiling
a match beforehand to gen a performance boost (see Noaidi’s README for more details).
To avoid that, we would need to pass an array of three: [i, i % 3, i % 5]
and this is a bit too much.Modules of Noaidi were inspired by Erlang. You can (and should - this is the idea) define many functions (called “funs”) with the same name but with different signature. The “best match” (i.e. the first one that matches) will be picked and executed.
We could create a direct translation of the matcher above to a module. But to add more fun, we are going to use guard clauses this time to check if we “like” the arguments we get.
Here is the code:
fizz = Noaidi.module do
fun :buzz, [->(i) { i % 5 == 0 && i % 3 == 0}] do
'FizzBuzz'
end
fun :buzz, [->(i) { i % 3 == 0}] do
'Fizz'
end
fun :buzz, [->(i) { i % 5 == 0 }] do
'Buzz'
end
fun :buzz, [Fixnum] do |n|
n
end
end
1.upto(100).each do |i|
puts fizz.buzz(i)
end
Here, the logic was moved to the module completely. In the actual loop we only
call fun buzz
of module fizz
with the current number.
Module does its work by checking first if the number is divisible by both 3 and 5, then by each one of them and in the end for just any number. A correct value (string or number) is returned from the module, which is then printed in the loop.
This is faster than previous implementation, as module is created only once, before the loop. We also don’t need intermediate values calculated before passing to the module and thus we don’t need any closures. If you ask me, this is most “correct” solution.
Noaidi is not the only solution for pattern matching in Ruby. Folks from dry.rb have their own implementation, called dry-matcher. I’m not a big fan of this solution, as it requires to write a lot of boliterplate code, but I’m giving example of it too.
fizzbuzz_case = Dry::Matcher::Case.new(
match: -> value { value % 3 == 0 && value % 5 == 0 },
resolve: -> value {}
)
fizz_case = Dry::Matcher::Case.new(
match: -> value { value % 3 == 0 },
resolve: -> value {}
)
buzz_case = Dry::Matcher::Case.new(
match: -> value { value % 5 == 0 },
resolve: -> value {}
)
other_case = Dry::Matcher::Case.new(
match: -> value { true },
resolve: -> value { value }
)
matcher = Dry::Matcher.new(fizzbuzz: fizzbuzz_case,
fizz: fizz_case, buzz: buzz_case, other: other_case)
1.upto(100).each do |i|
matcher.(i) do |m|
m.fizzbuzz { puts 'FizzBuzz' }
m.fizz { puts 'Fizz' }
m.buzz { puts 'Buzz' }
m.other { |x| puts x }
end
end
Again, there are four cases, which is no surprise. Each of them has a value
key,
which is an actual matcher. A resolve
key denotes what will be passed to an action block,
when matcher is executed.
After having written those four cases, we create an actual matcher. And then it becomes somewhat pleasure. We execute the matcher in the loop and take action depending on what has been matched.
It is not that bad, but clearly requires us to write a lot more code than in previous examples. That being said, it is almost certain that it is much more flexible on edge cases. When we have to do more manual work, we definitely can do crazier things if we want.
This is all just fun, of course. It probably won’t convince you to use pattern matching. But I think it is worth to know that there are libraries in Ruby ecosystem that let you do that. Maybe you’ll find it useful at some point?