To understand how recursion works you should know that local variables are stored on call stack in a sequence of locations called Stack Frame. When new recursive call
is made, new stack frame is pushed to call stack, hence new instances of local variables are allocated in memory.
If you want to convert recursion into iteration, one way is to do that is to mimic call stack.
You can design a class which is specific for your function (lets call it Frame, like stack frame) of which objects represent current execution state of a function.
If function returns value, then Frame class must contain a field which contains return value.
All lines in a function operate on that object, rather than on local variables. The only allowed local variable is a stack of Frame objects.
Then convert function starting with these guidelines:
1. Entry point of function dequeues Frame from stack.
2. Return statement is replaced with statements which gets current Frame's return value and discards current Frame;
then pop new Frame from stack and (if available) sets return value to appropriate variable in popped Frame.
3. Recursive call is replaced by creating new Frame and setting its fields which represent function arguments.
4. All other statements are changed to access fields in Frame, rather than local variables.
Now let's construct such solution (this is language-independant, works for Java or anything else):
retType f(arg1, arg2, ... argN)
{
statement1;
statement2;
...
statementP;
if (condition1) // This was missing in your example - recursive calls must end somewhere!
x = f(v11, v12, ..., v1N);
statementP+1;
...
statementQ;
if (condition2) // Multiple recursive calls make general case
y = f(v21, v22, ..., v2N);
statementQ+1;
...
statementR;
return retVal;
}
This function is converted into this form:
retType f(arg1, arg2, ..., argN)
{
retType retVal = default;
// Initialize call at depth 1:
Stack frameStack = new Stack();
Stack stageStack = new Stack();
Frame f = new Frame();
f.arg1 = arg1;
f.arg2 = arg2;
...
f.argN = argN;
// stages: 1 - statements 1-P, 2 - statements (P+1)-Q, 3 - statements (Q+1)-R, 4 - return statement
frameStack.push(f); // this means that frame f is being executed at stage 1
stageStack.push(1);
// while there is current frame, keep going
while (!frameStack.IsEmpty())
{
f = frameStack.Pop();
int stage = stageStack.Pop();
frameStack.Push(f);
stageStack.Push(stage + 1);
switch (stage)
{
case 1:
statement1(f);
...
statementP(f);
// Now make recursive call
if (f.condition1())
{
Frame f1 = new Frame();
f1.arg1 = f.stage1Arg1;
f1.arg2 = f.stage1Arg2;
...
f1.argN = f.stage1ArgN;
frameStack.Push(f1);
stageStack.Push(1);
}
break;
case 2:
statementP+1(f);
...
statementQ(f);
// Now make recursive call
if (f.condition2())
{
Frame f1 = new Frame();
f1.arg1 = f.stage2Arg1;
f1.arg2 = f.stage2Arg2;
...
f1.argN = f.stage2ArgN;
frameStack.Push(f1);
stageStack.Push(1);
}
break;
case 3:
statementQ+1(f);
...
statementR(f);
// Now execute return statement:
retVal = f.retVal;
frameStack.Pop();
stageStack.Pop();
// If call depth is greater than 1, then retVal must be passed to previous frame:
// If call depth is 1, execution will stop after this point,
// and retVal local variable will contain overall return value of the function
if (!frameStack.IsEmpty())
{
Frame parent = frameStack.Pop();
parent.calledRetVal = retVal;
frameStack.Push(parent); // Return frame to stack so that its execution continues, having retVal of recursive call available
}
break;
}
}
return retVal;
}