Goto is a useful precursor for writing a tail recursive solution.
For instance, say we have a state machine for recognizing that the last four button presses were 1234.
int keypad() // returns 1 if correct key is entered, otherwise loops forever
{
keypad:
switch (getkey()) {
case 1:
goto got_1;
default:
goto keypad;
}
got_1:
switch (getkey()) {
case 2:
goto got_2;
case 1:
goto got_1;
default:
goto keypad;
}
got_2:
switch (getkey()) {
case 3:
goto got_3;
case 1:
goto got_1;
default:
goto keypad;
}
got_3:
switch (getkey()) {
case 4:
return 1;
case 1:
goto got_1;
default:
goto keypad;
}
}
Now, refactor the same structure into tail calls. Now it's suddenly beyond reproach.
int got_1();
int got_2();
int got_3();
int keypad()
{
switch (getkey()) {
case 1:
return got_1();
default:
return keypad();
}
}
int got_1()
{
switch (getkey()) {
case 1:
return got_1();
case 2:
return got_2();
default:
return keypad();
}
}
int got_2()
{
switch (getkey()) {
case 3:
return got_3();
case 1:
return got_1();
default:
return keypad();
}
}
int got_3()
{
switch (getkey()) {
case 4:
return 1;
case 1:
return got_1();
default:
return keypad();
}
}
You can refactor any flow control graph based on goto, with local variables, into a network of tail calling functions. The tail call graph is isomorphic to the goto: it's just as complex and hard to understand. Yet it has the virtue of being beyond the reproach of computer science academia.