469,362 Members | 2,353 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,362 developers. It's quick & easy.

Recursive calls and stack

Hi,
I have a program which literately finds the object that overlapping a
point. The horizontal and vertical search are called recursively from
inside each other.
Is this way of implementation fill the stack space with the local
variables inside each call. If this is not good, is there a better way
to implement? Or python itself will understand that the calls happen
in the last line, so local variables need not be pushed into the
stack?

def find_point(pt):
return _hor_search(pt, random_obj)

def _hor_search(pt, obj):
...
object = ...
...
if object meets some condition:
return object
else:
return _ver_search(pt, object)

def _ver_search(pt, obj):
...
object = ...
...
if object meets some condition:
return object
else:
return _hor_search(pt, object)
-
Suresh

Feb 14 '07 #1
13 1903
En Wed, 14 Feb 2007 03:09:37 -0300, jm*******@no.spam.gmail.com
<jm*******@gmail.comescribió:
Hi,
I have a program which literately finds the object that overlapping a
point. The horizontal and vertical search are called recursively from
inside each other.
Is this way of implementation fill the stack space with the local
variables inside each call. If this is not good, is there a better way
to implement? Or python itself will understand that the calls happen
in the last line, so local variables need not be pushed into the
stack?
I'm afraid not, the calls will be stacked until some object is found.
Python does not do "tail recursion optimization" (at least, I'm not aware
of that). But even if it could do that, in this case you have recursive
calls between two functions, and that's a bit harder.

Going back to your original problem, maybe you can use some known
algorithms from computational geometry; start with
http://www.faqs.org/faqs/graphics/algorithms-faq/

--
Gabriel Genellina

Feb 14 '07 #2
On Feb 14, 11:45 am, "Gabriel Genellina" <gagsl...@yahoo.com.ar>
wrote:
En Wed, 14 Feb 2007 03:09:37 -0300, jm.sur...@no.spam.gmail.com
<jm.sur...@gmail.comescribió:
Hi,
I have a program which literately finds the object that overlapping a
point. The horizontal and vertical search are called recursively from
inside each other.
Is this way of implementation fill the stack space with the local
variables inside each call. If this is not good, is there a better way
to implement? Or python itself will understand that the calls happen
in the last line, so local variables need not be pushed into the
stack?

I'm afraid not, the calls will be stacked until some object is found.
Python does not do "tail recursion optimization" (at least, I'm not aware
of that). But even if it could do that, in this case you have recursive
calls between two functions, and that's a bit harder.

Going back to your original problem, maybe you can use some known
algorithms from computational geometry; start with http://www.faqs.org/faqs/graphics/algorithms-faq/

--
Gabriel Genellina
Thanks Gabriel for the response.

I am OK with calls being stacked, but I wondering will the local
variables be stacked given that return statement is followed by the
function call?

def test():
x = 22
y = 33
z = x+y
return anotherFunction(z)

In this function will all the local variables (x,y,z) be pushed into
the stack before calling anotherFunction(z) or Python will find out
that the locals are no longer needed as anotherFunction(z) is just
returned?

-
Suresh

Feb 14 '07 #3
On Feb 14, 11:09 am, "jm.sur...@no.spam.gmail.com"
<jm.sur...@gmail.comwrote:
Hi,
I have a program which literately finds the object that overlapping a
point. The horizontal and vertical search are called recursively from
inside each other.
Is this way of implementation fill thestackspace with the local
variables inside eachcall. If this is not good, is there a better way
to implement? Orpythonitself will understand that the calls happen
in the last line, so local variables need not be pushed into thestack?

def find_point(pt):
return _hor_search(pt, random_obj)

def _hor_search(pt, obj):
...
object = ...
...
if object meets some condition:
return object
else:
return _ver_search(pt, object)

def _ver_search(pt, obj):
...
object = ...
...
if object meets some condition:
return object
else:
return _hor_search(pt, object)

-
Suresh
I found a simpler solution: get rid of recursive calls; using while
loops.

-
Suresh

Feb 14 '07 #4
En Wed, 14 Feb 2007 04:23:46 -0300, jm*******@no.spam.gmail.com
<jm*******@gmail.comescribió:
I am OK with calls being stacked, but I wondering will the local
variables be stacked given that return statement is followed by the
function call?

def test():
x = 22
y = 33
z = x+y
return anotherFunction(z)

In this function will all the local variables (x,y,z) be pushed into
the stack before calling anotherFunction(z) or Python will find out
that the locals are no longer needed as anotherFunction(z) is just
returned?
Not exactly like "pushed into the stack", but there exists a frame object,
and it contains references to its local variables, and will be alive until
anotherFunction returns. From inside anotherFunction you can even inspect
those variables:

pydef test():
.... x = 22
.... y = 33
.... z = x+y
.... return anotherFunction(z)
....
pydef anotherFunction(z):
.... print "previous frame:", sys._getframe(1).f_locals
....
pytest()
previous frame: {'y': 33, 'x': 22, 'z': 55}

So x,y,z will still be there until anotherFunction actually returns to
test, and the frame object is disposed. For a highly recursive function,
that's bad news. For debugging normal programs, that's good news; the
cgitb module, by example, is able to show the values for local variables
in all previous frames when an exception is raised.

--
Gabriel Genellina

Feb 14 '07 #5

"jm*******@no.spam.gmail.com" <jm*******@gmail.comwrote in message
news:11**********************@q2g2000cwa.googlegro ups.com...
I am OK with calls being stacked, but I wondering will the local
variables be stacked given that return statement is followed by the
function call?

def test():
x = 22
y = 33
z = x+y
return anotherFunction(z)

In this function will all the local variables (x,y,z) be pushed into
the stack before calling anotherFunction(z) or Python will find out
that the locals are no longer needed as anotherFunction(z) is just
returned?
=================
To add to Gabriel's answer:

Questions of this sort are implementation specific. CPython will allocate
the objects on the heap. But I believe at present something will go on the
C stack with each function call. I suspect that the C array that typically
implements the local namespace is included but don't specifically know.
The local names are deleted, and the associated heap objects released if
that make the reference count 0, as part of the return process. No
optimization of the sort you indicate is done. Indeed, the heap objects
could have other references, and the namespace array is fixed size, so I am
not sure there is anything that could, in general, be done. In any case,
Python never deletes without at least implicit instruction to do so.

The maximun recursion depth CPython will attempt is governed by an internal
variable that you can get and set to adjust to your problem and hardware:
>>sys.getrecursionlimit()
1000
>>sys.setrecursionlimit(500)
sys.getrecursionlimit()
500

Terry Jan Reedy


Feb 14 '07 #6
On 2007-02-14, Gabriel Genellina <ga******@yahoo.com.arwrote:
En Wed, 14 Feb 2007 03:09:37 -0300, jm*******@no.spam.gmail.com
<jm*******@gmail.comescribió:
> Is this way of implementation fill the stack space with the
local variables inside each call. If this is not good, is
there a better way to implement? Or python itself will
understand that the calls happen in the last line, so local
variables need not be pushed into the stack?

I'm afraid not, the calls will be stacked until some object is
found. Python does not do "tail recursion optimization" (at
least, I'm not aware of that).
To be just a little pedantic, it's "tail call optimization". Any
tail call can be optimized, not just recursive ones.
But even if it could do that, in this case you have recursive
calls between two functions, and that's a bit harder.
So the effect is that mutual recursion isn't actually any harder.

Moreover, if his functions calls really are tail-calls, then
translating them by hand into iteration might be fairly easy.

A simple, silly example:

def sum(seq):
if len(seq) == 0:
return 0
else:
return seq[0] + sum(seq[1:])

Above, sum is not a tail call, since the + operation must be
"defered" until after the call to sum returns.

Below, sum is a tail call.

def sum(seq, accum=0):
if len(seq) == 0:
return accum
else:
return sum(seq[1:], accum+s[0])

Since no state must be retained by sum when calling sum, it's a
tail call. The last version translates more or less directly into
a dumb while loop.

I don't believe Python does tail call optimization; at least it
isn't document that it does it.

--
Neil Cerutti
The audience is asked to remain seated until the end of the recession.
--Church Bulletin Blooper
Feb 14 '07 #7
En Wed, 14 Feb 2007 10:41:53 -0300, Neil Cerutti <ho*****@yahoo.com>
escribió:
On 2007-02-14, Gabriel Genellina <ga******@yahoo.com.arwrote:
>Python does not do "tail recursion optimization" (at
least, I'm not aware of that).

To be just a little pedantic, it's "tail call optimization". Any
tail call can be optimized, not just recursive ones.
Maybe, but I didn't invent the term:
http://computing-dictionary.thefreed...20optimisation
It's true that tail recursion is a special case of tail call - but it's
just the case that can be managed by hand, no special support on the
language -trampoline or whatever- is required (just a loop, "GOTO 1").
>But even if it could do that, in this case you have recursive
calls between two functions, and that's a bit harder.

So the effect is that mutual recursion isn't actually any harder.
But some kind of language support is required in this case. At least I
don't know how to handle mutual recursion (apart from inlining one
function inside the other...). But I'm certainly not a "program
transformation guru" (neither a novice!) so I would not be surprised if
someone says it can be done...

--
Gabriel Genellina

Feb 15 '07 #8

jm*******@no.spam.gmail.com wrote:
Hi,
I have a program which literately finds the object that overlapping a
point. The horizontal and vertical search are called recursively from
inside each other.
...
in case you ever need deeply nested recursion:
one solution is to use the already mentioned sys.setrecursionlimit(n)
another is to use your own stack

dummy example:

def fact_recursive(n):
if n>0:
return fact_recursive(n-1)*n
else:
return 1

def fact_iterative(n):
stack = []
while n 0:
stack.append(n)
n -= 1
ret = 1
while stack:
ret *= stack.pop()
return ret

actually you can always rewrite recursion with a stack and iterations

note that if you use version >= 2.4, then collections.deque is faster
for stack (and especially for queue) data structure than list.

Feb 15 '07 #9
On 2007-02-15, Gabriel Genellina <ga******@yahoo.com.arwrote:
En Wed, 14 Feb 2007 10:41:53 -0300, Neil Cerutti <ho*****@yahoo.com>
escribió:
>So the effect is that mutual recursion isn't actually any
harder.

But some kind of language support is required in this case. At
least I don't know how to handle mutual recursion (apart from
inlining one function inside the other...). But I'm certainly
not a "program transformation guru" (neither a novice!) so I
would not be surprised if someone says it can be done...
What happens (using the model of an imaginary virtual machine,
perhaps like the Python interpreter) is the following.

A normal function call pushes a call-frame onto a stack. The
call-frame contains information necessary for returning from the
function, and usually a place to store its local variables and
parameters.

A function called in "tail" position simply overwrites the
current call-frame with the new one instead of pushing it onto
the stack.

--
Neil Cerutti
The church will host an evening of fine dining, superb entertainment, and
gracious hostility. --Church Bulletin Blooper
Feb 15 '07 #10
En Thu, 15 Feb 2007 13:37:19 -0300, Neil Cerutti <ho*****@yahoo.com>
escribió:
On 2007-02-15, Gabriel Genellina <ga******@yahoo.com.arwrote:
>En Wed, 14 Feb 2007 10:41:53 -0300, Neil Cerutti <ho*****@yahoo.com>
escribió:
>>So the effect is that mutual recursion isn't actually any
harder.

But some kind of language support is required in this case. At
least I don't know how to handle mutual recursion (apart from
inlining one function inside the other...). But I'm certainly
not a "program transformation guru" (neither a novice!) so I
would not be surprised if someone says it can be done...

What happens (using the model of an imaginary virtual machine,
perhaps like the Python interpreter) is the following.

A normal function call pushes a call-frame onto a stack. The
call-frame contains information necessary for returning from the
function, and usually a place to store its local variables and
parameters.

A function called in "tail" position simply overwrites the
current call-frame with the new one instead of pushing it onto
the stack.
This is the "kind of language support" menctioned. For tail recursion you
don't require that support.

--
Gabriel Genellina

Feb 15 '07 #11
On 2007-02-15, Gabriel Genellina <ga******@yahoo.com.arwrote:
En Thu, 15 Feb 2007 13:37:19 -0300, Neil Cerutti <ho*****@yahoo.com>
escribió:
>On 2007-02-15, Gabriel Genellina <ga******@yahoo.com.arwrote:
>>En Wed, 14 Feb 2007 10:41:53 -0300, Neil Cerutti <ho*****@yahoo.com>
escribió:
So the effect is that mutual recursion isn't actually any
harder.

But some kind of language support is required in this case. At
least I don't know how to handle mutual recursion (apart from
inlining one function inside the other...). But I'm certainly
not a "program transformation guru" (neither a novice!) so I
would not be surprised if someone says it can be done...

What happens (using the model of an imaginary virtual machine,
perhaps like the Python interpreter) is the following.

A normal function call pushes a call-frame onto a stack. The
call-frame contains information necessary for returning from the
function, and usually a place to store its local variables and
parameters.

A function called in "tail" position simply overwrites the
current call-frame with the new one instead of pushing it onto
the stack.

This is the "kind of language support" menctioned. For tail
recursion you don't require that support.
I'm not sure what you mean. The above support is enough for tail
recursion, mutual recursion, and any other tail call to be
"optimized."

--
Neil Cerutti
Feb 15 '07 #12
En Thu, 15 Feb 2007 16:35:25 -0300, Neil Cerutti <ho*****@yahoo.com>
escribió:
On 2007-02-15, Gabriel Genellina <ga******@yahoo.com.arwrote:
>En Thu, 15 Feb 2007 13:37:19 -0300, Neil Cerutti <ho*****@yahoo.com>
escribió:
>>On 2007-02-15, Gabriel Genellina <ga******@yahoo.com.arwrote:
En Wed, 14 Feb 2007 10:41:53 -0300, Neil Cerutti <ho*****@yahoo.com>
escribió:
So the effect is that mutual recursion isn't actually any
harder.

But some kind of language support is required in this case. At
least I don't know how to handle mutual recursion (apart from
inlining one function inside the other...). But I'm certainly
not a "program transformation guru" (neither a novice!) so I
would not be surprised if someone says it can be done...

What happens (using the model of an imaginary virtual machine,
perhaps like the Python interpreter) is the following.

A normal function call pushes a call-frame onto a stack. The
call-frame contains information necessary for returning from the
function, and usually a place to store its local variables and
parameters.

A function called in "tail" position simply overwrites the
current call-frame with the new one instead of pushing it onto
the stack.

This is the "kind of language support" menctioned. For tail
recursion you don't require that support.

I'm not sure what you mean. The above support is enough for tail
recursion, mutual recursion, and any other tail call to be
"optimized."
I only want to say that tail *recursion* can be eliminated trivially
transforming the code into a while loop, and that can be done by the
programmer, and doesn't require compiler support. Head *recursion* can be
eliminated too by using some storage as temporary stack, and that doesn't
require external support either. Mutual recursion (and generic tail call
elimination) require some sort of external support: one can't eliminate
the call just by transforming the program.

--
Gabriel Genellina

Feb 15 '07 #13
On 2007-02-15, Gabriel Genellina <ga******@yahoo.com.arwrote:
>I'm not sure what you mean. The above support is enough for
tail recursion, mutual recursion, and any other tail call to
be "optimized."

I only want to say that tail *recursion* can be eliminated
trivially transforming the code into a while loop, and that
can be done by the programmer, and doesn't require compiler
support. Head *recursion* can be eliminated too by using some
storage as temporary stack, and that doesn't require external
support either. Mutual recursion (and generic tail call
elimination) require some sort of external support: one can't
eliminate the call just by transforming the program.
Ah, I see now. Had my blinders on.

--
Neil Cerutti
Low Self-Esteem Support Group will meet Thursday at 7 to 8:30 p.m. Please use
the back door. --Church Bulletin Blooper
Feb 15 '07 #14

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

2 posts views Thread by Marcin Kielar | last post: by
5 posts views Thread by Alex Polite | last post: by
9 posts views Thread by Bill Borg | last post: by
9 posts views Thread by Csaba Gabor | last post: by
41 posts views Thread by Harry | last post: by
1 post views Thread by CARIGAR | last post: by
1 post views Thread by Marylou17 | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.