Recursion in scripts -- global variables required?

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

On Jul 20, 2006, at 20:03, Eric A. wrote:

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

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…”

Eric A. 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 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. :wink:

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

On 7/20/06, Eric A. [email protected] 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

Doh! “while true” makes a lot more sense than
recursion, doesn’t it?

Anybody have a sledgehammer? I see a fly…

Sean O’Halpin wrote:

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

On Jul 20, 2006, at 10:05 PM, Eric A. wrote:

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

Justin C. wrote:

Eric A. 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.)

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