[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

recursion in scripts -- global variables required?

Eric Armstrong

7/20/2006 7:04:00 PM

Is it me, or is it pretty much impossible
to write a recursive script in ruby unless
you use global variables?

I have a simple countdown timer. I
set up the number of seconds to sleep,
sleep for a while, and then beep.

A single run is easily done in a script
(code below). The problems start when
I define a run method and recurse.

Unless I use global variables, there
doesn't seem to be any way for the
variables to get the data.

Is that about it?


Initial Script
--------------
#!/usr/bin/env ruby

# Beeps and character I/O
require 'curses'
include Curses

@seconds = 10
@minutes = 0
@hours = 0

#def run
timeremaining = (3600 * @hours)
+ (60 * @minutes)
+ @seconds
puts "sleeping for " + time_remaining.to_s
timeremaining.downto(0) do |i|
sleep(1)
print i.to_s + ".."
end
puts
beep; beep; beep
sleep 1
beep; beep; beep
sleep 1
beep; beep; beep

# run
#end

#run




9 Answers

Matthew Smillie

7/20/2006 7:32:00 PM

0

On Jul 20, 2006, at 20:03, Eric Armstrong wrote:

> Is it me, or is it pretty much impossible
> to write a recursive script in ruby unless
> you use global variables?
>
> I have a simple countdown timer. I
> set up the number of seconds to sleep,
> sleep for a while, and then beep.
>
> A single run is easily done in a script
> (code below). The problems start when
> I define a run method and recurse.
>
> Unless I use global variables, there
> doesn't seem to be any way for the
> variables to get the data.
>
> Is that about it?

Well, in this case, if you're using global variables, there's not
much of a point to making the method recursive (which stores all of
its local variables on the stack). The typical way to do recursive
method is to pass the relevant information as parameters, e.g.,

def countdown(seconds)
sleep(1)
puts "tick"
sleep(1)
puts "tock"
countdown(seconds - 2)
end

I'll also mention that a deep recursion like this is a horribly
inefficient way to do a countdown timer.

matthew smillie

>
>
> Initial Script
> --------------
> #!/usr/bin/env ruby
>
> # Beeps and character I/O
> require 'curses'
> include Curses
>
> @seconds = 10
> @minutes = 0
> @hours = 0
>
> #def run
> timeremaining = (3600 * @hours)
> + (60 * @minutes)
> + @seconds
> puts "sleeping for " + time_remaining.to_s
> timeremaining.downto(0) do |i|
> sleep(1)
> print i.to_s + ".."
> end
> puts
> beep; beep; beep
> sleep 1
> beep; beep; beep
> sleep 1
> beep; beep; beep
>
> # run
> #end
>
> #run
>
>
>
>


Eric Armstrong

7/20/2006 9:43:00 PM

0

There is no inefficency here. Recursion isn't
being used to count down. That's in a loop.

Recursion is only being used to restart the
timer after it expires. No stack space is
required. Any sufficiently optimizing compiler
will take the stack out of the equation.
(Whether ruby does so is another matter.)

The question is really about the nature of
instance variables in a script, I guess.

It seems they can only be initialized in a
method. (Something I keep forgetting. I'm
/so/ used to intializing instance variables
in Java.)

Defining initialize() didn't work, because
the Object object the variables belong to
in a script is already created when that
definition is processed. So it never runs.

The obvious solution (which occurs to me only
now) is to create an init() method, and
invoke it before the first call to run().

As the mathematician said...

"It is now obvious that...wait a minute...
let me check that...(works furiously)...
Yes! It /is/ obvious..."


Matthew Smillie wrote:
> On Jul 20, 2006, at 20:03, Eric Armstrong wrote:
>
>> Is it me, or is it pretty much impossible
>> to write a recursive script in ruby unless
>> you use global variables?
>>
>> I have a simple countdown timer. I
>> set up the number of seconds to sleep,
>> sleep for a while, and then beep.
>>
>> A single run is easily done in a script
>> (code below). The problems start when
>> I define a run method and recurse.
>>
>> Unless I use global variables, there
>> doesn't seem to be any way for the
>> variables to get the data.
>>
>> Is that about it?
>
> Well, in this case, if you're using global variables, there's not much
> of a point to making the method recursive (which stores all of its local
> variables on the stack). The typical way to do recursive method is to
> pass the relevant information as parameters, e.g.,
>
> def countdown(seconds)
> sleep(1)
> puts "tick"
> sleep(1)
> puts "tock"
> countdown(seconds - 2)
> end
>
> I'll also mention that a deep recursion like this is a horribly
> inefficient way to do a countdown timer.
>
> matthew smillie
>
>>
>>
>> Initial Script
>> --------------
>> #!/usr/bin/env ruby
>>
>> # Beeps and character I/O
>> require 'curses'
>> include Curses
>>
>> @seconds = 10
>> @minutes = 0
>> @hours = 0
>>
>> #def run
>> timeremaining = (3600 * @hours)
>> + (60 * @minutes)
>> + @seconds
>> puts "sleeping for " + time_remaining.to_s
>> timeremaining.downto(0) do |i|
>> sleep(1)
>> print i.to_s + ".."
>> end
>> puts
>> beep; beep; beep
>> sleep 1
>> beep; beep; beep
>> sleep 1
>> beep; beep; beep
>>
>> # run
>> #end
>>
>> #run
>>
>>
>>
>>
>
>

Eric Armstrong

7/20/2006 10:20:00 PM

0

Doh! "while true" makes a lot more sense than
recursion, doesn't it?

Anybody have a sledgehammer? I see a fly...

Eric Armstrong wrote:
> There is no inefficency here. Recursion isn't
> being used to count down. That's in a loop.
>
> Recursion is only being used to restart the
> timer after it expires. No stack space is
> required. Any sufficiently optimizing compiler
> will take the stack out of the equation.
> (Whether ruby does so is another matter.)
>
> The question is really about the nature of
> instance variables in a script, I guess.
>
> It seems they can only be initialized in a
> method. (Something I keep forgetting. I'm
> /so/ used to intializing instance variables
> in Java.)
>
> Defining initialize() didn't work, because
> the Object object the variables belong to
> in a script is already created when that
> definition is processed. So it never runs.
>
> The obvious solution (which occurs to me only
> now) is to create an init() method, and
> invoke it before the first call to run().
>
> As the mathematician said...
>
> "It is now obvious that...wait a minute...
> let me check that...(works furiously)...
> Yes! It /is/ obvious..."
>
>
> Matthew Smillie wrote:
>> On Jul 20, 2006, at 20:03, Eric Armstrong wrote:
>>
>>> Is it me, or is it pretty much impossible
>>> to write a recursive script in ruby unless
>>> you use global variables?
>>>
>>> I have a simple countdown timer. I
>>> set up the number of seconds to sleep,
>>> sleep for a while, and then beep.
>>>
>>> A single run is easily done in a script
>>> (code below). The problems start when
>>> I define a run method and recurse.
>>>
>>> Unless I use global variables, there
>>> doesn't seem to be any way for the
>>> variables to get the data.
>>>
>>> Is that about it?
>>
>> Well, in this case, if you're using global variables, there's not much
>> of a point to making the method recursive (which stores all of its
>> local variables on the stack). The typical way to do recursive method
>> is to pass the relevant information as parameters, e.g.,
>>
>> def countdown(seconds)
>> sleep(1)
>> puts "tick"
>> sleep(1)
>> puts "tock"
>> countdown(seconds - 2)
>> end
>>
>> I'll also mention that a deep recursion like this is a horribly
>> inefficient way to do a countdown timer.
>>
>> matthew smillie
>>
>>>
>>>
>>> Initial Script
>>> --------------
>>> #!/usr/bin/env ruby
>>>
>>> # Beeps and character I/O
>>> require 'curses'
>>> include Curses
>>>
>>> @seconds = 10
>>> @minutes = 0
>>> @hours = 0
>>>
>>> #def run
>>> timeremaining = (3600 * @hours)
>>> + (60 * @minutes)
>>> + @seconds
>>> puts "sleeping for " + time_remaining.to_s
>>> timeremaining.downto(0) do |i|
>>> sleep(1)
>>> print i.to_s + ".."
>>> end
>>> puts
>>> beep; beep; beep
>>> sleep 1
>>> beep; beep; beep
>>> sleep 1
>>> beep; beep; beep
>>>
>>> # run
>>> #end
>>>
>>> #run
>>>
>>>
>>>
>>>
>>
>>
>

Justin Collins

7/20/2006 10:23:00 PM

0

Eric Armstrong wrote:
> There is no inefficency here. Recursion isn't
> being used to count down. That's in a loop.
>
> Recursion is only being used to restart the
> timer after it expires. No stack space is
> required. Any sufficiently optimizing compiler
> will take the stack out of the equation.
> (Whether ruby does so is another matter.)

It doesn't, sadly. Soon, hopefully.

>
> The question is really about the nature of
> instance variables in a script, I guess.
>
> It seems they can only be initialized in a
> method. (Something I keep forgetting. I'm
> /so/ used to intializing instance variables
> in Java.)
>
> Defining initialize() didn't work, because
> the Object object the variables belong to
> in a script is already created when that
> definition is processed. So it never runs.
>
> The obvious solution (which occurs to me only
> now) is to create an init() method, and
> invoke it before the first call to run().
>

I would think, given Ruby's nature, that the solution would be to create
a timer object. ;)

class MyTimer

def initialize(hours, minutes, seconds)
@hours = hours
@minutes = minutes
@seconds = seconds
end

#Rather than recursion, which in Ruby
#will eventually run out of stack space
def start
loop do
run
end
end

#Using nearly your exact same method
def run
#Won't see count down until the end without this
$stdout.sync = true

#Your line breaks meant that time_remaining was always zero
time_remaining = (3600 * @hours) + (60 * @minutes) +
@seconds
puts "sleeping for " + time_remaining.to_s
time_remaining.downto(0) do |i|
sleep(1)
print i.to_s + ".."
end
puts
beep; beep; beep
sleep 1
beep; beep; beep
sleep 1
beep; beep; beep
end
end

timer = MyTimer.new(0,0,15)
timer.start


Maybe that helps...

-Justin

> As the mathematician said...
>
> "It is now obvious that...wait a minute...
> let me check that...(works furiously)...
> Yes! It /is/ obvious..."
>
>
> Matthew Smillie wrote:
>> On Jul 20, 2006, at 20:03, Eric Armstrong wrote:
>>
>>> Is it me, or is it pretty much impossible
>>> to write a recursive script in ruby unless
>>> you use global variables?
>>>
>>> I have a simple countdown timer. I
>>> set up the number of seconds to sleep,
>>> sleep for a while, and then beep.
>>>
>>> A single run is easily done in a script
>>> (code below). The problems start when
>>> I define a run method and recurse.
>>>
>>> Unless I use global variables, there
>>> doesn't seem to be any way for the
>>> variables to get the data.
>>>
>>> Is that about it?
>>
>> Well, in this case, if you're using global variables, there's not
>> much of a point to making the method recursive (which stores all of
>> its local variables on the stack). The typical way to do recursive
>> method is to pass the relevant information as parameters, e.g.,
>>
>> def countdown(seconds)
>> sleep(1)
>> puts "tick"
>> sleep(1)
>> puts "tock"
>> countdown(seconds - 2)
>> end
>>
>> I'll also mention that a deep recursion like this is a horribly
>> inefficient way to do a countdown timer.
>>
>> matthew smillie
>>
>>>
>>>
>>> Initial Script
>>> --------------
>>> #!/usr/bin/env ruby
>>>
>>> # Beeps and character I/O
>>> require 'curses'
>>> include Curses
>>>
>>> @seconds = 10
>>> @minutes = 0
>>> @hours = 0
>>>
>>> #def run
>>> timeremaining = (3600 * @hours)
>>> + (60 * @minutes)
>>> + @seconds
>>> puts "sleeping for " + time_remaining.to_s
>>> timeremaining.downto(0) do |i|
>>> sleep(1)
>>> print i.to_s + ".."
>>> end
>>> puts
>>> beep; beep; beep
>>> sleep 1
>>> beep; beep; beep
>>> sleep 1
>>> beep; beep; beep
>>>
>>> # run
>>> #end
>>>
>>> #run
>>>
>>>
>>>
>>>
>>
>>

Sean O'Halpin

7/21/2006 1:22:00 AM

0

On 7/20/06, Eric Armstrong <Eric.Armstrong@sun.com> wrote:

> Recursion is only being used to restart the
> timer after it expires. No stack space is
> required. Any sufficiently optimizing compiler
> will take the stack out of the equation.
> (Whether ruby does so is another matter.)

It doesn't - you'd quickly run out of stack space.

>
> The question is really about the nature of
> instance variables in a script, I guess.
>
> It seems they can only be initialized in a
> method.

Not so. Try this:

@foo = 1
def bar
p @foo
end

bar
#=> 1

Regards,
Sean

Eric Armstrong

7/21/2006 2:06:00 AM

0

Sean O'Halpin wrote:
> On 7/20/06, Eric Armstrong <Eric.Armstrong@sun.com> wrote:
>
>> The question is really about the nature of
>> instance variables in a script, I guess.
>> It seems they can only be initialized in a
>> method.
>
> Not so. Try this:
>
> @foo = 1
> def bar
> p @foo
> end
>
> bar
> #=> 1
>
I must be going out of my mind. I can't
tell you how many times that has seemed
to fail... At the moment, of course, it's
working fine. So either something is
failing in some strange intermittent way
(unlikely) or something is confusing the
heck out of me (very likely).



Gary Wright

7/21/2006 5:13:00 AM

0


On Jul 20, 2006, at 10:05 PM, Eric Armstrong wrote:
> Sean O'Halpin wrote:
>> On 7/20/06, Eric Armstrong <Eric.Armstrong@sun.com> wrote:
>>> The question is really about the nature of
>>> instance variables in a script, I guess.
>>> It seems they can only be initialized in a
>>> method.
>> Not so. Try this:
>> @foo = 1
>> def bar
>> p @foo
>> end
>> bar
>> #=> 1
> I must be going out of my mind. I can't
> tell you how many times that has seemed
> to fail... At the moment, of course, it's
> working fine. So either something is
> failing in some strange intermittent way
> (unlikely) or something is confusing the
> heck out of me (very likely).

Be careful here. The top-level scope in Ruby is not the same thing
as class level scope:

p self # 1) top_level object
def foo
p self # 2) top_level object
end
foo

class A
p self # 3) the class object, A
def a_foo
p self # 4) an instance of A
end
end
A.new.a_foo

So instance variables in scopes 1 and 2 are actually associated with
the same object (the top_level object) while instance variables in
3 and 4 are associated with different objects (the class A and an
instance of the class A).

Gary Wright




Eric Armstrong

7/22/2006 1:06:00 PM

0

Justin Collins wrote:
> Eric Armstrong wrote:
>
>> Recursion is only being used to restart the
>> timer after it expires. No stack space is
>> required. Any sufficiently optimizing compiler
>> will take the stack out of the equation.
>> (Whether ruby does so is another matter.)
>
> It doesn't, sadly. Soon, hopefully.
>
Good to know. Thanks for the info.

Nice O-O solution below, too. (Too much scripting
in my blood.)

> I would think, given Ruby's nature, that the solution would be to create
> a timer object. ;)
>
> class MyTimer
>
> def initialize(hours, minutes, seconds)
> @hours = hours
> @minutes = minutes
> @seconds = seconds
> end
>
> #Rather than recursion, which in Ruby
> #will eventually run out of stack space
> def start
> loop do
> run
> end
> end
>
> #Using nearly your exact same method
> def run
> #Won't see count down until the end without this
> $stdout.sync = true
> #Your line breaks meant that
> time_remaining was always zero
> time_remaining = (3600 * @hours) + (60 * @minutes) +
> @seconds
> puts "sleeping for " + time_remaining.to_s
> time_remaining.downto(0) do |i|
> sleep(1)
> print i.to_s + ".."
> end
> puts
> beep; beep; beep
> sleep 1
> beep; beep; beep
> sleep 1
> beep; beep; beep
> end
> end
>
> timer = MyTimer.new(0,0,15)
> timer.start
>
>
> Maybe that helps...
>
> -Justin
>
>> As the mathematician said...
>>
>> "It is now obvious that...wait a minute...
>> let me check that...(works furiously)...
>> Yes! It /is/ obvious..."
>>
>>
>> Matthew Smillie wrote:
>>> On Jul 20, 2006, at 20:03, Eric Armstrong wrote:
>>>
>>>> Is it me, or is it pretty much impossible
>>>> to write a recursive script in ruby unless
>>>> you use global variables?
>>>>
>>>> I have a simple countdown timer. I
>>>> set up the number of seconds to sleep,
>>>> sleep for a while, and then beep.
>>>>
>>>> A single run is easily done in a script
>>>> (code below). The problems start when
>>>> I define a run method and recurse.
>>>>
>>>> Unless I use global variables, there
>>>> doesn't seem to be any way for the
>>>> variables to get the data.
>>>>
>>>> Is that about it?
>>>
>>> Well, in this case, if you're using global variables, there's not
>>> much of a point to making the method recursive (which stores all of
>>> its local variables on the stack). The typical way to do recursive
>>> method is to pass the relevant information as parameters, e.g.,
>>>
>>> def countdown(seconds)
>>> sleep(1)
>>> puts "tick"
>>> sleep(1)
>>> puts "tock"
>>> countdown(seconds - 2)
>>> end
>>>
>>> I'll also mention that a deep recursion like this is a horribly
>>> inefficient way to do a countdown timer.
>>>
>>> matthew smillie
>>>
>>>>
>>>>
>>>> Initial Script
>>>> --------------
>>>> #!/usr/bin/env ruby
>>>>
>>>> # Beeps and character I/O
>>>> require 'curses'
>>>> include Curses
>>>>
>>>> @seconds = 10
>>>> @minutes = 0
>>>> @hours = 0
>>>>
>>>> #def run
>>>> timeremaining = (3600 * @hours)
>>>> + (60 * @minutes)
>>>> + @seconds
>>>> puts "sleeping for " + time_remaining.to_s
>>>> timeremaining.downto(0) do |i|
>>>> sleep(1)
>>>> print i.to_s + ".."
>>>> end
>>>> puts
>>>> beep; beep; beep
>>>> sleep 1
>>>> beep; beep; beep
>>>> sleep 1
>>>> beep; beep; beep
>>>>
>>>> # run
>>>> #end
>>>>
>>>> #run
>>>>
>>>>
>>>>
>>>>
>>>
>>>
>


Eric Armstrong

7/22/2006 1:08:00 PM

0

That's IT. That's precisely what confuses me. The same
surface syntax works completely differently in the two
different settings. It only makes sense when you
understand the deeper model -- and I CONTINUALLY forget
that initializing a variable in a class definition only
applies to the class objects, rather than instance
objects.

The same thing came just last week in another way,
in fact. It only took a week to forget again!
Thanks for the reminder.

(But I wonder if there is a way to make things less
surprising.)

gwtmp01@mac.com wrote:
>
> On Jul 20, 2006, at 10:05 PM, Eric Armstrong wrote:
>>> @foo = 1
>>> def bar
>>> p @foo
>>> end
>>> bar
>>> #=> 1
>> I must be going out of my mind. I can't
>> tell you how many times that has seemed
>> to fail... At the moment, of course, it's
>> working fine. So either something is
>> failing in some strange intermittent way
>> (unlikely) or something is confusing the
>> heck out of me (very likely).
>
> Be careful here. The top-level scope in Ruby is not the same thing
> as class level scope:
>
> p self # 1) top_level object
> def foo
> p self # 2) top_level object
> end
> foo
>
> class A
> p self # 3) the class object, A
> def a_foo
> p self # 4) an instance of A
> end
> end
> A.new.a_foo
>
> So instance variables in scopes 1 and 2 are actually associated with
> the same object (the top_level object) while instance variables in
> 3 and 4 are associated with different objects (the class A and an
> instance of the class A).
>
> Gary Wright
>
>
>
>