simple loops and recursion

I

ishamid

Hi,

I'm a total newbie, and this is my very first message. I'm going
through Chris Pine'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.

Thanks in advance
Idris
 
E

Edwin Fine

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

Hope this helps. I recommend buying and reading Pragmatic Programming in
Ruby. It will answer a lot of your Ruby questions.
 
D

dblack

Hi --

Hi,

I'm a total newbie, and this is my very first message. I'm going
through Chris Pine'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.

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

--
David A. Black | (e-mail address removed)
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] http://www.manning.com/black | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org
 
I

ishamid

Thank you very much, Edwin, for your comments.

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 Pine (Learn to Program), then go through Mark
Slagell (Ruby in 21 Days), then plow through the PickAxe. Any
suggestions in this regard?

Thanks again for your help.

Idris
 
I

ishamid

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

expect('We want ', '98')
===================

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
 
I

ishamid

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...

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
 
I

ishamid

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...

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
 
M

Martin DeMello

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

ishamid

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
 
D

Doug Kramer

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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top