Chris Pine Tutorial 99 Bottles of Beer Program

Michael Morin wrote:

I did something slightly different, which works on multiple levels, not
just two. For example, it can go all the way up to 999 without any
extra code.

[...]

Unfortunately your implemention fails with multiples of 100.

Being the lazy guy I just used the excellent [Ruby Lingustics
Framework][1] to deduce English numerals. The following program
produces the [full 99 Bottles of Beer lyrics][2] with numerals:

···

---
require 'Linguistics'
Linguistics::use(:en)

class Fixnum
  def bottles
    case self
      when 0: "no more bottles"
      when 1: "one bottle"
      else "#{self.en.numwords} bottles"
    end
  end
end

99.downto(0) do |n|
  puts "#{n.bottles.capitalize} of beer on the wall, #{n.bottles} of
beer."
  if n > 0
    puts "Take one down and pass it around, #{(n-1).bottles} of beer on
the wall."
    puts
  else
    puts "Go to the store and buy some more, #{99.bottles} of beer on
the wall."
  end
end
---

Regards,
Matthias

[1]: dev(E)iate
[2]: 99 Bottles of Beer | The lyrics to the song 99 Bottles of Beer
--
Posted via http://www.ruby-forum.com/\.

Matthias Reitinger wrote:

Michael Morin wrote:

I did something slightly different, which works on multiple levels, not
just two. For example, it can go all the way up to 999 without any
extra code.

[...]

Unfortunately your implemention fails with multiples of 100.

Being the lazy guy I just used the excellent [Ruby Lingustics Framework][1] to deduce English numerals. The following program produces the [full 99 Bottles of Beer lyrics][2] with numerals:

---
require 'Linguistics'
Linguistics::use(:en)

class Fixnum
  def bottles
    case self
      when 0: "no more bottles"
      when 1: "one bottle"
      else "#{self.en.numwords} bottles"
    end
  end
end

99.downto(0) do |n|
  puts "#{n.bottles.capitalize} of beer on the wall, #{n.bottles} of beer."
  if n > 0
    puts "Take one down and pass it around, #{(n-1).bottles} of beer on the wall."
    puts
  else
    puts "Go to the store and buy some more, #{99.bottles} of beer on the wall."
  end
end
---

Regards,
Matthias

[1]: dev(E)iate
[2]: 99 Bottles of Beer | The lyrics to the song 99 Bottles of Beer

So it does. Just drop the "and" from the strings and it's still technically correct. Things could be better if I had special cases for things like that, but I didn't want to complicate the code.

Nothing lazy about using libraries. Linguistics takes a different approach though. They have three different procs stored in an array for the number of digits in the number.

# A collection of functions for transforming digits into word
# phrases. Indexed by the number of digits being transformed; e.g.,
# <tt>NumberToWordsFunctions[2]</tt> is the function for transforming
# double-digit numbers.
NumberToWordsFunctions = [
  proc {|*args| raise "No digits (#{args.inspect})"},

  # Single-digits
  proc {|zero,x|
    (x.nonzero? ? to_units(x) : "#{zero} ")
  },

  # Double-digits
  proc {|zero,x,y|
    if x.nonzero?
      to_tens( x, y )
    elsif y.nonzero?
      "#{zero} " + NumberToWordsFunctions[1].call( zero, y )
    else
      ([zero] * 2).join(" ")
    end
  },

  # Triple-digits
  proc {|zero,x,y,z|
    NumberToWordsFunctions[1].call(zero,x) +
    NumberToWordsFunctions[2].call(zero,y,z)
  }
]

That's quite clever. Each proc calls the previous proc. Mine relied on recursion and subtraction instead of separating the digits. Doing it this way gives you an easy way to implement the special cases.

···

--
Michael Morin
Guide to Ruby

Become an About.com Guide: beaguide.about.com
About.com is part of the New York Times Company