
5.1. Проектирование регистровых машин
467
остановленное вычисление, машина должна сохранить содержимое всех регистров, ко-
торые ей понадобятся после того, как подзадача будет решена, а затем восстановить их,
прежде чем возобновить работу. В случае с факториалом мы сохраним старое значение
n и восстановим его, когда закончим вычисление факториала от уменьшенного значения
регистра n
2
.
Поскольку нет никакого априорного ограничения на число вложенных рекурсивных
вызовов, нам может понадобиться хранить произвольное число значений регистров. Зна-
чения эти требуется восстанавливать в порядке, обратном порядку их сохранения, по-
скольку в гнезде рекурсий последняя нач атая подзадача долж на завершаться первой.
Поэтому требуется использовать для сохранения значений регистров стек (stack), или
структуру данных вида « последним вошел, первым вышел». Можно расширить язык ре-
гистровых машин и до бавить в него стек, если ввести два новых вида команд: значения
заносятся на стек командой save и снимаются со стека при помощи команды restore.
После того, как последовательность значений сохранена на стеке , последовательность
команд restore восстановит их в обратном порядке
3
.
С помощью стека можно использовать для всех подзадач-факториалов единую копию
путей данных факториальной машины. Имеется подобная проблема и при использова-
нии последовательности команд контроллера, который управляет путями данных. Чтобы
запустить новое вычисление факториала, контроллер не может просто перейти в начало
последовательности, как в итеративном процессе, поскольку после решения подзадачи
поиска (n − 1)! машине тре буется еще домножить результат на n. Контроллер должен
остановить вычисление n!, решить подзадачу поиска (n −1)! и затем продолжить вычис-
ление n!. Такой взгляд на вычисление факториала приводит к использованию механизма
подпрограмм из раздела 5.1.3, при котором контроллер с помощью регистра continue
переходит к той части последовательности команд, которая решает подзадачу, а затем
продолжает с того места, где он остановился в главной задаче. Мы можем таким образом
написать факториальную подпрограмму, которая возвращается к точке входа, сохранен-
ной в регистре continue. При каждом вызове подпрограммы мы сохраняем и восста-
навливаем регистр continue подобно регистру n, поскольку все «уровни» вычисления
факториала используют один и тот же р егистр continue. Так что факториальная под-
программа должна записать в continue новое значение, когда она вызывает сама себя
для решения подзадачи, но для возврата в место, откуда она была вызвана для решения
подзадачи, ей потребуется старое значение continue.
На рисунке 5.11 показаны пути данных и контроллер машины, реализующей р екур-
сивную процедуру factorial. В этой машине имеются стек и три регистра с именами
n, val и continue. Чтобы упростить диаграм му путей данн ых, мы не стали давать
имена кнопкам присваивания регистров, и поименовали только кнопки работы со сте-
ком — sc и sn для сохранения регистров, rc и rn для их восстановления. В нач але
работы мы кладем в регистр n число, факториал которого желаем вычислить, и запус-
каем машину. Когда машина достигает состояния fact-done, вычисление закончено
и результат находится в регистре val. В последовательности команд контроллера n и
2
Казалось бы, незачем сохранять старое n; после того, как мы его уменьшим на единицу и решим подзадачу,
можно эту единицу добавить и восстановить старое значение. Такая стратегия работает для факториала, но в
общем случае она работать не может, поскольку старое значение регистра не всегда можно вычислить на
основан ии нового.
3
В разделе 5.3 мы увидим, как стек можно реализовать на основе более элементарных операций.