I was able to count down to 97 bottle of beers but I am having trouble looping to count down to 1. Is it possible to create a loop with what I wrote? Here's what I have so far.
all_beers = (99).to_s
one_less = ((all_beers).to_i - 1).to_s
puts '' +
all_beers + ' bottles of beer on the wall, ' +
all_beers + ' bottles of beer. You take one down you pass it around ' +
one_less + ', beers on the wall!'
all_beers = one_less
one_less = ((all_beers).to_i - 1).to_s
puts '' +
all_beers + ' bottles of beer on the wall, ' +
all_beers + ' bottles of beer. You take one down you pass it around ' +
one_less + ', beers on the wall!'
use downto :
it will loop from the number you want upto the number you wish.
99.downto(1).each do |s|
all_beers = s
one_less = s - 1
puts '' +
all_beers.to_s + ' bottles of beer on the wall, ' +
all_beers.to_s + ' bottles of beer. You take one down you pass it around ' +
one_less.to_s + ', beers on the wall!'
end
Yes, it is certainly possible. This is taken from 99 Bottles of Beer project:
#
# Rubeer.rb
# by Eric Budd, Jan. 2008
#
# Demonstrates adding functionality to a built-in class, optional method parameters, inline
# conditionals, string replacement, alcohol aversion, and excessively fancy use of hashes.
#
# This borrows the hash from Daniel Straight's excellent implementation for the "wordalize" method
#
class Integer
NUMBER_WORDS = { 0 => "no", 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 => "nineteen",
20 => "twenty", 30 => "thirty", 40 => "forty", 50 => "fifty", 60 => "sixty",
70 => "seventy", 80 => "eighty", 90 => "ninety"}
def wordalize
raise "Invalid number to wordalize - should be in the range (0..99)" unless (0..99) === self
return NUMBER_WORDS[self] if self < 20
wordalized = NUMBER_WORDS[self - (self % 10)]
wordalized += '-' + NUMBER_WORDS[self % 10] unless (self % 10) == 0
return wordalized
end
def bottles
raise "Invalid number of bottles - should be in the range (0..99)" unless (0..99) === self
how_many_bottles = self.wordalize + ' bottle'
how_many_bottles += 's' unless self == 1
return how_many_bottles
end
alias :bottle :bottles # for grammar Nazis
end
def sing(number, teetotaller = false)
beverage = teetotaller ? 'coke' : 'beer'
puts "#{number.bottles.capitalize} of #{beverage} on the wall, #{number.bottles} of #{beverage}."
if number != 0
puts "Take one down, pass it around, #{(number - 1).bottles} of #{beverage} on the wall.\n\n"
else
puts "Go to the store and buy some more, 99 bottles of #{beverage} on the wall."
end
end
99.downto(0) { |number| sing(number) }
# Uncomment the following for the alternative teetotaller version
# 99.downto(0) { |number| sing(number, true) }
There are multiple Ruby versions uploaded there, but the default one is too complicated for beginners. This one, though, is very nice, and you should be able to understand what is going on.
The point that should answer your question is the bit with 99.downto(0) { |number| ... }. This is a loop that will repeat anything within the braces (in this case, sing(number)) a hundred times, with number going from 99 to 0.
Also note that it is inefficient (and illegible) to carry around the number as a string ((99).to_s) and convert it back to integer when you need it; rather, have it always be an integer, and convert it to string just before you need it as a string, when you display (or have the string concatenation/interpolation do it for you automatically, as in this piece of code).
While Ruby does have both for and while loops, they are rarely (while) or never (for) used. Instead, Rubyists usually rely on iterators and enumerators. Other functions like Integer#downto are Integer#upto, Integer#times and pretty much everything in the most awesome Enumerable mixin.
The short answer is yes, you can make a loop using what you wrote, and there are many ways of looping with ruby. However, since this seems to be about learning programming, considering that you did not use any control structures or string interpolation, not to mention casting that doesn't make much sense, I'd recommend Why's Poignant Guide to Ruby to learn the concepts of programming while using ruby.
As well as downto, you could do something like:
(1..99).reverse_each do |number|
bottle = number == 1 ? 'bottle' : 'bottles'
verse = "#{number} #{bottle} of beer on the wall, #{number} #{bottle} of beer. "
verse << "Take one down, pass it around, #{number-1} #{number-1 == 1 ? 'bottle' : 'bottles'} of beer on the wall"
puts verse
end
Things you may use to make your life easier: downto(), proc, ternary if (--that--thing--> ?:)
I enjoyed experiencing this exercise for the first time, so I'm a little hesitant about providing an answer here but so it goes.
It's a little bit more advanced, but using a 'proc' to make sure you pluralize "bottle(s)" correctly is a nice and clean way to get it done.
'downto()' is also an awesome way to iterate through those 99 bottles since it makes it feel like you're reading English instead of code.
num_at_start = 99 # You may change this number.
num_bottles = proc { |n| "#{n} bottle#{ n == 1 ? '' : 's'}" }
num_at_start.downto(1) do |num|
print "#{num_bottles.call(num)} of beer on the wall, " +
"#{num_bottles.call(num)} of beer!\n" +
"You take one down, pass it around, "
unless num == 1
puts "#{num_bottles.call(num - 1)} of beer on the wall!"
else
puts "No more bottles of beer on the wall!"
end
end
Source: Learn to Program 2nd Edition by Chris Pine (I changed a couple of things though)
This is the fix that I found for this question:
beer = 99
while beer > 0
puts beer.to_s + " bottles of beer on the wall. " + beer.to_s +
" bottles of beer."
(beer -= 1).to_s
puts "Take one down, pass it around. " + beer.to_s +
" bottles of beer on the wall."
end
Related
I was wondering if there is a way for me to avoid writing .to_s method on bottles_of_beer variable? (DRY principle)
bottles_of_beer = 99
while bottles_of_beer != 1
beer_left = bottles_of_beer - 1
bottles_of_beer = beer_left
puts bottles_of_beer.to_s + ' bottles of beer on the wall ' + bottles_of_beer.to_s + ' bottles of beer.'
puts 'Take one down and pass it around, ' + beer_left.to_s + ' of beer on the wall.'
end
if bottles_of_beer == 1
puts bottles_of_beer.to_s + ' bottle of beer on the wall, ' + bottles_of_beer.to_s + ' bottle of beer.'
puts 'Take one down and pass it around, no more bottles of beer on the wall.'
end
puts 'No more bottles of beer on the wall, no more bottles of beer.'
puts 'Go to the store and buy some more, 99 bottles of beer on the wall.'
I was wondering if there is a way for me to avoid writing .to_s method on bottles_of_beer variable?
Normally, you would use string interpolation for this, which will use to_s automatically, if necessary:
puts "#{bottles_of_beer} bottles of beer on the wall #{bottles_of_beer} bottles of beer."
By the way, your code would normally be implemented more like this:
puts 99.downto(2).map { |number_of_bottles|
"#{number_of_bottles} bottles of beer on the wall #{number_of_bottles} bottles of beer.
Take one down and pass it around, #{number_of_bottles - 1} of beer on the wall."
}
puts 'Take one down and pass it around, no more bottles of beer on the wall.'
puts 'No more bottles of beer on the wall, no more bottles of beer.'
puts 'Go to the store and buy some more, 99 bottles of beer on the wall.'
Starting from Jorg idea with interpolation but more of a functional approach, just for fun:
def bottles_song(n)
if n.zero?
puts "No more bottles of beer on the wall, no more bottles of beer!\nGo to the store and buy some more, #{n} bottles of beer on the wall.\n"
return
end
puts "#{n} #{ n != 1 ? 'bottles' : 'bottle' } of beer on the wall, #{n} #{ n != 1 ? 'bottles' : 'bottle' } of beer.\nTake one down, pass it around, #{n != 1 ? (n-1) : 'no more'} #{ n != 1 ? 'bottles' : 'bottle' } of beer on the wall!\n"
bottles_song(n-1)
end
Improved readability but it's taking other methods, not really an improvement :)
def bottles_song(n)
if n.zero?
puts "No more bottles of beer on the wall, no more bottles of beer!\nGo to the store and buy some more, #{n} bottles of beer on the wall.\n"
return
end
puts_bottles(n)
bottles_song(n-1)
end
def puts_bottles(n)
puts "#{n} #{ pluralize_bottle(n) } of beer on the wall, #{n} #{ pluralize_bottle(n) } of beer.\nTake one down, pass it around, #{n != 1 ? (n-1) : 'no more'} #{ pluralize_bottle(n-1) } of beer on the wall!\n"
end
def pluralize_bottle(n)
return 'bottles' if n != 1
'bottle'
end
I am working on the "Rosetta Code 100 Doors" problem and have hit a wall.
I found "100 doors help using Ruby" which was of some help, but I still can't get my code to work.
My toggle method doesn't work within my array iteration method.
def toggle(state)
if state == 'closed'
state.replace('open')
elsif state == 'open'
state.replace('closed')
end
end
def one_hundred_doors(array)
i = 0
100.times do
i += 1
array.each_with_index.map do |state, index|
if (index + 1) % i == 0
toggle(state)
end
end
end
array.each_with_index { |state, door| puts "Door #{door + 1} is #{state}." }
end
doors = Array.new(100, "closed")
one_hundred_doors(doors)
Can someone please explain what I am doing wrong?
Your problem is in your array creation method. You create it to contain 100 references to the same string:
doors = Array.new(100, "closed")
doors.first.replace("lala")
doors # => ["lala", "lala", ...]
but you need different strings.
Create it this way:
doors = 100.times.map{"closed"}
I am trying to create a method for the classic bottles of beers output.
Given a number, #beers, I want to convert it to the word version of the number. I wanted to split the number into an array and then compare with a list of numbers to words:
class BeerSong
attr_accessor :beers
def initialize(beers)
if beers > 99
beers = 99
elsif beers < 1
beers = "Zero"
end
#beers = beers
end
def worded
num_name = {
90 => "Ninety", 80 => "Eighty", 70 => "Seventy",60 => "Sixty",
50 => "Fifty",40 => "Forty",30 => "Thirty",20 => "Twenty",
19 => "Nineteen", 18 => "Eighteen", 17 => "Seventeen", 16 => "Sixteen",
15 => "Fifteen",14 => "Fourteen", 13 =>"Thirteen", 12 => "Twelve",
11 => "Eleven", 10 => "Ten", 9 => "Nine",8 => "Eight", 7 => "Seven",
6 => "Six",5 => "Five",4 => "Four",3 => "Three",2 => "Two",1 => "One"
}
worded = ""
beers = #beers
split_beers = beers.to_s.split
num_name.each do |number, name|
split_number = number.to_s.split
if beers == number
worded << name
else
number > 19 && split_number[0].to_i == split_beers[0].to_i
worded << name
worded << "-"
end
end
num_name.each do |number, name|
if number < 10 && split_beers[1].to_i == number
worded << name
end
end
worded
end
def print_song
while #beers.to_i > 2
puts "#{worded} bottles of beer on the wall,"
puts "#{worded} bottles of beer,"
puts "Take one down, pass it around,"
#beers -= 1
puts "#{worded} bottles of beer on the wall.\n\n"
end
if #beers.to_i == 2
puts "#{worded} bottles of beer on the wall,"
puts "#{worded} bottles of beer,"
puts "Take one down, pass it around,"
#beers -= 1
puts "#{worded} bottle of beer on the wall.\n\n"
end
if #beers.to_i == 1
puts "#{worded} bottle of beer on the wall,"
puts "#{worded} bottle of beer,"
puts "Take one down, pass it around,"
puts "Zero bottles of beer on the wall.\n\n"
end
if #beers.to_i == 0
print ""
end
end
end
I am trying to compare the first digit to get the tens, then compare the second digit for the units separated by a hyphen.
Your lines:
split_beers = beers.to_s.split
split_number = number.to_s.split
assume an array as a result which is not the case because you don't provide a pattern to split on
so the default is used. See: http://ruby-doc.org/core-2.0.0/String.html#method-i-split
puts "10".to_s.split.inspect # gives ==> ["10"]
puts "10".to_s.split('',2).inspect # gives ==> ["1", "0"]
The last is what you want.
I'm trying to solve a problem :
Your task is to write a simple function that takes a number of meters,
and outputs it using metric prefixes.For this exercise we just want
units bigger than a meter, from meters up to yottameters, excluding
decameters and hectometers.All values passed in will be positive
integers
Examples
meters(51500)
# returns "51.5km"
meters(5000000)
# returns "5Mm"
My code:
def meters(x)
map_prefix={ 24=>'Y', 21=> 'Z', 18=> 'E', 15=> 'P', 12=> 'T', 9=>'G', 6=>'M', 3=>'k',0=>'' }
digits=(x.to_i.to_s.size-1)/3
division=x/(10.0**(3*digits))
"#{division}#{map_prefix[3*digits]}m".sub(/\.0([^\d])/,'\1')
end
It doesn't work for meters(56*10**24) #->expected 56Ym ,instead got 56.000000000004Ym, but it works for bigger numbers such as meters(88*10**24) #->88Ym. The code passes 49 out of 50 tests, can someone help me find the error?
The easiest way to hack your code to get it working seems to avoid float-pointing number, like here:
#!/usr/bin/env ruby
def meters(x)
map_prefix={ 24=>'Y', 21=> 'Z', 18=> 'E', 15=> 'P', 12=> 'T', 9=>'G', 6=>'M', 3=>'k',0$
map_prefix.default = 'Y'
digits = [((x.to_s.size-1)/3)*3, 24].min
division = x.to_s.insert(-digits - 1, '.')
division.sub!(/0+\z/, '')
division.sub!(/\.\z/, '')
"#{division}#{map_prefix[digits]}m"
end
puts meters(51500)
puts meters(5000000)
puts meters(5001)
puts meters(88*10**24)
puts meters(88*10**24 + 1)
puts meters(100)
puts meters(88*10**27)
puts meters(88*10**27 + 1)
With results like:
./ruby.rb
51.5km
5Mm
5.001km
88Ym
88.000000000000000000000001Ym
100m
88000Ym
88000.000000000000000000000001Ym
More seriously, you need to avoid strings whatsoever (no conversions to string at all should be made).
You need arbitrary precision, so float is not an option at all.
I think your issue is that you are multiplying by 10.0, yet you only want to deal with integers.
Something like the following is what you want. (I'm also doing a couple style changes).
def meters(x)
digits=(x.to_i.to_s.size-1)/3
prefix = prefixes[3*digits]
value = x / (10 ** (3 * digits))
"#{value}#{prefix}m".sub(/\.0([^\d])/,'\1')
end
def prefixes
{
24 => 'Y',
21 => 'Z',
18 => 'E',
15 => 'P',
12 => 'T',
9 => 'G',
6 => 'M',
3 => 'k',
0 => ''
}
end
This at least gives the correct solution to the one that is wrong. I'm not going to guarantee it is the correct solution to everything.
I made the hash into its own function here because it seems like it would be static. As well was the other things I mentioned in my comment.
You can use Float#round to round the number to certain digits. For this certain problem, 3 should do fine.
"#{division.round(3)}#{map_prefix[3*digits]}m".sub(/\.0([^\d])/,'\1')
# ^^^^^^^^^
The reason behind the problem is: Float can store integers that's pretty big. However, for integers bigger than a certain limit, Float cannot store them precisely. For [IEEE-754 double precision] floating point, that limit is 253.
FWIW this line will change ruby's duck typing to float. (notice you're introducing 10.0 as a float.)
division=x/(10.0**(3*digits))
When dealing with large numbers, its best to use the built in BigDecimal class. Much cleaner and less error prone, although this is definitely NOT error proof code.
require 'bigdecimal'
def meters(x)
b = BigDecimal.new(x).split
"#{b[1]}#{prefix[b[3] - b[1].length]}m"
end
def prefix
{
24 =>'Y', 21 => 'Z', 18 => 'E', 15 => 'P',
12 => 'T', 9 =>'G', 6 =>'M', 3 =>'k',0 =>''
}
end
UNITS = " kMGTPEZY"
def meters(x)
sx = x.to_s
pwr = sx.size - 1
return sx if pwr < 3
pfx_sz = (pwr < 24) ? (1 + pwr % 3) : pwr - 23
sxs = sx.reverse.to_i.to_s.reverse
sxs = sxs.ljust([sxs.size, pfx_sz].max, '0')
pfx = sxs[0, pfx_sz]
pfx << '.' if (pfx.size < sxs.size)
"#{ pfx }#{ sxs[pfx_sz..-1] }#{ UNITS[[pwr/3, 8].min] }"
end
meters 3 #=> "3"
meters 100 #=> "100"
meters 4000 #=> "4k"
meters 5001 #=> "5.001k"
meters 51500 #=> "51.5k"
meters 5000000 #=> "5M"
meters 88*10**24 #=> "88Y"
meters 88*10**24 + 1 #=> "88.000000000000000000000001Y"
meters 88*10**27 #=> "88000Y"
meters 88*10**27 + 1 #=> "88000.000000000000000000000001Y"
def is_even?(n)
remainder_when_divided_by_2 = n % 2
if remainder_when_divided_by_2 == 0
return true
else
return false
end
end
def is_odd?(n)
return ! is_even?(n)
end
puts "1 is_even? #{is_even?(1)} - is_odd? #{is_odd?(1)}"
puts "2 is_even? #{is_even?(2)} - is_odd? #{is_odd?(2)}"
puts "3 is_even? #{is_even?(3)} - is_odd? #{is_odd?(3)}"
puts "4 is_even? #{is_even?(4)} - is_odd? #{is_odd?(4)}"
puts "5 is_even? #{is_even?(5)} - is_odd? #{is_odd?(5)}"
puts "6 is_even? #{is_even?(6)} - is_odd? #{is_odd?(6)}"
def is_even_and_divisible_by_five?(n)
remainder_when_divided_by_five = n % 5
if (remainder_when_divided_by_five == 0) && (is_even?(n) == true)
return true
else
return false
end
end
puts "5 is_even_and_divisible_by_five? #{is_even_and_divisible_by_five?(5)}"
puts "10 is_even_and_divisible_by_five? #{is_even_and_divisible_by_five?(10)}"
puts "15 is_even_and_divisible_by_five? #{is_even_and_divisible_by_five?(15)}"
puts "20 is_even_and_divisible_by_five? #{is_even_and_divisible_by_five?(20)}"
puts "25 is_even_and_divisible_by_five? #{is_even_and_divisible_by_five?(25)}"
puts "30 is_even_and_divisible_by_five? #{is_even_and_divisible_by_five?(30)}"
The problem was I had not called the method is_even_and_divisible_by_five in the puts commands at the bottom of the code. I called it is_even_and_divisble_by_5. Then in the if statement in the is_even_and_divisble_by_five method, I left of the (n) arguement from Is_even. Thank you all very much!
Even (divisible by two) and divisible by five also means "divisible by ten":
def is_even_and_divisible_by_five?(n)
n % 10 == 0
end
You called
is_even_and_divisible_by_5?
instead of
is_even_and_divisible_by_five?
Also is_even? function is undefined. I guess there was some mistake made with its defining or maybe even not-defining. So maybe when you defined is_even_and_divisible_by_five?(n) function there were some other errors and It was not defined too. Plus I think here is much easier solution:
def is_even_and_divisible_by_five?(n)
n % 5 == 0 && n.even?
end
In Ruby You don't have to use return all the time. You should use it quite rarely. The reason is ruby functions return last calculated value by default. And nearly everything is returning value in ruby, even blocks and If-Else statements. If you open irb console and try to do some code, for example:
a = 5
=> 5
Second line is what first line returns. You can do some experiments like this by your own with any type of conditions you like.
The name of your method is is_even_and_divisible_by_five?, not is_even_and_divisible_by_5?.
is_even? is not defined by itself
Here a shorter version of your method
def is_even_and_divisible_by_five? n
0 == n % 5 + n % 2
end