[ИУ9] Основы информатики

Лекция 12. Вычисления на стеке, конкатенативное программирование

Конкатенативное программирование — парадигма программирования, в которой композиция функций выражается через конкатенацию строк. Примеры языков: FORTH, Joy, Factor.

Т.е. пусть у нас есть две программы P1 и P2, конкатенация этих двух программ P1 P2 будет выражать применение P2 к результату выполнения P1.

В конкатенативных языках явных переменных нет, данные передаются неявно, через стек. Программа представляет собой последовательность операторов, каждый из которых выполняет какую либо операцию со стеком. Частный случай: константы — это тоже операторы, которые на стек кладут соответствующее значение.

В конкатенативных языках программирования принято записывать стек, растущий слева направо, причём верхушка стека расположена справа.

Действия операторов принято записывать как

оператор : ... стек до  =>  ... стек после

Например:

+ : ... x y  =>  ... (x+y)

Программа пишется в обратной польской записи или постфиксной записи: сначала записываются операнды, а потом сама операция.

Местность (арность) операции, функции — количество операндов (аргументов) у неё.

Коместность (коарность) — количество возвращаемых значений.

Поскольку местность каждой операции фиксирована, скобки не нужны.

Пример.

Выражение в обычной (инфиксной записи):

(2 - 1) * (3 + 4)

Выражение в постфиксной записи (обратной польской):

2 1 - 3 4 + *

Можно добавить скобки для наглядности, но их никто не ставит:

((2 1 -) (3 4 +) *)

Местность констант 0 (не принимают аргументов), коместность — 1.

константа : ...  =>  ... константа

Обратная польская запись допускает простую и эффективную реализацию:

Пример:

Стек                Программа

...                 2 1 - 3 4 + *
                    ↑
... 2               2 1 - 3 4 + *
                      ↑
... 2 1             2 1 - 3 4 + *
                        ↑
... 1               2 1 - 3 4 + *
                          ↑
... 1 3             2 1 - 3 4 + *
                            ↑
... 1 3 4           2 1 - 3 4 + *
                              ↑
... 1 7             2 1 - 3 4 + *
                                ↑
... 7               2 1 - 3 4 + *
                                  ↑
программа завершилась

Как выполняются вызовы функций в стековом языке программирования

Функции в FORTH принято называть статьями, хранилище функций — словарём.

Программа на языке FORTH состоит из последовательности слов, словом может быть или целочисленная константа, или некоторое имя. Часть слов предопределены (встроены в язык), часть определяются пользователем в виде статей:

Определение статьи выглядит так

 : ИМЯ  слова…  ;

Знак : начинает определение, знак ; — заканчивает.

Для вызовов функций вводится второй стек — стек возвратов. В основном стеке, стеке данных находятся значения, которыми обмениваются операции, в классическом FORTH’е это целые числа. В стеке возвратов хранятся адреса команд в словарных статьях.

Интерпретатор работает в следующем цикле:

Некоторые встроенные слова FORTH:

Пример программы на FORTH. Функция (слово) hypot вычисляет гипотенузу прямоугольного треугольника:

\ square : ... x  =>  ... x²
: square DUP * ;

\ hypot : ... x y  =>  ... √(x²+y²)
: hypot square SWAP square + SQRT ;

Выполнение слова square:

Стек             Программа
--------------------------
... x            DUP * ;
                 ↑
... x x          DUP * ;
                     ↑
... x²           DUP * ;
                       ↑
происходит возврат из square

Выполнение слова hypot:

Стек             Программа
--------------------------
... x y          square SWAP square + SQRT ;
                 ↑

Когда слово square вызывается, на стек возвратов кладётся указатель на слово SWAP в определении слова hypot.

Стек             Программа
--------------------------
... x y²         square SWAP square + SQRT ;
                        ↑
... y² x         square SWAP square + SQRT ;
                             ↑
... y² x²        square SWAP square + SQRT ;
                                    ↑
... y²+x²        square SWAP square + SQRT ;
                                      ↑
... √(y²+x²)     square SWAP square + SQRT ;
                                           ↑
происходит возврат из hypot

Пример, характерный для FORTH:

: 2 3 ;
2 2 * .

Она выведет 9, а не 4.

: + - ;
10 5 + .

Выведет 5, а не 15.

Слово с переменным числом параметров:

: SUM DUP WHILE + SWAP DUP WEND DROP ;

Сложит все числа на стеке до ближайшего нуля.

1 2 3 0 4 5 6 SUM   =>  1 2 3 15
===== ~~~~~~~           ===== ~~

1 2 3 0 4 5 6          DUP WHILE + SWAP DUP WEND DROP ;
                       ↑
1 2 3 0 4 5 6 6        DUP WHILE + SWAP DUP WEND DROP ;
                           ↑
1 2 3 0 4 5 6          DUP WHILE + SWAP DUP WEND DROP ;
                                 ↑
1 2 3 0 4 11           DUP WHILE + SWAP DUP WEND DROP ;
                                   ↑
1 2 3 0 11 4           DUP WHILE + SWAP DUP WEND DROP ;
                                        ↑
1 2 3 0 11 4 4         DUP WHILE + SWAP DUP WEND DROP ;
                                            ↑
1 2 3 0 11 4 4         DUP WHILE + SWAP DUP WEND DROP ;
                           ↑
1 2 3 0 11 4           DUP WHILE + SWAP DUP WEND DROP ;
                                 ↑
1 2 3 0 15             DUP WHILE + SWAP DUP WEND DROP ;
                                   ↑
1 2 3 15 0             DUP WHILE + SWAP DUP WEND DROP ;
                                        ↑
1 2 3 15 0 0           DUP WHILE + SWAP DUP WEND DROP ;
                                            ↑
1 2 3 15 0 0           DUP WHILE + SWAP DUP WEND DROP ;
                           ↑
1 2 3 15 0             DUP WHILE + SWAP DUP WEND DROP ;
                                                 ↑
1 2 3 15               DUP WHILE + SWAP DUP WEND DROP ;
                                                      ↑