Во-первых, просто посмотрите на поток управления, убрав все остальное и добавив явные goto
s для выполнения, которое достигает метки при прямом выполнении.
NC: if (…) goto print;
goto NR;
NR: if (…) goto backtrack;
if (…) goto NR;
goto NC;
backtrack:
if (…) return;
goto NR;
print:
goto backtrack;
Теперь возьмите безусловные переходы и попробуйте переместить блоки, чтобы они представляли прямолинейное выполнение.
NR: if (…) goto backtrack;
if (…) goto NR;
goto NC;
NC: if (…) goto print;
goto NR;
print:
goto backtrack;
backtrack:
if (…) return;
goto NR;
Теперь исключите прямолинейные переходы
NR: if (…) goto backtrack;
if (…) goto NR;
NC: if (…) goto print;
goto NR;
print:
backtrack:
if (…) return;
goto NR;
Обратите внимание, что метка со всеми обратными goto является циклом:
for (;;) {
NR: if (…) goto backtrack;
if (…) continue;
NC: if (…) goto print;
continue;
print:
backtrack:
if (…) return;
}
Хм, мы можем изменить смысл NC: if()
и устранить goto print
. И goto backtrack
просто перепрыгивает через некоторые утверждения, что эквивалентно другому перевернутому if
.
for (;;) {
NR: if (! …) {
if (…) continue;
NC: if (! …) continue;
print:
}
backtrack:
if (…) return;
}
У цикла нет условия, но backtrack: … if(…) return;
просто выходит из него, поэтому переместите этот блок и условие в цикл.
for (;…; /* backtrack */ …) {
NR: if (! …) {
if (…) continue;
NC: if (! …) continue;
print:
}
}
Выглядит довольно хорошо, больше никаких gotos и никакой "подозрительной" структуры! Тем не менее, NC
должен быть точкой входа.
Вот где слепые, механистические преобразования компилятора завершаются неудачей. Я вижу три альтернативы:
- Ввести переменные для принудительного выполнения там для первой итерации цикла. На мой взгляд, это хуже, чем
goto
.
- Маскировка
goto NC;
как switch(0)
и вызов метки NC:
как case 0:
. Учитель, вероятно, не примет этот компромисс.
- Скопируйте блоки из
NC
в конец цикла и вставьте над началом цикла. Затем вы можете выделить их в функции. На самом деле, это слепая, механистическая трансформация. Ура. Я также буду представлять NR
и backtrack
как функции для единообразия.
.
NC();
if (…) {
print();
}
while ( backtrack(), … ) { // <- note comma operator
NR();
if (! …) {
if (…) continue;
NC();
if (! …) continue;
print();
}
}
Вероятно, есть более элегантное решение, которое включает просмотр содержимого кода, но для этого нужно меньше думать.