Chris Pine Tutorial 99 Bottles of Beer Program

Discussion in 'Ruby' started by danielj, Aug 27, 2008.

  1. danielj

    danielj Guest

    Just a beginner with a question about this:

    <i>• "99 bottles of beer on the wall..." Write a program which prints
    out the lyrics to that beloved classic, that field-trip favorite: "99
    Bottles of Beer on the Wall."</i>

    This is what I came up with:

    bottles = 100

    while bottles > 1

    puts (bottles-1).to_s + " Bottles of beer on the wall, " +
    (bottles-1).to_s + " Bottles of beer! You take one down, you pass it
    around and " + (bottles-2).to_s + " Bottles of beer on the wall!"

    bottles = (bottles-1)

    end

    Is this ok?

    Are there faster or better ways?

    Thanks.
    danielj, Aug 27, 2008
    #1
    1. Advertising

  2. danielj

    danielj Guest

    Also, would there be an easy way to print the words out instead of the
    numbers of bottles on the wall?
    danielj, Aug 27, 2008
    #2
    1. Advertising

  3. danielj

    Todd Benson Guest

    On Tue, Aug 26, 2008 at 6:30 PM, danielj <> wrote:
    > Just a beginner with a question about this:
    >
    > <i>=95 "99 bottles of beer on the wall..." Write a program which prints
    > out the lyrics to that beloved classic, that field-trip favorite: "99
    > Bottles of Beer on the Wall."</i>
    >
    > This is what I came up with:
    >
    > bottles =3D 100
    >
    > while bottles > 1
    >
    > puts (bottles-1).to_s + " Bottles of beer on the wall, " +
    > (bottles-1).to_s + " Bottles of beer! You take one down, you pass it
    > around and " + (bottles-2).to_s + " Bottles of beer on the wall!"
    >
    > bottles =3D (bottles-1)
    >
    > end
    >
    > Is this ok?
    >
    > Are there faster or better ways?


    Have a look at the #downto method.

    hth a little,
    Todd
    Todd Benson, Aug 27, 2008
    #3
  4. danielj

    Ryan Davis Guest

    On Aug 26, 2008, at 16:30 , danielj wrote:

    > puts (bottles-1).to_s + " Bottles of beer on the wall, " +


    pls use interpolation:

    puts "#{bottles-1} bottles of beer on the wall"

    is much more idiomatic... cleaner too.
    Ryan Davis, Aug 27, 2008
    #4
  5. danielj

    James Gray Guest

    On Aug 26, 2008, at 7:45 PM, Ryan Davis wrote:

    > On Aug 26, 2008, at 16:30 , danielj wrote:
    >
    >> puts (bottles-1).to_s + " Bottles of beer on the wall, " +

    >
    > pls use interpolation:
    >
    > puts "#{bottles-1} bottles of beer on the wall"
    >
    > is much more idiomatic... cleaner too.


    I agree. I really wish Chris Pine's book taught this, but it does not.

    James Edward Gray II
    James Gray, Aug 27, 2008
    #5
  6. danielj

    Todd Benson Guest

    On Tue, Aug 26, 2008 at 6:30 PM, danielj <> wrote:
    > Also, would there be an easy way to print the words out instead of the
    > numbers of bottles on the wall?


    Divide the number by 10 and it will give the prefix (ninety-, eighty-,
    etc.) You need a list or hash of some sort, of course. Simple
    example...

    prefixes = {9 => "ninety-", 8 => "eighty-"}

    ...or even better...

    prefixes = ["", "", "twenty-", "thirty-", "forty-"]

    ...and so on. And then modify the numbers ten through nineteen after
    the transformation (you don't want "one" for "eleven" do you) since
    they differ in nomenclature from the others.

    You could automagically use suffixes instead of prefixes if the
    number, when divided by 10 is 1, but that wouldn't help you with the
    edge cases of 'ten', 'eleven', and 'twelve'.

    Todd

    Todd
    Todd Benson, Aug 27, 2008
    #6
  7. Rob Biedenharn, Aug 27, 2008
    #7
  8. On Wed, Aug 27, 2008 at 9:57 AM, Todd Benson <> wrote:
    > On Tue, Aug 26, 2008 at 6:30 PM, danielj <> wrote:
    >> Also, would there be an easy way to print the words out instead of the
    >> numbers of bottles on the wall?

    >
    > Divide the number by 10 and it will give the prefix (ninety-, eighty-,
    > etc.) You need a list or hash of some sort, of course. Simple
    > example...
    >
    > prefixes = {9 => "ninety-", 8 => "eighty-"}
    >
    > ...or even better...
    >
    > prefixes = ["", "", "twenty-", "thirty-", "forty-"]
    >
    > ...and so on. And then modify the numbers ten through nineteen after
    > the transformation (you don't want "one" for "eleven" do you) since
    > they differ in nomenclature from the others.
    >
    > You could automagically use suffixes instead of prefixes if the
    > number, when divided by 10 is 1, but that wouldn't help you with the
    > edge cases of 'ten', 'eleven', and 'twelve'.


    http://p.ramaze.net/1900
    my stab at it

    ^ manveru
    Michael Fellinger, Aug 27, 2008
    #8
  9. Michael Fellinger wrote:
    > On Wed, Aug 27, 2008 at 9:57 AM, Todd Benson <> wrote:
    >> On Tue, Aug 26, 2008 at 6:30 PM, danielj <> wrote:
    >>> Also, would there be an easy way to print the words out instead of the
    >>> numbers of bottles on the wall?

    >> Divide the number by 10 and it will give the prefix (ninety-, eighty-,
    >> etc.) You need a list or hash of some sort, of course. Simple
    >> example...
    >>
    >> prefixes = {9 => "ninety-", 8 => "eighty-"}
    >>
    >> ...or even better...
    >>
    >> prefixes = ["", "", "twenty-", "thirty-", "forty-"]
    >>
    >> ...and so on. And then modify the numbers ten through nineteen after
    >> the transformation (you don't want "one" for "eleven" do you) since
    >> they differ in nomenclature from the others.
    >>
    >> You could automagically use suffixes instead of prefixes if the
    >> number, when divided by 10 is 1, but that wouldn't help you with the
    >> edge cases of 'ten', 'eleven', and 'twelve'.

    >
    > http://p.ramaze.net/1900
    > my stab at it
    >
    > ^ manveru
    >


    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.

    #!/usr/bin/env ruby

    class Fixnum
    ENGLISH = {
    0 => 'zero',
    1 => 'one',
    2 => 'two',
    3 => 'three',
    4 => 'four',
    5 => 'five',
    6 => 'six',
    7 => 'seven',
    8 => 'eight',
    9 => 'nine',

    10 => 'ten',
    11 => 'eleven',
    12 => 'twelve',
    13 => 'thirteen',
    14 => 'fourteen',
    15 => 'fifteen',
    16 => 'sixteen',
    17 => 'seventeen',
    18 => 'eighteen',
    19 => 'ninteen',

    20 => 'twenty',
    30 => 'thirty',
    40 => 'forty',
    50 => 'fifty',
    60 => 'sixty',
    70 => 'seventy',
    80 => 'eighty',
    90 => 'ninety',

    100 => 'one hundred and',
    200 => 'two hundred and',
    300 => 'three hundred and',
    400 => 'four hundred and',
    500 => 'five hundred and',
    600 => 'six hundred and',
    700 => 'seven hundred and',
    800 => 'eight hundred and',
    900 => 'nine hundred and'
    }

    def to_english
    i = ENGLISH.keys.select{|n| n <= self}.max
    ENGLISH + (i < self ? " " + (self-i).to_english : '')
    end
    end

    99.downto(1) do|i|
    puts "#{i.to_english} bottles of beer on the wall"
    end

    --
    Michael Morin
    Guide to Ruby
    http://ruby.about.com/
    Become an About.com Guide: beaguide.about.com
    About.com is part of the New York Times Company
    Michael Morin, Aug 28, 2008
    #9
  10. 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]: http://www.deveiate.org/projects/Linguistics/
    [2]: http://99-bottles-of-beer.net/lyrics.html
    --
    Posted via http://www.ruby-forum.com/.
    Matthias Reitinger, Aug 28, 2008
    #10
  11. 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]: http://www.deveiate.org/projects/Linguistics/
    > [2]: http://99-bottles-of-beer.net/lyrics.html


    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
    http://ruby.about.com/
    Become an About.com Guide: beaguide.about.com
    About.com is part of the New York Times Company
    Michael Morin, Aug 28, 2008
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Materialised

    [OT]99 Bottles of Beer

    Materialised, Aug 10, 2004, in forum: C Programming
    Replies:
    2
    Views:
    315
    CBFalconer
    Aug 11, 2004
  2. danielj
    Replies:
    6
    Views:
    166
  3. Replies:
    9
    Views:
    402
    whisperjim
    Nov 27, 2008
  4. Replies:
    0
    Views:
    252
  5. whisperjim
    Replies:
    9
    Views:
    156
    Sam Howes
    Aug 31, 2009
Loading...

Share This Page