Any algorithm that uses a recursive coding style can be translated
algebraically to an algorithm that uses only loops and a stack to keep
track of state. Let's give it a try.
Recursive coding style
void move(int n, char from, char to, char hold)
{
if (n == 0) return;
else {
move(n - 1, from, hold, to);
printf("move top of %c to %c\n", from, to);
move(n - 1, hold, to, from);
}
}
You would call this with something like move(4, 'a', 'c', 'b') to get 4
disks from a to c.
To get rid of the self-calls, we need a stack to keep track of function
parameters and return locations. Each recursive call to move() is
replaced by pushing new parameter values on the stack then executiong a
'goto'. The recursive function return is implemented by popping the
stack and going to the same place in the code where execution would
have continued in the recursive version.
int main(void)
{
// stack for parameters and return labels.
struct stack_elt_t {
int n; char from, hold, to;
int rtn;
} stk[1000];
int p = 0; // stack pointer
// set up some names for top 2 stack locations
#define N stk[p].n
#define FROM stk[p].from
#define TO stk[p].to
#define HOLD stk[p].hold
#define RTN stk[p].rtn
// macro to push new params on the stack
#define PUSH(N_, FROM_, TO_, HOLD_, RTN_) \
do { struct stack_elt_t e = { N_, FROM_, HOLD_, TO_, RTN_ }; \
stk[++p] = e; } while (0)
// Set up initial parameter values.
N = 4; FROM = 'a'; TO = 'c'; HOLD = 'b';
RTN = 0;
// Our function becomes a goto target.
move:
// if statement remains the same
if (N == 0)
goto rtn; // return is a goto target, too.
else {
// equivalent of first recursive call
PUSH(N - 1, FROM, HOLD, TO, 1);
goto move;
return_from_move_1: // control returns here on pop
// no change here
printf("move top of %c to %c\n", FROM, TO);
// equivalent of second recursive call
PUSH(N - 1, HOLD, TO, FROM, 2);
goto move;
return_from_move_2: // control returns here on pop
}
rtn:
switch (stk[p--].rtn) {
case 0: break; // stack is now empty
case 1: goto return_from_move_1;
case 2: goto return_from_move_2;
}
return 0;
}
You can of course try to simplify this code including getting rid of
the gotos. Sometimes this can make the code easier to read. Sometimes
not.
Cheers!