Выбрать главу

#(вт) «Включение трассировки» (один аргумент). Эта функция с пустым значением заставляет процессор начать трассировку выполнения функций. Всякий раз, когда функция готова к выполнению, на устройство вывода выдаются все ее аргументы. Если процессор работает в интерактивной системе, то после печати каждого списка аргументов делается пауза, позволяющая пользователю ввести некоторую цепочку. Если это будет пустая цепочка, то процессор продолжает работу, вычисляя функцию; любая другая цепочка вызывает переход к шагу 1 алгоритма Трак.

#(кт) «Выключение (конец) трассировки» (один аргумент). Эта функция с пустым значением выключает трассировку; если трассировка не была включена, то выполнение функции не оказывает никакого воздействия.

Примеры

Следующие примеры помогут вам почувствовать некоторые возможности языка Трак. Не думайте, однако, что эти конкретные примеры иллюстрируют все возможности каких-либо функций.

#(оц,АА,Кот)’

#(оц,ВВ,(#(вц,АА)))’

#(пц,(#(вц,ВВ)))'

#(пц,##(вц,ВВ))'

#(пц,#(вц,ВВ))’

Выполнение первой строки из этого ряда программ (заметьте, что каждая строка оканчивается металитерой — апострофом) приводит к запоминанию бланка с именем АА и телом Кот и к печати пустой цепочки. Вторая строка аналогичным образом создает бланк с телом #(вц,АА) и именем ВВ и печатает пустую цепочку. Дальше начинается самое интересное. Функция пц в третьей строке печатает #(вц,ВВ), поскольку внутренняя функция защищена скобками; та же функция в четвертой строке печатает #(вц,АА), поскольку внутренняя функция — нейтральная и, наконец, при выполнении пятой строки печатается Кот.

#(уо,#(си,(,)))'

Эта программа делает точно то же, что и вызов #(ув). Аргументами уо должны быть имена бланков, и си генерирует список имен, разделенных запятыми.

#(оц, Факториал,(#(рв,Х,1,1,(#(ум,X,#(вц, Факториал,#(вч,Х,1)))))))'

#(cц, Факториал,X)’

#(вц, Факториал,5)’

В этих трех строках стандартным рекурсивным способом определяется функция факториал, цепочка Факториал сегментируется по аргументу X и затем вычисляется значение 5!. Необходимы обе пары защитных скобок; внешняя защищает рв от выполнения в момент создания бланка, а внутренняя позволяет избежать умножения в случае истинности условия. Попытайтесь удалить любую из этих пар скобок или заменить вызов рв на нейтральный.

#(вц, Факториал,5

#(оц, Факториал,(

#(рв,Х,1,

1,

#(ум,X,#(вц, Факториал,#(вч,Х,1)))))))

#(cц, Факториал,X)’

Этот пример делает то же, что предыдущий — определяет функцию факториал и вычисляет 5!. Однако здесь используется то, что аргументы функции вычисляются до вычисления самой функции; это позволяет спрятать определение и сегментацию бланка Факториал внутрь его вызова. Отметим, что после аргумента 5 вызова Факториал не нужно ставить запятую, поскольку он всегда возвращает в качестве значения пустую цепочку.

Тема. Напишите процессор Трака для вашей системы. Процессор должен реализовывать описанные выше алгоритм Трак и встроенные функции. Если в вашей системе имеется возможность хранения файлов, используйте ее для организации внешней памяти бланков. Блоки, записанные в каком-либо сеансе работы с процессором, должны быть доступны в последующих сеансах. Разумно будет включить в ваш процессор как можно больше внутренних средств отладки. Рекомендации исполнителю. В каждой системе приняты свои соглашения относительно набора литер и способа завершения строки. Один из шагов алгоритма Трак выбрасывает из сканируемого текста незащищенные литеры возврата каретки, перевода строки и табуляции. Это позволяет спокойно вводить исходные данные, не думая о возможности случайного разбиения программы на несколько строк вследствие ограничений на длину строки физических устройств. Но выбрасывание этих литер означает также, что их следует явно указывать во входном потоке, чтобы мы могли при желании управлять форматом вывода программы. Так, например, если CR-LF обозначает последовательность литер «возврат каретки — перевод строки», то при вводе цепочки Один CR-LF Два мы увидим на печати ОдинДва в одной строке, в то же время исходные данные Один(CR-LF)Два приведут к печати слова Один на одной строке, а Два — на следующей. Позаботьтесь о том, чтобы ваш процессор правильно работал в этой ситуации. Описывая алгоритм, мы лихо проскочили вопрос об установке и поиске меток аргументов и функций в нейтральной цепочке. На самом деле весьма важно всегда иметь точный список функций и аргументов, в котором указан, в частности, порядок их появления. Проще всего добиться этого с помощью стека. Этим стеком распоряжается исключительно процессор. Каждый раз, когда алгоритм выполняет шаг 1, стек устанавливается в пустое состояние. При маркировке любого начала функции на стек помещается новый блок функции. В блоке функции имеется основная часть постоянного вида, включающая, как минимум, положение в стеке ближайшего нижнего, блока функции, режим вызова данной функции, положение в нейтральной цепочке первой литеры функции, число аргументов функции, обработка которых начата, и число законченных аргументов и положение в нейтральной цепочке начала аргумента, который в данный момент сканируется. Выше постоянной части в стеке располагаются элементы, указывающие для каждого законченного аргумента его начало и конец. Часть этой информации может неявно присутствовать в других местах. Среди типичных ошибок реализации — неправильная работа с пустой цепочкой и потеря информации о функциях, расположенных в нижней части стека, при обработке вышележащих функций.