Recursion elimination

  • Thread starter Lorenzo Villari
  • Start date
L

Lorenzo Villari

I've tried to transform this into a not recursive version but without
luck...

#include <stdio.h>


void countdown(int p)
{
int x;

x = p - 1;

if(p != 0)
{
printf("%d\n", p);
countdown(x);
}
}


int main()
{
int i = 10;

countdown(i);

return 0;
}
 
S

Steve Zimmerman

Lorenzo said:
I've tried to transform this into a not recursive version but without
luck...

#include <stdio.h>


void countdown(int p)
{
int x;

x = p - 1;

if(p != 0)
{
printf("%d\n", p);
countdown(x);
}
}


int main()
{
int i = 10;

countdown(i);

return 0;
}

#include <stdio.h>


void countdown(int upper_limit);

int main()
{
int i = 10;
countdown(i);

return 0;
}

void countdown(int upper_limit)
{
int i;

for (i = upper_limit; i > 0; i--)
printf("%d\n", i);
{



--Steve
 
J

Jeff

Lorenzo Villari said:
I've tried to transform this into a not recursive version but without
luck...

#include <stdio.h>


void countdown(int p)
{
int x;

x = p - 1;

if(p != 0)
{
printf("%d\n", p);
countdown(x);
}
}


int main()
{
int i = 10;

countdown(i);

return 0;
}

Read the topic of "for-loop"

int main()
{
int i;

for(i=9; i!=0; i--)
{
printf("%d\n", i);
}

return 0;
}
 
E

E. Robert Tisdale

Lorenzo said:
I've tried to transform this into a not recursive version
but without luck...

A *good* optimizing C compiler will do this for you.
> cat recurse.c
#include <stdio.h>

void countdown(int p) {
if(0 != p) {
printf("%d\n", p);
countdown(p - 1);
}
}

int main(int argc, char* argv[]) {
countdown(10);

return 0;
}
> gcc -Wall -std=c99 -pedantic -O3 -S recurse.c
> cat recurse.save
.file "recurse.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "%d\n"
.text
.align 2
.p2align 2,,3
.globl main
.type main,@function
main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
andl $-16, %esp
subl $8, %esp
pushl $10
pushl $.LC0
call printf
movl $9, (%esp)
call countdown
addl $16, %esp
xorl %eax, %eax
leave
ret
.Lfe1:
.size main,.Lfe1-main
.align 2
.p2align 2,,3
.globl countdown
.type countdown,@function
countdown:
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %eax
movl 8(%ebp), %ebx // p
.p2align 2,,3
.L9:
testl %ebx, %ebx // if (0 != p)
je .L7 // go to .L7
subl $8, %esp
pushl %ebx
pushl $.LC0
call printf // printf("%d\n", p)
decl %ebx // --p
addl $16, %esp
jmp .L9 // go to .L9
.L7:
movl -4(%ebp), %ebx
leave
ret
.Lfe2:
.size countdown,.Lfe2-countdown
.ident "GCC: (GNU) 3.2 20020903 \
(Red Hat Linux 8.0 3.2-7)"
 
V

Vijay Kumar Zanvar

#include <stdio.h>


void countdown2 ( int p )
{
for ( ; p; )
printf ( "%d\n", p-- );
}

int main()
{
int i = 10;

countdown2 (i);

return 0;
}

Lorenzo Villari wrote in message ...
 
I

Irrwahn Grausewitz

E. Robert Tisdale said:
Lorenzo said:
I've tried to transform this into a not recursive version
but without luck...

A *good* optimizing C compiler will do this for you.

#include <stdio.h>

void countdown(int p) {
if(0 != p) {
printf("%d\n", p);
countdown(p - 1);
}
}

int main(int argc, char* argv[]) {
countdown(10);

return 0;
}
<SNIP>
Assembler listing completely irrelevant!
 
L

LibraryUser

Lorenzo said:
I've tried to transform this into a not recursive version but
without luck...

#include <stdio.h>

void countdown(int p)
{
int x;

x = p - 1;
if(p != 0)
{
printf("%d\n", p);
countdown(x);
}
}

void countdown(int p)
{
while (p) {
printf("%d\n", p);
p = p - 1;
}
}

All of which will go off into the boondocks if p is supplied as a
negative value. Therefore a better test is (p > 0).
 
P

Peter Ammon

Lorenzo said:
I've tried to transform this into a not recursive version but without
luck...

#include <stdio.h>


void countdown(int p)
{
int x;

x = p - 1;

if(p != 0)
{
printf("%d\n", p);
countdown(x);
}
}


int main()
{
int i = 10;

countdown(i);

return 0;
}

All recursive functions can be made iterative with an explicit stack.

Therefore, the right way to do this is with an explicit stack.

void countdown(int p) {
unsigned stackIndex = 0;
int stack[10];
#define PUSH(x) stack[stackIndex++]=(x)
#define POP() stack[--stackIndex]

PUSH(p);

beginning:
{
int p = POP();
int x;
x=p - 1;
if (p != 0)
{
printf("%d\n", p);
PUSH(x);
goto beginning;
}
}
}

-Peter
 
I

Irrwahn Grausewitz

<[email protected]>:

All recursive functions can be made iterative with an explicit stack. Err...

Therefore, the right way to do this is with an explicit stack.
Ehm...

Somebody correct me please if I'm totally wrong [for me it's 2:00am],
but if you have to make excessive use of a stack, then this /is/
recursion after all...
void countdown(int p) {
unsigned stackIndex = 0;
int stack[10];
#define PUSH(x) stack[stackIndex++]=(x)
#define POP() stack[--stackIndex]

PUSH(p);

beginning:
{
int p = POP();
int x;
x=p - 1;
if (p != 0)
{
printf("%d\n", p);
PUSH(x);
goto beginning;
}
}
}
Yup, all you did is recursion the hard way: instead of using your
implementation's stack you built your own one and replaced the
recursive function call by a (Uck!) goto (Yuck!). At least you
could have replaced the (Yuck!) goto (Bleah!) with a while(1)/break
construct - but still this would be recursion, even if no recursive
function call takes place.

Remember: for iterative calculations you don't need a stack at all -
hence iteration will never overflow your stack, whereas recursion
might do - try to 'countdown( 20 );'.

The probably shortest possible iterative countdown function in C
would be:

void countdown( int p )
{
while ( p )
printf( "%d\n", p-- );
}
/* Merged from the suggestions of Mr. Zanvar and Mr. Falconer */

Irrwahn
 
L

LibraryUser

Irrwahn said:
.... snip ...

The probably shortest possible iterative countdown function in C
would be:

void countdown( int p )
{
while ( p )
printf( "%d\n", p-- );
}
/* Merged from the suggestions of Mr. Zanvar and Mr. Falconer */

I did not use the p-- construct because it would (possibly)
confuse the OP. You have not protected against calls with
negative p, BTW.

This conversion falls under the general class of end-around
recursion, where the recursive call is the last action of the
function. It can always be replaced by a loop after altering the
input parameters. Many compilers will automate this.
 
I

Irrwahn Grausewitz

Irrwahn Grausewitz wrote:
... snip ...

I did not use the p-- construct because it would (possibly)
confuse the OP. You have not protected against calls with
negative p, BTW.
Definetely right. I'll make it:

void countdown( int p )
{
while ( p > 0 )
printf( "%d\n", p-- );
}

<SNIP>
 
P

Peter Ammon

Irrwahn said:
<[email protected]>:

All recursive functions can be made iterative with an explicit stack.
Err...


Therefore, the right way to do this is with an explicit stack.

Ehm...

Somebody correct me please if I'm totally wrong [for me it's 2:00am],
but if you have to make excessive use of a stack,

My use of a stack was very appropriate for what I intended to
accomplish, and in no way excessive.
then this /is/
recursion after all...

As a tongue out of cheek aside, I don't think most people would classify
this as recursion. I claim you can have a stack without recursion;
whats more, I claim you can have recursion without a stack. Look at
this code for adding two numbers:

unsigned foo(unsigned r3, unsigned r4) {
return r4 ? foo(++r3, --r4) : r3;
}

This is a bona-fide, guaranteed or your money back, indisputably
recursive function.

Here's gcc's assembly output for it:

L3:
cmpwi cr7,r4,0 /* compare r4 to 0 */
beqlr- cr7 /* return r3 if r4 is zero */
addi r3,r3,1 /* add one to r3 */
addi r4,r4,-1 /* subtract one from r4 */
b L3 /* go back to L3 */

Nowhere is any stack, including THE stack, touched, modified, or used at
all!
void countdown(int p) {
unsigned stackIndex = 0;
int stack[10];
#define PUSH(x) stack[stackIndex++]=(x)
#define POP() stack[--stackIndex]

PUSH(p);

beginning:
{
int p = POP();
int x;
x=p - 1;
if (p != 0)
{
printf("%d\n", p);
PUSH(x);
goto beginning;
}
}
}

Yup, all you did is recursion the hard way: instead of using your
implementation's stack you built your own one and replaced the
recursive function call by a (Uck!) goto (Yuck!).

Precisely, and hence, iteration!
At least you
could have replaced the (Yuck!) goto (Bleah!) with a while(1)/break
construct

But that wouldn't have captured the spirit of my solution.
- but still this would be recursion, even if no recursive
function call takes place.
Why?


Remember: for iterative calculations you don't need a stack at all -
hence iteration will never overflow your stack, whereas recursion
might do - try to 'countdown( 20 );'.

Ok, I tried countdown(20); in my code as above, and it worked fine. I
guess it must be iterative after all.
The probably shortest possible iterative countdown function in C
would be:

void countdown( int p )
{
while ( p )
printf( "%d\n", p-- );
}
/* Merged from the suggestions of Mr. Zanvar and Mr. Falconer */

countdown(-1);
puts("Houston, we have a problem...");

:>

-Peter
 
L

Lorenzo Villari

Read the topic of "for-loop"

Ok but there's a way to "mechanically" eliminate recursion?
I mean something valid for all recursive functions,
expecially the ones with tail recursion...

For example... I've got a recursive function and I want to eliminate its
recursion but the code is too much and I can't post it...
automation (a formula) would be a solution...
(of course being a master of the language would be better...)
 
I

Irrwahn Grausewitz

As a tongue out of cheek aside, I don't think most people would classify
this as recursion.
Hm, to be honest, looking at your code again I've noticed that I
misinterpreted it. All you do is alternating PUSH and POP, not
accumulating intermediate results on the stack (as I erroneously
thought from first glance). This way you need a maximum stack-size
of one. So, dealing with a stack in this case is IMHO nonsense at
all. Why not replace it with a temp-variable (a size 1 stack)?
Furthermore, why not eliminate even this, and end up with sth like
that:

void countdown( int p )
{
while( 1 )
{
int x;
x = p - 1;
if (p == 0)
break;
printf("%d\n", p);
p = x;
}
}

Looks familiar, yet still a bit more complex as necessary...
I claim you can have a stack without recursion;
Nobody doubted.
whats more, I claim you can have recursion without a stack.
<OT>
Well, you can have coffee without milk. :)
To get serious again: where will /real/ recursive code (not code
where recursion has been optimized away by the compiler, see below)
save the data (read:registers and values of automatic variables)
and the return address, if not in some place that at least /acts
like a stack/ ? [It may actually be some DNS in a test tube, but
who cares, as long as it resembles the funcionality of a lifo/stack
in some common platform architectures.]
Look at this code for adding two numbers:

unsigned foo(unsigned r3, unsigned r4) {
return r4 ? foo(++r3, --r4) : r3;
}

This is a bona-fide, guaranteed or your money back, indisputably
recursive function. Agreed.

Here's gcc's assembly output for it:

L3:
cmpwi cr7,r4,0 /* compare r4 to 0 */
beqlr- cr7 /* return r3 if r4 is zero */
addi r3,r3,1 /* add one to r3 */
addi r4,r4,-1 /* subtract one from r4 */
b L3 /* go back to L3 */

Nowhere is any stack, including THE stack, touched, modified, or used at
all!
Well, the (OT) compiler is free to produce anything, *as long as the
result matches the /intention/ of the source code*. If, for example,
you write a recursice faculty function, the (OT) compiler is free to
produce whatever code it likes, as long as it calculates the faculty!
This might very likely result in producing iterative code for a
recursive function, or what else modern /optimizing/ (OT) compilers
are capable of. I wonder what the result would look like, if you
compile foo() with (OT) code-optimization disabled...

Precisely, and hence, iteration!
I have to agree, now that I've discovered the fault in my
interpretation.
But that wouldn't have captured the spirit of my solution.
Hm, what is the effective difference between

loop:
{
/* ... */;
}
goto loop;

and

while( 1 )
{
/* ... */;
}

respectively?

Or between (pseudocode):

loop:
/* ... */;
if ( <expr> )
goto loop;

and

while ( 1 )
{
/* ... */;
if ( !<expr> )
break;
}

and

do
{
/* ... */;
}
while ( <expr> );

respectively?
Q&A now obsolete, see above.
Ok, I tried countdown(20); in my code as above, and it worked fine. I
guess it must be iterative after all.
Agreed, see above.
Has been corrected already.
countdown(-1);
puts("Houston, we have a problem...");
And *EXACTLY* the same as with your code!!! :p

Irrwahn
 
E

E. Robert Tisdale

Lorenzo said:
Ok but there's a way to "mechanically" eliminate recursion?
I mean something valid for all recursive functions,
expecially the ones with tail recursion...

For example... I've got a recursive function and I want to eliminate its
recursion but the code is too much and I can't post it...
automation (a formula) would be a solution...
(of course being a master of the language would be better...)

Your C compiler should do this for you.
 
B

Ben Pfaff

Lorenzo Villari said:
Ok but there's a way to "mechanically" eliminate recursion?
I mean something valid for all recursive functions,
expecially the ones with tail recursion...

For example... I've got a recursive function and I want to eliminate its
recursion but the code is too much and I can't post it...
automation (a formula) would be a solution...
(of course being a master of the language would be better...)

There's a mini-tutorial on eliminating recursion in my book on
binary search trees. You can read it at
http://adtinfo.org/libavl.html/Iterative-Traversal-of-a-BST.html

It's not always the right thing to do to eliminate recursion.
However, there are several reasons to avoid recursion in C:

* Recursion is more difficult to understand in some
algorithms (but see below). An algorithm that can
naturally be expressed iteratively may not be as easy
to understand if expressed recursively.

* There is no portable way to tell how deep recursion can
go without causing trouble (how much `stack space' the
machine has), and there is no way to recover from
too-deep recursion (a `stack overflow').

* In C you can't do some nice things recursively. For
instance, if I'm traversing a binary tree, I probably
want to do it using a for loop:

tree t;
item *i;

for (i = first (t); i != NULL; i = next (i)) {
...do something with i...
}

But you can't write the traversal recursively if you
want to do this. Factoring the traversal into
iteration or forcing the use of a callback function are
the only two choices. The former is the lesser of the
two evils, since you only have to do it once and then
can use many times in traversals.

(Now, if C had built-in support for co-routines, we
could do this recursively anyhow. Most procedural
languages do not support co-routines; I hear that Icon
is an exception. It's really too bad, but I don't see
this changing soon.)

* Suppose that you need to pass some data to the
recursive process. You might want to keep a count of
the number of nodes visited, or a set of parameters
that determine what to do at each node, or anything
else. In order to do this, you have to pass some data
to every recursive call. This is a waste of time and
space, unless your compiler is much smarter than mine.
Alternatively, you can use global variables, but that's
hardly a preferable solution.

Now, if you were to use an iterative solution instead,
you could just have a single set of local variables,
and there is no need to pass anything recursively.
This saves the time and memory that would be used for
passing these things in the recursive calls.

* Aborting a recursive process in midstream is a pain.
Suppose that you're using a function to enumerate all
the items in a binary search tree, and you discover
halfway through that you don't need to look at any more
items. Alternatively, consider the problem of aborting
after a syntax error while parsing an expression via
recursive descent. Trying to abort the process
involves the cooperation of the currently executing
instance with all of the instances in which it is
nested. This is slow and sometimes nasty. Use of
setjmp() and longjmp() is an alternative, but, like
goto, these constructs are best avoided when practical.

Even worse, suppose, in the context of the binary
search tree example, that halfway through you discover
that you need to change directions, move *backward*. I
don't even want to think about how to do that
recursively. It's simply impractical.

* Avoiding recursive calls often avoids other kinds of
overhead, such as the system's unavoidable function
call overhead. On some systems this can be
significant, so a transformation from recursion to
iteration can improve both speed and space
requirements.

There are reasons to avoid iteration, too:

* Iteration is more difficult to understand in some
algorithms (but see above). An algorithm that can
naturally be expressed recursively may not be as easy
to understand if expressed iteratively. It can also be
difficult to convert a recursive algorithm into an
iterative algorithm, and verifying that the algorithms
are equivalent can also be difficult.

* Recursion allows you to allocate additional automatic
objects at each function call. The iterative
alternative is to repeatedly dynamically allocate or
resize memory blocks. On many platforms automatic
allocation is much faster, to the point that its speed
bonus outweighs the speed penalty and storage cost of
recursive calls. (But some platforms don't support
allocation of large amounts of automatic data, as
mentioned above; it's a trade-off.)
 
M

Morris Dovey

Lorenzo said:
Ok but there's a way to "mechanically" eliminate recursion? I
mean something valid for all recursive functions, expecially
the ones with tail recursion...

Lorenzo...

No, not /all/ recursive functions can be implemented as
equivalent iterative functions.

I suspect that the subset of recursive functions whose exact
number of recursions can be calculated at time of invocation
/can/ be mechanically converted; and that the remainder can't -
but I have no proof to offer. 8^(
 
P

pete

Irrwahn said:
Definetely right. I'll make it:

void countdown( int p )
{
while ( p > 0 )
printf( "%d\n", p-- );
}

void countdown(unsigned p)
{
while ( p )
printf( "%u\n", p-- );
}

/*
** I like unsigned types for situations
** where negative values aren't allowed.
*/
 

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

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top