Voglio provare a dare una risposta generale. Poiché la ricorsione è , infatti, utilizzando uno stack (lo stack di chiamate), qualsiasi funzione ricorsiva può essere convertita in una iterativa usando una pila in modo diretto. Puoi semplicemente simulare lo stack di chiamate:
Lo stack di chiamate contiene:
- gli argomenti
- l'indirizzo di ritorno
- e le variabili locali.
Crea una struttura per questi:
struct Context {
Arguments args;
LocalVars lvars;
ExecutionState state;
}
Arguments
è semplice: gli argomenti sono impostati sui valori degli argomenti per la chiamata ricorsiva.
La parte difficile è "l'indirizzo di ritorno" o come ho chiamato qui ExecutionState
e LocalVars
.
ExecutionState
dovrebbe tenere traccia di dove è stata effettuata la chiamata, per riprendere l'esecuzione dopo il ritorno della "chiamata ricorsiva".
Nel tuo esempio, ci sono 3 possibili stati:
- valutare
- restituito dalla prima chiamata
- restituito dalla seconda chiamata
LocalVars
include result
e Children
ciclo iteratore. Poiché una chiamata viene effettuata all'interno del ciclo, deve essere interrotta e ripresa in seguito, quindi anche l'iteratore del ciclo deve essere in pila.
Aggiornamento: qui ho scritto una versione pseudocodice C ++ / # non testata. L'OP (@tigrou) ha corretto alcuni errori e ha creato una versione C # funzionante, che mi ha permesso di includere qui:
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;
}