1

I have the following pseudo-code:

function X(data, limit, level = 0)
{
    result = [];
    foreach (Y(data, level) as entity) {
        if (level < limit) {
            result = result + X(entity, limit, level + 1);
        } else {
            //trivial recursion case:
            result = result + Z(entity);
        }
    }
    return result;
}

which I need to turn into a plain (e.g. without recursive calls). So far I'm out of ideas regarding how to do that elegantly. Following this answer I see that I must construct the entire stack frames which are basically the code repetitions (i.e. I will place same code again and again with different return addresses).

Or I tried stuff like these suggestions - where there is a phrase

  1. Find a recursive call that’s not a tail call.
  2. Identify what work is being done between that call and its return statement.

But I do not understand how can the "work" be identified in the case when it is happening from within internal loop.

So, my problem is that all examples above are providing cases when the "work can be easily identified" because there are no control instructions from within the function. I understand the concept behind recursion on a compilation level, but what I want to avoid is code repetition. So,

My question: how to approach transformation of the pseudo-code above which does not mean code repetitions for simulating stack frames?

Community
  • 1
  • 1
Alma Do
  • 37,009
  • 9
  • 76
  • 105

1 Answers1

1

It looks like an algorithm to descend a nested data structure (lists of lists) and flatten it out into a single list. It would have been good to have a simple description like that in the question.

To do that, you need to keep track of multiple indices / iterators / cursors, one at each level that you've descended through. A recursive implementation does that by using the call stack. A non-recursive implementation will need a manually-implemented stack data structure where you can push/pop stuff.

Since you don't have to save context (registers) and a return address on the call stack, just the actual iterator (e.g. array index), this can be a lot more space efficient.

When you're looping over the result of Y and need to call X or Z, push the current state onto the stack. Branch back to the beginning of the foreach, and call Y on the new entity. When you get to the end of a loop, pop the old state if there is any, and pick up in the middle of that loop.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • Could you elaborate a bit with pseudo-code? In terms with X,Y and Z calls? Also, it's not lists of lists iteration, it's actually tree traversing up to some level and flattening it (but it's similar I agree) – Alma Do Oct 28 '15 at 21:11
  • @AlmaDo: I expanded some. The https://en.wikibooks.org/wiki/Data_Structures/Stacks_and_Queues link I added (first google hit for "stack data structure") has an example of recursive and manual-stack functions for the towers of Hanoi problem. Have a look at how that function uses a stack. If you're still not getting it, expand on exactly where you're getting stuck when trying to figure this out. Is it how you save the state of a `foreach` loop? I don't know either, because you just posted pseudo-code in some made-up language that can return lists. Pushing the entire list would be silly. – Peter Cordes Oct 29 '15 at 02:43
  • "Manually implemented stack", etc, etc - sorry, but that thing I know. How to do that - is the question. I have the problem with clear implementation. What I have now is just copying stack frames (so, each frame contains same code and it has to be repeated as many times as we have levels defined). How to turn that to a clear stack implementation I can not grasp for now. Note, that problem is derived from the fact, that within one local space there is actually not one, but multiple recursive calls (the amount is equal to count of elements returned by `Y()` ) – Alma Do Oct 29 '15 at 15:37
  • @AlmaDo: That link wasn't the only thing I added in the last edit. The last two paragraphs address exactly what you're asking about. Check the diff. – Peter Cordes Oct 30 '15 at 16:12
  • Thank you, you have my vote - while I already resolved the case, it's still useful to read generic insights – Alma Do Oct 30 '15 at 16:19