# Simple loops and recursion

Hi,

I’m a total newbie, and this is my very first message. I’m going
through Chris P.'s Learn to Program; almost half done.

Problem: Consider the following working code:

===============
def repeat type
number_of_bottles = ‘99’
while number_of_bottles != ‘0’
puts number_of_bottles.to_s + ’ bottles:’ + ’ If one falls, ’ +
(number_of_bottles.to_i - 1).to_s + ’ bottles.’
type + (number_of_bottles.to_i - 1).to_s + ‘:’
number_of_bottles = gets.chomp
end
end

# repeat 'Type ’

I want to add a recursion conditional: If there are 98 bottles left,
one should type ‘98’; if there are 97, one should type ‘97’ etc. At 0
bottles the programs ends. What I want is an additional conditional
recursion, so that e.g. if, at 98 bottles, I type anything other than
‘98’, the program recursively tells me to type ‘98’ until I type ‘98’.

Every way I’ve tried this gives me a " undefined local variable or
method `number_of_bottles’ " type of error, or else the program keeps
going back to 99.

Idris

def repeat type
number_of_bottles = ‘99’
while number_of_bottles != ‘0’
puts number_of_bottles.to_s + ’ bottles:’ + ’ If one falls, ’ +
(number_of_bottles.to_i - 1).to_s + ’ bottles.’
type + (number_of_bottles.to_i - 1).to_s + ‘:’
number_of_bottles = gets.chomp
end
end

# repeat 'Type ’

I want to add a recursion conditional: If there are 98 bottles left,

First of all, use of the term “recursion” is not correct here. Recursion
occurs when a function calls itself. The code above is iterative, not
recursive.

For example (this can be written more tersely but I am trying to
illustrate something clearly):

def factorial(n)
return 1 if n <= 1 # Termination condition
return n * factorial(n - 1) # Recursive call
end

factorial(6) => 720

The same thing iteratively is

def factorial(n)
fact = 1
n.downto(1) { |i| fact *= i }
fact
end

Second of all, your code uses strings where it should use integers.
Writing

while number_of_bottles != ‘0’

is doing a string comparison. ‘0’ is a 1-character string consisting of
the character ‘0’. You really want an integer.

So, rewriting your code to be more Rubyish,

def repeat(type)
number_of_bottles = 99
while number_of_bottles != 0
puts “#{number_of_bottles} bottles: If one falls,
#{number_of_bottles - 1} bottles.”
puts “#{type}#{number_of_bottles - 1}:”
number_of_bottles = gets.chomp.to_i
end
end

Now, to ensure that someone types in what you want, you should create a
simple function to get what you are looking for, e.g.

def expect(msg, expected_value)
puts “#{msg}#{expected_value}:”
loop do
actual_value = gets.chomp.to_i
return actual_value if actual_value == expected_value
puts “Sorry, expected #{expected_value} but got #{actual_value}”
end
end

Note that this function is not perfect; it does not check to see if an
actual integer was entered, so if people enter non-digits they will be
seen as 0 values. I leave this as a exercise for the reader…

Anyway, we now change the original function to the following:

def repeat(msg)
number_of_bottles = 99
while number_of_bottles != 0 do
puts “#{number_of_bottles} bottles: If one falls,
#{number_of_bottles - 1} bottles.”
number_of_bottles = expect(msg, number_of_bottles - 1)
end
end

Hi –

On Mon, 27 Nov 2006, ishamid wrote:

while number_of_bottles != ‘0’
I want to add a recursion conditional: If there are 98 bottles left,
one should type ‘98’; if there are 97, one should type ‘97’ etc. At 0
bottles the programs ends. What I want is an additional conditional
recursion, so that e.g. if, at 98 bottles, I type anything other than
‘98’, the program recursively tells me to type ‘98’ until I type ‘98’.

Every way I’ve tried this gives me a " undefined local variable or
method `number_of_bottles’ " type of error, or else the program keeps
going back to 99.

Here’s a rewrite that might give you some ideas.

def repeat(prompt)
bottles = 99
until bottles.zero?
expected = bottles-1
puts “#{bottles} bottles: If one falls, #{expected} bottles.”
print "#{prompt} #{expected}: "
input = gets.chomp
until input == expected.to_s
print "Wrong! Try again: "
input = gets.chomp
end
bottles = input.to_i
end
end

repeat(“Type”)

David

1. The recursive factorial was easy to follow; the iterative example
less so (still have a lot to learn). I will study both examples
carefully and I really appreciate the lesson!

2. (Keep in my mind my only previous experience programming is in TeX,
and not at a low level). For your second point, what makes code
“Rubyish”. What benchmarks are there?

3. I will study your solution carefully. About books, etc.: My plan was
to get through Chris P. (Learn to Program), then go through Mark
Slagell (Ruby in 21 Days), then plow through the PickAxe. Any
suggestions in this regard?

Idris

An ambiguity:

I find it interesting that, in its output, Ruby (and I guess other
programming languages as well) have no convention for the use-mention
distinction. For example:

===================
def expect(msg, expected_value)
puts “#{msg}#{expected_value}:”
loop do
actual_value = gets.chomp.to_i
return actual_value if actual_value == expected_value
puts “Sorry, expected #{expected_value} but got #{actual_value}”
end
end

gives me

# =================== Sorry, expected 98 but got 98

Of course, the problem is that I entered a string not an integer.

The output does not distinguish number from numeral. Is this standard
or is there something else going on?

Best
Idris

actual integer was entered, so if people enter non-digits they will be
seen as 0 values. I leave this as a exercise for the reader…

Is this too naive/non-Rubyish for what you had in mind? I just moved
the .to_i; it seems to work…

===========
def expect(msg, expected_value)
puts “#{msg}#{expected_value}:”
loop do
actual_value = gets.chomp
return actual_value if actual_value.to_i == expected_value
puts “Sorry, expected #{expected_value} but got #{actual_value}”
end
end

# expect('We want ', 98)

Thank you again

Idris

On 11/28/06, ishamid [email protected] wrote:

``````puts "Sorry, expected #{expected_value} but got #{actual_value}"
``````

[…]

gives me

# =================== Sorry, expected 98 but got 98

Of course, the problem is that I entered a string not an integer.

The output does not distinguish number from numeral. Is this standard
or is there something else going on?

When you interpolate an expression in a string, ruby first evaluates
it, then calls to_s on the result. 98.to_s = “98”

Here’s an illustrative example - I’ll define a class and give it a
to_s method, then create an object of that class and embed it inside a
string:

irb(main):001:0> class MyClass
irb(main):002:1> def to_s
irb(main):003:2> “hello world”
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> a = MyClass.new
=> hello world
irb(main):007:0> puts “the language that cannot print ‘#{a}’ is not
the true language”
the language that cannot print ‘hello world’ is not the true language
=> nil

martin

I leave this as a exercise for the reader…

Ok, here is the solution to the exercise

==============
def expect(msg, expected_value)
puts “#{msg}#{expected_value}:”
loop do
actual_value = gets.chomp
return actual_value.to_i if actual_value.to_i == expected_value
puts “Sorry, expected #{expected_value} but got #{actual_value}”
end
end

# expect('We want ', 98) # test

def repeat(msg)
number_of_bottles = 99
while number_of_bottles != 0 do
puts “#{number_of_bottles} bottles: If one falls,
#{number_of_bottles - 1} bottles.”
number_of_bottles = expect(msg, number_of_bottles - 1)
end
end

# repeat('We want ')

If I input a string, it comes back as a string, and everything else
works too. Thank you again for this lesson!

Now I will study David’s approach…

Thnx again and
Best
Idris

Try learning ruby in your browser with a live Ruby interpreter:
http://tryruby.hobix.com/

Start by typing “help” on the Ruby command line.
It’s a lotta fun.

Don’t let it sit idle for more than 10 minutes between chapters
or you’ll need to start the chapter over from scratch.

If you stop, to move to chapter 2, type “help 2”, to chapter 3,
type “help 3” and so on.

Took me a little over a half hour to go through the lessons.

-Doug

I can only comment on point 3, as I’m doing the same thing you are
(although
I might be a page or two ahead of you). I basically did things in the
same
order - I did the Chris P. thing (a while ago) via his web page (I
didn’t
realize it was also a book until recently), then I read through most of
why’s poignant guide to ruby(http://poignantguide.net/ruby/), then
started
reading “Ruby in 21 Days”. I keep hoping one day that the regex stuff
on
day 8 will make sense. I also started Agile Web D. With Rails
and
now I’m back to ruby in 21 days. I also have the pickaxe as a reference
when I want to look over stuff. For what it’s worth, I think the order
in
which you’re reading the books is very good. That said, I still suck
but of
course YMMV