FizzBuzzer

fizzbuzzer - 1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, Fizz Buzz, ... - a collection of algorithms for playing the word game for children that teaches division (one of the four basic arithmetic operations in mathematics) or helps you find the world's best coders in programming job interviews - are you (re)using the fizzbuzzer library? ;-) - don't reinvent the wheel or the feedbuzzer!

What's Fizz Buzz?

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.

-- Fizz Buzz @ Wikipedia

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

Algorithms

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)

-- Fizz Buzz @ Rosseta Code

Gold Standard

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

Classic

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

Monkey Patch (Extend) Fixnum Class

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

Object-Oriented with New Fizznum Class

class Fizznum
  attr_accessor :num

  def initialize(num)
    self.num = num
  end

  def fizzbuzz?() num % 3 == 0 && num % 5 == 0;  end
  def fizz?()     num % 3 == 0;  end
  def buzz?()     num % 5 == 0;  end

  def to_s
    if    fizzbuzz? then "FizzBuzz"
    elsif fizz?     then "Fizz"
    elsif buzz?     then "Buzz"
    else  num.to_s
    end
  end

end

def fizzbuzz
  (1..100).map{ |n| Fizznum.new(n).to_s }
end

Don't Repeat Yourself (DRY)

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

Enumarator

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

Lazy Enumerator to Infinity

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

Pattern Matching with Noaidi

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

Poor Man's (Do-It-Yourself) Pattern Matching

def fizzbuzz
  (1..100).map do |n|
    case [n % 3, n % 5].map {|remainder| remainder.zero? }
    when [true, true]    then "FizzBuzz"
    when [true, false]   then "Fizz"
    when [false, true]   then "Buzz"
    else n
    end
  end
end

Pattern Matching with dry-matcher

require "dry-matcher"

FizzBuzz = Dry::Matcher.new(
  fizzbuzz: Dry::Matcher::Case.new(
              match: -> value { value % 3 == 0 && value % 5 == 0 },
              resolve: -> value { "FizzBuzz" } ),
  fizz:     Dry::Matcher::Case.new(
              match: -> value { value % 3 == 0 },
              resolve: -> value { "Fizz" } ),
  buzz:     Dry::Matcher::Case.new(
              match: -> value { value % 5 == 0 },
              resolve: -> value { "Buzz" } ),
  other:    Dry::Matcher::Case.new(
              match: -> value { true },
              resolve: -> value { value } ))

def fizzbuzz
  (1..100).map do |n|
    FizzBuzz.(n) do |m|
      m.fizzbuzz { |v| v }
      m.fizz     { |v| v }
      m.buzz     { |v| v }
      m.other    { |v| v }
    end
  end
end

Rotate / No Conditionals

def fizzbuzz
  fizzy = [1,0,0]
  buzzy = [1,0,0,0,0]

  (1..100).map do |n|
     fizzy.rotate!
     buzzy.rotate!
     result = n.to_s * (1 - (fizzy[0] | buzzy[0]))
     result << "Fizz" * fizzy[0]
     result << "Buzz" * buzzy[0]
     result
  end
end

Contributed by John Schank.

With Parameterization

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

Install

Just install the gem:

$ gem install fizzbuzzer

License

The fizzbuzzer scripts are dedicated to the public domain. Use it as you please with no restrictions whatsoever.

Questions? Comments?

Send them along to the ruby-talk mailing list. Thanks!