Way to go from recursion to iteration
Asked Answered
H

21

453

I've used recursion quite a lot during my many years of programming to solve simple problems, but I'm fully aware that sometimes you need iteration due to memory/speed problems.

So, sometime in the very far past I went to try and find if there existed any "pattern" or text-book way of transforming a common recursion approach to iteration and found nothing. Or at least nothing that I can remember it would help.

  • Are there general rules?
  • Is there a "pattern"?
Him answered 1/10, 2008 at 20:38 Comment(2)
I found this series informative: blog.moertel.com/posts/2013-05-11-recursive-to-iterative.htmlCrowberry
Also this: textbooks.cs.ksu.edu/cc310/6-recursion/…Gandhi
P
421

Usually, I replace a recursive algorithm by an iterative algorithm by pushing the parameters that would normally be passed to the recursive function onto a stack. In fact, you are replacing the program stack by one of your own.

var stack = [];
stack.push(firstObject);

// while not empty
while (stack.length) {

    // Pop off end of stack.
    obj = stack.pop();

    // Do stuff.
    // Push other objects on the stack as needed.
    ...

}

Note: if you have more than one recursive call inside and you want to preserve the order of the calls, you have to add them in the reverse order to the stack:

foo(first);
foo(second);

has to be replaced by

stack.push(second);
stack.push(first);

Edit: The article Stacks and Recursion Elimination (or Article Backup link) goes into more details on this subject.

Palisade answered 1/10, 2008 at 21:12 Comment(17)
Pushing just the parameters may not be sufficient. you need to push the local variables which are used subsequent to the point of recursive call. Also, the linked article provides a skeleton that is not suitable for implementations having recur. calls from deeper code blocks. I have tried to provide a repeatable method for converstion as an answer below.Demarcusdemaria
If you replace your stack with a Queue son't that solve the problem of reversing the add order?Hyams
I worked it out on paper and they are two different things. If you reverse the order you added them it makes you traverse forward as usual, but your traversal is still depth-first search. But if you change the whole thing into a queue now you are doing breadth-first rather than depth-first traversal.Strickler
It might be worth mentioning that if between the two foos you add some work(), you could push that on your stack as well, but you would want to add an additional parameter like isWork.Kaspar
I just recently did this in a general way, by replacing my node visitation function (node)->() with (node)->[actions] where action is () -> [actions]. Then on the outside, you just pop an action/continuation off the stack, apply/execute it, push the actions it returned on the stack in reverse in order and repeat. Contingent/complex traversals, you just capture what would have been local stack variables in reference-counted pointers that you close over in your thunks, then subsequent thunks can be contingent on the results of previous sub-traversals etc.Centistere
I think you can do tail recursion this way in languages which does not support it.Conjuncture
@Hyams If you use a queue instead of a stack you turn the depth-first traversal of the original call tree into a breadth-first traversal. eg if f(1) calls f(2) then f(3), and f(2) calls f(4), you should get 1,2,4,3, but by putting calls onto a queue you'd get 1,2,3,4 (since 1 will enqueue 3 before 2 gets a chance to enqueue 4).Liverpool
Sometimes we avoid recursion in order to avoid stackoverflow. But maintaining our own stack will also cause stackoverflow. So why do we want to implement recursion with our own stack?Eligibility
stack.pop() is not necessary for every iteration, sometimes you might want to push other objects on top of the current one.Animalize
Why it's better than internal call stack?Tantalate
@ZhuLi If we use new we can create an object on the heap instead of the stack. Unlike stack, heap does not have memory restrictions. See gribblelab.org/CBootCamp/7_Memory_Stack_vs_Heap.htmlChancellorship
@Hyams If you guarantee nothing is not popped before everything is pushed, then you could convert a stack into a queue. However, in most cases (including this one), popping occurs in the process of pushing. In one word, stacks and queues are not convertible in general.Manner
If you have more than one recursive call inside your function, and you don't want to jumble up the order of statements or you use the return value, it's generally a bit more complicated than this answer makes it seem (see this answer for how to deal with that).Chinquapin
I added an answer that shows a question with both an iterative and recursive solutionBrochette
Basically while using a stack, we are traversing the call stack via depth first search, and while using a queue, we are traversing the call stack via breadth first search.Alehouse
+1 for the Note: if you have more than one recursive call inside and you want to preserve the order of the calls, you have to add them in the reverse order to the stack, which saved me :-)Ripe
Updating link from @Chancellorship gribblelab.org/teaching/CBootCamp/7_Memory_Stack_vs_Heap.html -- to reiterate, maintaining our own "stack" (in the sense of an array; maybe a last-in-first-out (LIFO) array, aka a "stack", but not the notion of the "stack" region of computer memory) will actually use the heap region of computer memory...Perilous
I
88

Really, the most common way to do it is to keep your own stack. Here's a recursive quicksort function in C:

    void quicksort(int* array, int left, int right)
    {
        if(left >= right)
            return;
     
        int index = partition(array, left, right);
        quicksort(array, left, index - 1);
        quicksort(array, index + 1, right);
    }

Here's how we could make it iterative by keeping our own stack:

    void quicksort(int *array, int left, int right)
    {
        int stack[1024];
        int i=0;

        stack[i++] = left;
        stack[i++] = right;

        while (i > 0)
        {
            right = stack[--i];
            left = stack[--i];

            if (left >= right)
                 continue;

            int index = partition(array, left, right);
            stack[i++] = left;
            stack[i++] = index - 1;
            stack[i++] = index + 1;
            stack[i++] = right;
        }
    }

Obviously, this example doesn't check stack boundaries... and really you could size the stack based on the worst case given left and and right values. But you get the idea.

Indraft answered 1/10, 2008 at 20:55 Comment(4)
Any ideas on how to work out the maximum stack to allocate for a particular recursion?Elysium
@Elysium suppose you have a recursive algorithm in O(N) = O(R*L), where L is the sum of complexity "for layer r", e.g. in this case you have O(N) work at each step doing the partitionings, the recursive depth is O(R), i.e. worst case O(N), average case O(logN) here.Ressler
@Elysium always push the longest part's boundaries to the stack and continue the loop to work on the shortest part of the partition. this way the stack is guaranteed to be logarithmic in the array size.Skean
... this is at most logarithmic.Skean
S
76

It seems nobody has addressed where the recursive function calls itself more than once in the body, and handles returning to a specific point in the recursion (i.e. not primitive-recursive). It is said that every recursion can be turned into iteration, so it appears that this should be possible.

I just came up with a C# example of how to do this. Suppose you have the following recursive function, which acts like a postorder traversal, and that AbcTreeNode is a 3-ary tree with pointers a, b, c.

public static void AbcRecursiveTraversal(this AbcTreeNode x, List<int> list) {
        if (x != null) {
            AbcRecursiveTraversal(x.a, list);
            AbcRecursiveTraversal(x.b, list);
            AbcRecursiveTraversal(x.c, list);
            list.Add(x.key);//finally visit root
        }
}

The iterative solution:

        int? address = null;
        AbcTreeNode x = null;
        x = root;
        address = A;
        stack.Push(x);
        stack.Push(null)    

        while (stack.Count > 0) {
            bool @return = x == null;

            if (@return == false) {

                switch (address) {
                    case A://   
                        stack.Push(x);
                        stack.Push(B);
                        x = x.a;
                        address = A;
                        break;
                    case B:
                        stack.Push(x);
                        stack.Push(C);
                        x = x.b;
                        address = A;
                        break;
                    case C:
                        stack.Push(x);
                        stack.Push(null);
                        x = x.c;
                        address = A;
                        break;
                    case null:
                        list_iterative.Add(x.key);
                        @return = true;
                        break;
                }

            }


            if (@return == true) {
                address = (int?)stack.Pop();
                x = (AbcTreeNode)stack.Pop();
            }


        }
Shellyshelman answered 14/12, 2011 at 21:42 Comment(8)
It's really usefull, I had to write iterative version of reccurence which ivokes itself n-times, thanks to your post I did it.Deformity
This has to be the best example I have ever seen of emulating call stack recursion for situations where multiple recursive calls are being made within the method. Nice job.Troytroyer
You had me at "It seems nobody has addressed where the recursive function calls itself more than once in the body, and handles returning to a specific point in the recursion" and then I already upvoted. OK, now I am going to read the rest of your answer and see if my premature upvote was justified. (Because I desperately need to know the answer to that).Athwart
@Athwart - Returning to this question after so long, it even took me a moment to remember what I was thinking. Hope the answer helped.Shellyshelman
@T.Webster Well, it did send me down a specific path of thinking, although I still don't have my answer. For now, I am going to use a recursive function, although I would like to get away from it eventually. Thanks, however; the upvote remains :-)Athwart
@Athwart thank you. I'm taking some time to pick up Python 3.x now (Microsoft is pushing cross-platform development so), and there may be an easier way to express this.Shellyshelman
@T.Webster As for me, I am doing the nand2tetris course on Coursera to try and broaden my mind and hopefully find the answer I am looking for. (Because I believe it does come up at some point).Athwart
I liked the idea of this solution, but it seemed confusing to me. I wrote simplified version for binary tree in python, maybe it will help someone to understand the idea: gist.github.com/azurkin/abb258a0e1a821cbb331f2696b37c3acSeessel
A
37

Strive to make your recursive call Tail Recursion (recursion where the last statement is the recursive call). Once you have that, converting it to iteration is generally pretty easy.

Aigneis answered 1/10, 2008 at 20:45 Comment(2)
Some JIT's transform tail recursion: ibm.com/developerworks/java/library/j-diag8.htmlBiting
Plenty of interpreters (ie Scheme being the most well known) will optimize tail recursion well. I know that GCC, with a certain optimization, does tail recursion (even though C is an odd choice for such an optimization).Ultrasonics
M
23

Well, in general, recursion can be mimicked as iteration by simply using a storage variable. Note that recursion and iteration are generally equivalent; one can almost always be converted to the other. A tail-recursive function is very easily converted to an iterative one. Just make the accumulator variable a local one, and iterate instead of recurse. Here's an example in C++ (C were it not for the use of a default argument):

// tail-recursive
int factorial (int n, int acc = 1)
{
  if (n == 1)
    return acc;
  else
    return factorial(n - 1, acc * n);
}

// iterative
int factorial (int n)
{
  int acc = 1;
  for (; n > 1; --n)
    acc *= n;
  return acc;
}

Knowing me, I probably made a mistake in the code, but the idea is there.

Muchness answered 1/10, 2008 at 20:53 Comment(0)
J
18

Even using stack will not convert a recursive algorithm into iterative. Normal recursion is function based recursion and if we use stack then it becomes stack based recursion. But its still recursion.

For recursive algorithms, space complexity is O(N) and time complexity is O(N). For iterative algorithms, space complexity is O(1) and time complexity is O(N).

But if we use stack things in terms of complexity remains same. I think only tail recursion can be converted into iteration.

Jacobinism answered 6/11, 2010 at 16:27 Comment(2)
I agree with your first bit, but I think I'm misunderstanding the second paragraph. Consider cloning an array through just copying the memory copy = new int[size]; for(int i=0; i<size; ++i) copy[i] = source[i]; space and time complexity are both O(N) based on the size of the data, but it's clearly an iterative algorithm.Crankcase
@Crankcase Yes. Both of the iterative and recursive solutions take O(N) space and time complexities because recursion is just replacing the call stack with a program stack. But still there are reasons one would go for converting a recursion to iteration, one of them would be coverting your serially executed code into a parallel one using something like CUDA programming.Alehouse
D
15

The stacks and recursion elimination article captures the idea of externalizing the stack frame on heap, but does not provide a straightforward and repeatable way to convert. Below is one.

While converting to iterative code, one must be aware that the recursive call may happen from an arbitrarily deep code block. Its not just the parameters, but also the point to return to the logic that remains to be executed and the state of variables which participate in subsequent conditionals, which matter. Below is a very simple way to convert to iterative code with least changes.

Consider this recursive code:

struct tnode
{
    tnode(int n) : data(n), left(0), right(0) {}
    tnode *left, *right;
    int data;
};

void insertnode_recur(tnode *node, int num)
{
    if(node->data <= num)
    {
        if(node->right == NULL)
            node->right = new tnode(num);
        else
            insertnode(node->right, num);
    }
    else
    {
        if(node->left == NULL)
            node->left = new tnode(num);
        else
            insertnode(node->left, num);
    }    
}

Iterative code:

// Identify the stack variables that need to be preserved across stack 
// invocations, that is, across iterations and wrap them in an object
struct stackitem 
{ 
    stackitem(tnode *t, int n) : node(t), num(n), ra(0) {}
    tnode *node; int num;
    int ra; //to point of return
};

void insertnode_iter(tnode *node, int num) 
{
    vector<stackitem> v;
    //pushing a stackitem is equivalent to making a recursive call.
    v.push_back(stackitem(node, num));

    while(v.size()) 
    {
        // taking a modifiable reference to the stack item makes prepending 
        // 'si.' to auto variables in recursive logic suffice
        // e.g., instead of num, replace with si.num.
        stackitem &si = v.back(); 
        switch(si.ra)
        {
        // this jump simulates resuming execution after return from recursive 
        // call 
            case 1: goto ra1;
            case 2: goto ra2;
            default: break;
        } 

        if(si.node->data <= si.num)
        {
            if(si.node->right == NULL)
                si.node->right = new tnode(si.num);
            else
            {
                // replace a recursive call with below statements
                // (a) save return point, 
                // (b) push stack item with new stackitem, 
                // (c) continue statement to make loop pick up and start 
                //    processing new stack item, 
                // (d) a return point label
                // (e) optional semi-colon, if resume point is an end 
                // of a block.

                si.ra=1;
                v.push_back(stackitem(si.node->right, si.num));
                continue; 
ra1:            ;         
            }
        }
        else
        {
            if(si.node->left == NULL)
                si.node->left = new tnode(si.num);
            else
            {
                si.ra=2;                
                v.push_back(stackitem(si.node->left, si.num));
                continue;
ra2:            ;
            }
        }

        v.pop_back();
    }
}

Notice how the structure of the code still remains true to the recursive logic and modifications are minimal, resulting in less number of bugs. For comparison, I have marked the changes with ++ and --. Most of the new inserted blocks except v.push_back, are common to any converted iterative logic

void insertnode_iter(tnode *node, int num) 
{

+++++++++++++++++++++++++

    vector<stackitem> v;
    v.push_back(stackitem(node, num));

    while(v.size())
    {
        stackitem &si = v.back(); 
        switch(si.ra)
        {
            case 1: goto ra1;
            case 2: goto ra2;
            default: break;
        } 

------------------------

        if(si.node->data <= si.num)
        {
            if(si.node->right == NULL)
                si.node->right = new tnode(si.num);
            else
            {

+++++++++++++++++++++++++

                si.ra=1;
                v.push_back(stackitem(si.node->right, si.num));
                continue; 
ra1:            ;    

-------------------------

            }
        }
        else
        {
            if(si.node->left == NULL)
                si.node->left = new tnode(si.num);
            else
            {

+++++++++++++++++++++++++

                si.ra=2;                
                v.push_back(stackitem(si.node->left, si.num));
                continue;
ra2:            ;

-------------------------

            }
        }

+++++++++++++++++++++++++

        v.pop_back();
    }

-------------------------

}
Demarcusdemaria answered 29/4, 2013 at 14:43 Comment(3)
This has helped me a lot, but there dis a problem: stackitem objects are allocated with a garbage value for ra. Everything still works in the most like case, but should ra by coincidence be 1 or 2 you will get incorrect behavior. The solution is to initialize ra to 0.Hus
@JanX2, stackitem must not be pushed without initializing. But yes, initializing to 0 would catch errors.Demarcusdemaria
Why aren't both return addresses set to the v.pop_back() statement instead?Composure
O
10

Search google for "Continuation passing style." There is a general procedure for converting to a tail recursive style; there is also a general procedure for turning tail recursive functions into loops.

Orlon answered 1/10, 2008 at 20:48 Comment(0)
O
8

Just killing time... A recursive function

void foo(Node* node)
{
    if(node == NULL)
       return;
    // Do something with node...
    foo(node->left);
    foo(node->right);
}

can be converted to

void foo(Node* node)
{
    if(node == NULL)
       return;

    // Do something with node...

    stack.push(node->right);
    stack.push(node->left);

    while(!stack.empty()) {
         node1 = stack.pop();
         if(node1 == NULL)
            continue;
         // Do something with node1...
         stack.push(node1->right);             
         stack.push(node1->left);
    }

}
Orianna answered 25/8, 2011 at 5:15 Comment(1)
The above example is an example of recursive to iterative dfs on binary search tree :)Orchestrate
S
7

Thinking of things that actually need a stack:

If we consider the pattern of recursion as:

if(task can be done directly) {
    return result of doing task directly
} else {
    split task into two or more parts
    solve for each part (possibly by recursing)
    return result constructed by combining these solutions
}

For example, the classic Tower of Hanoi

if(the number of discs to move is 1) {
    just move it
} else {
    move n-1 discs to the spare peg
    move the remaining disc to the target peg
    move n-1 discs from the spare peg to the target peg, using the current peg as a spare
}

This can be translated into a loop working on an explicit stack, by restating it as:

place seed task on stack
while stack is not empty 
   take a task off the stack
   if(task can be done directly) {
      Do it
   } else {
      Split task into two or more parts
      Place task to consolidate results on stack
      Place each task on stack
   }
}

For Tower of Hanoi this becomes:

stack.push(new Task(size, from, to, spare));
while(! stack.isEmpty()) {
    task = stack.pop();
    if(task.size() = 1) {
        just move it
    } else {
        stack.push(new Task(task.size() -1, task.spare(), task,to(), task,from()));
        stack.push(new Task(1, task.from(), task.to(), task.spare()));
        stack.push(new Task(task.size() -1, task.from(), task.spare(), task.to()));
    }
}

There is considerable flexibility here as to how you define your stack. You can make your stack a list of Command objects that do sophisticated things. Or you can go the opposite direction and make it a list of simpler types (e.g. a "task" might be 4 elements on a stack of int, rather than one element on a stack of Task).

All this means is that the memory for the stack is in the heap rather than in the Java execution stack, but this can be useful in that you have more control over it.

Samaria answered 14/8, 2017 at 15:9 Comment(0)
M
5

Generally the technique to avoid stack overflow is for recursive functions is called trampoline technique which is widely adopted by Java devs.

However, for C# there is a little helper method here that turns your recursive function to iterative without requiring to change logic or make the code in-comprehensible. C# is such a nice language that amazing stuff is possible with it.

It works by wrapping parts of the method by a helper method. For example the following recursive function:

int Sum(int index, int[] array)
{
 //This is the termination condition
 if (int >= array.Length)
 //This is the returning value when termination condition is true
 return 0;

//This is the recursive call
 var sumofrest = Sum(index+1, array);

//This is the work to do with the current item and the
 //result of recursive call
 return array[index]+sumofrest;
}

Turns into:

int Sum(int[] ar)
{
 return RecursionHelper<int>.CreateSingular(i => i >= ar.Length, i => 0)
 .RecursiveCall((i, rv) => i + 1)
 .Do((i, rv) => ar[i] + rv)
 .Execute(0);
}
Mother answered 23/5, 2012 at 11:31 Comment(0)
M
4

One pattern to look for is a recursion call at the end of the function (so called tail-recursion). This can easily be replaced with a while. For example, the function foo:

void foo(Node* node)
{
    if(node == NULL)
       return;
    // Do something with node...
    foo(node->left);
    foo(node->right);
}

ends with a call to foo. This can be replaced with:

void foo(Node* node)
{
    while(node != NULL)
    {
        // Do something with node...
        foo(node->left);
        node = node->right;
     }
}

which eliminates the second recursive call.

Mistrust answered 1/10, 2008 at 20:48 Comment(3)
Still looks recursive to me... :)Fronia
Well, yeah - but it's half as recursive. Getting rid of the other recursion is going to require using another technique...Regulation
Resolving the remaining recursion would have been the most interesting part...Oceania
D
3

A question that had been closed as a duplicate of this one had a very specific data structure:

enter image description here

The node had the following structure:

typedef struct {
    int32_t type;
    int32_t valueint;
    double  valuedouble;
    struct  cNODE *next;
    struct  cNODE *prev;
    struct  cNODE *child;
} cNODE;

The recursive deletion function looked like:

void cNODE_Delete(cNODE *c) {
    cNODE*next;
    while (c) {
        next=c->next;
        if (c->child) { 
          cNODE_Delete(c->child)
        }
        free(c);
        c=next;
    }
}

In general, it is not always possible to avoid a stack for recursive functions that invoke itself more than one time (or even once). However, for this particular structure, it is possible. The idea is to flatten all the nodes into a single list. This is accomplished by putting the current node's child at the end of the top row's list.

void cNODE_Delete (cNODE *c) {
    cNODE *tmp, *last = c;
    while (c) {
        while (last->next) {
            last = last->next;   /* find last */
        }
        if ((tmp = c->child)) {
            c->child = NULL;     /* append child to last */
            last->next = tmp;
            tmp->prev = last;
        }
        tmp = c->next;           /* remove current */
        free(c);
        c = tmp;
    }
}

This technique can be applied to any data linked structure that can be reduce to a DAG with a deterministic topological ordering. The current nodes children are rearranged so that the last child adopts all of the other children. Then the current node can be deleted and traversal can then iterate to the remaining child.

Drawtube answered 8/7, 2016 at 10:58 Comment(0)
A
3

You can compare the source code below, Recursive function vs Reimplemented as "iterative" to intuitively understand the approach without reading this whole answer.

Some conversions are trivial and not nearly as involved as what's described here. But for non-trivial cases, this methodology should cover converting even the most complex recursive cases. Even those where there are multiple recursive call points in the code and return values used within the code itself.

Sometimes it may be better not to convert a complex recursive algorithm; for instance, where the recursive depth is predictable and minimal. Some sorting algorithms are good examples of this where the max depth of recursion is close to log2(n).

A case where conversion would be beneficial is where the recursive depth can go beyond the allocated call stack space. The stack of an iterative solution can scale to even unpredictably large data sets without necessitating stack allocation tuning. Unexpected bugs can be avoided as datasets continue to grow year after year.

With compiled languages, there may be a slight boost in speed of execution from conversion. However, with interpreted bytecode languages like Python, instead of the native engine managing the call stack, bytecode is managing an explicit stack, which may slightly impact performance.

The approach

A general way to convert a recursive function to an iterative solution that will apply to any case is to mimic the process natively compiled code uses during a function call and the return from the call.

Taking an example that requires a somewhat involved approach, we have the multi-key quicksort algorithm. This function has three successive recursive calls, and after each call, execution begins at the next line.

The state of the function is captured in the stack frame, which is pushed onto the execution stack. When sort() is called from within itself and returns, the stack frame present at the time of the call is restored. In that way all the variables have the same values as they did before the call - unless they were modified by the call.

Recursive function

def sort(a: list_view, d: int):
    if len(a) <= 1:
        return
    p = pivot(a, d)
    i, j = partition(a, d, p)
    sort(a[0:i], d)
    sort(a[i:j], d + 1)
    sort(a[j:len(a)], d)

Taking this model, and mimicking it, a list is set up to act as the stack. In this example tuples are used to mimic frames. If this were encoded in C, structs could be used. The data can be contained within a data structure instead of just pushing one value at a time.

Reimplemented as "iterative"

# Assume `a` is view-like object where slices reference
# the same internal list of strings.

def sort(a: list_view):
    LEFT, MID, RIGHT  = 0, 1, 2
    stack = []
    stack.append((LEFT, a, 0))                  # Initial frame.
    while stack:
        frame = stack.pop()  

        if len(frame[1]) <= 1:                  # Guard.
            continue

        stage = frame[0]                        # Where to jump to.

        if stage is LEFT: 
            _, a, d = frame                     # a - array/list, d - depth.
            p = pivot(a, d)
            i, j = partition(a, d, p)
            stack.append((MID, a, i, j, d))     # Where to go after "return".
            stack.append((LEFT, a[0:i], d))     # Simulate function call.

        elif stage is MID:                      # Picking up here after "call"
            _, a, i, j, d = frame               # State before "call" restored.
            stack.append((RIGHT, a, j, d))      # Set up for next "return".
            stack.append((LEFT, a[i:j], d + 1)) # Split list and "recurse".

        elif stage is RIGHT:
            _, a, j, d = frame
            stack.append((LEFT, a[j:len(a)], d)

        else:
           raise ValueError("Unreachable. Fix your code.")

When a function call is made, information on where to begin execution after the function returns is included in the stack frame. In this example, if/elif/else blocks represent the points where execution begins after return from a call. In C this could be implemented as a switch statement.

In the example, the blocks are given labels; they're arbitrarily labeled by how the list is partitioned within each block. The first block, "LEFT" splits the list on the left side. The "MID" section represents the block that splits the list in the middle, etc.

With this approach, mimicking a call takes two steps. First a frame is pushed onto the stack that will cause execution to resume in the block following the current one after the "call" "returns". A value in the frame indicates which if/elif/else section to fall into on the loop that follows the "call".

Then the "call" frame is pushed onto the stack. This sends execution to the first, "LEFT", block in most cases for this specific example. This is where the actual sorting is done regardless which section of the list was split to get there.

Before the looping begins, the primary frame pushed at the top of the function represents the initial call. Then on each iteration, a frame is popped. The "LEFT/MID/RIGHT" value/label from the frame is used to fall into the correct block of the if/elif/else statement. The frame is used to restore the state of the variables needed for the current operation, then on the next iteration the return frame is popped, sending execution to the subsequent section.

The structure of the frame tuple could vary for each branch of the switch or if-elif block to minimize the amount of data on the stack. Only place in a certain arm's frame what it needs, if possible, and overall space usage reduced.

Return values

I've found that the best approach to handling return values used within the converted code is to create a separate stack for return values. Any section of code that returns a value pushes it on this results stack, and any section of code that expects to receive a return value from a call, pops it from the results stack. For an example of this approach, you can check out this conversion of a recursive algorithm.

Applicant answered 21/8, 2021 at 12:20 Comment(0)
S
2

Recursion is nothing but the process of calling of one function from the other only this process is done by calling of a function by itself. As we know when one function calls the other function the first function saves its state(its variables) and then passes the control to the called function. The called function can be called by using the same name of variables ex fun1(a) can call fun2(a). When we do recursive call nothing new happens. One function calls itself by passing the same type and similar in name variables(but obviously the values stored in variables are different,only the name remains same.)to itself. But before every call the function saves its state and this process of saving continues. The SAVING IS DONE ON A STACK.

NOW THE STACK COMES INTO PLAY.

So if you write an iterative program and save the state on a stack each time and then pop out the values from stack when needed, you have successfully converted a recursive program into an iterative one!

The proof is simple and analytical.

In recursion the computer maintains a stack and in iterative version you will have to manually maintain the stack.

Think over it, just convert a depth first search(on graphs) recursive program into a dfs iterative program.

All the best!

Sumba answered 18/5, 2012 at 10:6 Comment(0)
D
1

There is a general way of converting recursive traversal to iterator by using a lazy iterator which concatenates multiple iterator suppliers (lambda expression which returns an iterator). See my Converting Recursive Traversal to Iterator.

Dickman answered 14/11, 2017 at 4:45 Comment(0)
H
1

Another simple and complete example of turning the recursive function into iterative one using the stack.

#include <iostream>
#include <stack>
using namespace std;

int GCD(int a, int b) { return b == 0 ? a : GCD(b, a % b); }

struct Par
{
    int a, b;
    Par() : Par(0, 0) {}
    Par(int _a, int _b) : a(_a), b(_b) {}
};

int GCDIter(int a, int b)
{
    stack<Par> rcstack;

    if (b == 0)
        return a;
    rcstack.push(Par(b, a % b));

    Par p;
    while (!rcstack.empty()) 
    {
        p = rcstack.top();
        rcstack.pop();
        if (p.b == 0)
            continue;
        rcstack.push(Par(p.b, p.a % p.b));
    }

    return p.a;
}

int main()
{
    //cout << GCD(24, 36) << endl;
    cout << GCDIter(81, 36) << endl;

    cin.get();
    return 0;
}
Heritable answered 14/6, 2018 at 12:3 Comment(0)
C
1

My examples are in Clojure, but should be fairly easy to translate to any language.

Given this function that StackOverflows for large values of n:

(defn factorial [n]
  (if (< n 2)
    1
    (*' n (factorial (dec n)))))

we can define a version that uses its own stack in the following manner:

(defn factorial [n]
  (loop [n n
         stack []]
    (if (< n 2)
      (return 1 stack)
      ;; else loop with new values
      (recur (dec n)
             ;; push function onto stack
             (cons (fn [n-1!]
                     (*' n n-1!))
                   stack)))))

where return is defined as:

(defn return
  [v stack]
  (reduce (fn [acc f]
            (f acc))
          v
          stack))

This works for more complex functions too, for example the ackermann function:

(defn ackermann [m n]
  (cond
    (zero? m)
    (inc n)

    (zero? n)
    (recur (dec m) 1)

    :else
    (recur (dec m)
           (ackermann m (dec n)))))

can be transformed into:

(defn ackermann [m n]
  (loop [m m
         n n
         stack []]
    (cond
      (zero? m)
      (return (inc n) stack)

      (zero? n)
      (recur (dec m) 1 stack)

      :else
      (recur m
             (dec n)
             (cons #(ackermann (dec m) %)
                   stack)))))
Cosignatory answered 28/4, 2020 at 6:20 Comment(0)
M
1

This is an old question but I want to add a different aspect as a solution. I'm currently working on a project in which I used the flood fill algorithm using C#. Normally, I implemented this algorithm with recursion at first, but obviously, it caused a stack overflow. After that, I changed the method from recursion to iteration. Yes, It worked and I was no longer getting the stack overflow error. But this time, since I applied the flood fill method to very large structures, the program was going into an infinite loop. For this reason, it occurred to me that the function may have re-entered the places it had already visited. As a definitive solution to this, I decided to use a dictionary for visited points. If that node(x,y) has already been added to the stack structure for the first time, that node(x,y) will be saved in the dictionary as the key. Even if the same node is tried to be added again later, it won't be added to the stack structure because the node is already in the dictionary. Let's see on pseudo-code:

startNode = pos(x,y)

Stack stack = new Stack();

Dictionary visited<pos, bool> = new Dictionary();

stack.Push(startNode);

while(stack.count != 0){
    currentNode = stack.Pop();
    if "check currentNode if not available"
        continue;
    if "check if already handled"
        continue;
    else if "run if it must be wanted thing should be handled"      
        // make something with pos currentNode.X and currentNode.X  
        
        // then add its neighbor nodes to the stack to iterate
        // but at first check if it has already been visited.
        
        if(!visited.Contains(pos(x-1,y)))
            visited[pos(x-1,y)] = true;
            stack.Push(pos(x-1,y));
        if(!visited.Contains(pos(x+1,y)))
            ...
        if(!visited.Contains(pos(x,y+1)))
            ...
        if(!visited.Contains(pos(x,y-1)))
            ...
}

Mady answered 1/7, 2021 at 11:3 Comment(0)
K
0

A rough description of how a system takes any recursive function and executes it using a stack:

This intended to show the idea without details. Consider this function that would print out nodes of a graph:

function show(node)
0. if isleaf(node):
1.  print node.name
2. else:
3.  show(node.left)
4.  show(node)
5.  show(node.right)

For example graph: A->B A->C show(A) would print B, A, C

Function calls mean save the local state and the continuation point so you can come back, and then jump the the function you want to call.

For example, suppose show(A) begins to run. The function call on line 3. show(B) means - Add item to the stack meaning "you'll need to continue at line 2 with local variable state node=A" - Goto line 0 with node=B.

To execute code, the system runs through the instructions. When a function call is encountered, the system pushes information it needs to come back to where it was, runs the function code, and when the function completes, pops the information about where it needs to go to continue.

Kellikellia answered 2/8, 2013 at 21:8 Comment(0)
P
0

This link provides some explanation and proposes the idea of keeping "location" to be able to get to the exact place between several recursive calls:

However, all these examples describe scenarios in which a recursive call is made a fixed amount of times. Things get trickier when you have something like:

function rec(...) {
  for/while loop {
    var x = rec(...)
    // make a side effect involving return value x
  }
}
Phanerozoic answered 30/11, 2014 at 4:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.