I want to try to give a general answer. Because recursion is in fact using a stack (the call stack), any recursive function can be converted to an iterative one using a stack in a straight-forward way. You can just simulate the call stack:
The call stack contains:
- the arguments
- the return address
- and the local variables.
Create a struct for these:
struct Context {
Arguments args;
LocalVars lvars;
ExecutionState state;
}
Arguments
is easy: The arguments are set to the values of the arguments for the recursive call.
The tricky part is the "return address" or as I called it here the ExecutionState
, and the LocalVars
.
ExecutionState
should keep track of where the call was made, to resume execution after the "recursive call" returns.
In your example, there are 3 possible states:
- evaluating
- returned from first call
- returned from second call
LocalVars
include the result
, and the Children
loop iterator. Because a call is made inside the loop, it has to be interrupted, and resumed later, so the loop iterator must be on the stack too.
Update: here I had written an untested C++/# pseudocode version. The OP (@tigrou) corrected some errors and made a working C# version, which he allowed me to include here:
enum ExecutionState
{
S_EVAL,
S_CALL1,
S_CALL2
};
enum NodeType
{
AND_OPERATOR,
OR_OPERATOR,
VALUE
};
class LocalVars
{
public IEnumerator<Node> Enumerator;
public bool Result;
}
class Context
{
public Node Node;
public LocalVars LocalVariables;
public ExecutionState State;
public Context(Node node, ExecutionState state)
{
this.Node = node;
this.State = state;
this.LocalVariables = new LocalVars();
}
};
class Node
{
public bool Value;
public NodeType Type;
public List<Node> Childrens;
};
static bool EvaluateNode(Node node)
{
Stack<Context> stack = new Stack<Context>();
Context context = new Context(node, ExecutionState.S_EVAL);
stack.Push(context);
bool returnresult = false;
while(stack.Any())
{
context = stack.Pop();
switch(context.State)
{
case ExecutionState.S_EVAL:
switch(context.Node.Type)
{
case NodeType.AND_OPERATOR:
if(context.LocalVariables.Enumerator == null)
{
context.LocalVariables.Enumerator = context.Node.Childrens.GetEnumerator();
context.LocalVariables.Result = true;
}
if(context.LocalVariables.Enumerator.MoveNext())
{
context.State = ExecutionState.S_CALL1;
stack.Push(context); // push resume with S_CALL1
Context call = new Context(context.LocalVariables.Enumerator.Current, ExecutionState.S_EVAL);
stack.Push(call); // push call
}
else
{
returnresult = context.LocalVariables.Result; // no push -> return
}
break;
case NodeType.OR_OPERATOR:
if(context.LocalVariables.Enumerator == null)
{
context.LocalVariables.Enumerator = context.Node.Childrens.GetEnumerator();
context.LocalVariables.Result = false;
}
if(context.LocalVariables.Enumerator.MoveNext())
{
context.State = ExecutionState.S_CALL2;
stack.Push(context); // push resume with S_CALL2
Context call = new Context(context.LocalVariables.Enumerator.Current, ExecutionState.S_EVAL);
stack.Push(call); // push call
}
else
{
returnresult = context.LocalVariables.Result; // no push -> return
}
break;
case NodeType.VALUE:
returnresult = context.Node.Value; // no push -> return
break;
}
break;
case ExecutionState.S_CALL1:
context.LocalVariables.Result = context.LocalVariables.Result && returnresult;
context.State = ExecutionState.S_EVAL;
stack.Push(context); // continue with S_EVAL
break;
case ExecutionState.S_CALL2:
context.LocalVariables.Result = context.LocalVariables.Result || returnresult;
context.State = ExecutionState.S_EVAL;
stack.Push(context); // continue with S_EVAL
break;
}
}
return returnresult;
}