Слова для манипуляций в стеке
Каждое из слов, приведенных в , рассматривается в этом разделе самостоятельно. В конце раздела мы приводим несколько задач на употребление этих слов, которые будут полезны для совершенствования ваших навыков в их использовании.
Прежде чем продолжить описание приведенных в таблице слов, обратим внимание на обозначения в скобках. Как вы уже видели в , в Форте принято применять такие обозначения (они называются диаграммой состояния стека), чтобы показать состояние стека до и после применения какого-либо слова. Правило хорошего тона -- помещать такую диаграмму сразу после имени определяемого слова. Поскольку все, что заключено с двух сторон в скобки (если только после открывающей скобки стоит пробел). Форт при вводе игнорирует, скобки можно использовать для помещения комментариев к Форт-словам. Вот, например, определение слова для сложения двух чисел и умножения на третье число:
: +* (n1 n2 n3 -- п) + * :
Таблица 2.1. Перечень слов для манипуляций в стеке
Слово | Состояние стека ( до - после ) | Действие |
DROP | ( n - ) | Очищает вершину стека |
DUP | ( n - n n ) | Делает копию числа на вершине стека |
SWAP | ( n1 n2 - n2 n1 ) | Переставляет местами два числа |
OVER | ( n1 n2 - n1 n2 n1 ) | Копирует второе число на вершину |
ROT | ( n1 n2 n3 - n2 n3 n1 ) | Перекладывает третье число на вершину |
n PICK | ( n1 ... - n1 ... n1 ) | Кладет на вершину копию п-го элемента |
n ROLL | ( n1 ... - ... n1) | Перекладывает п-й элемент на вершину |
?DUP | ( n - n n ) или ( 0 - 0 ) | Выполняет операцию DUP, если n == 0 |
DEPTH | ( ... - n ) | Возвращает в стек n -- число элементов |
Если мы вводим 3 4 5 + *, то до того, как слово будет исполнено, n1=3, n2=4, n3=5. После операции умножения в стеке находится n, которое имеет значение 27. Перечень принятых обозначений для содержимого стека приведен в .
Таблица 2.2. Обозначения для содержимого стека
Символ | Значение |
n, n1 , | 16-разрядное целое число одинарной длины |
d,d1, . . . | 32-разрядное целое число двойной длины |
U | 16-разрядное число без знака одинарной длины |
ud | 32-разрядное число без знака двойной длины |
char или с | 7-разрядное представление кода символа ASCII |
byte или Ь | 8-разрядное число, байт |
флаг или f | 1 или 0 - булев флаг |
адр, адр1 | Адреса |
$ или $адр | Адрес, где находится строка символов |
Если вы пока еще не усвоили эти обозначения, не огорчайтесь: они станут вам понятнее в последующих главах и мы привели их для использования в последующих ссылках. Обычно ход программы становится более понятным, если вы применяете более содержательные описатели в диаграммах состояния стека. Например, скорость можно обозначить как "скор.", а адрес переменной, содержащей значение скорости, -- как "адр.скор." и т.д. Другой более полный набор символов приведен в стандарте Форт-83, но лучше всего все-таки использовать содержательные обозначения.
А теперь вернемся к рассмотрению каждого из слов для манипуляций в стеке. DROP -- это самое простое слово; оно просто очищает вершину стека, например, после ввода
1 2 3 DROP .S
мы получим
1 2 Ok
Помимо того, что слово DROP полезно для уничтожения неправильно введенных данных при вычислениях (что-то вроде клавиши очистки регистра в калькуляторе), оно чаще всего используется для того, чтобы убрать какие-либо числа из стека во время выполнения программы. Очевидно, при пустом стеке мы получим
хххх DROP ? Stack EMPTY ! (стек пуст!)
или аналогичное сообщение об ошибке. Вас может удивлять, почему Форт не знает о том, что стек пуст, и почему он не игнорирует слово DROP в этом случае. Дело в том, что проверка на наличие ошибок и принятие решения, игнорировать их или не игнорировать, занимает немало времени. В отличие от большинства других языков программирования Форт состоит из полуавтономных процедур -- (слов языка Форт), каждое из которых оптимизировано с точки зрения быстродействия. Если бы в каждое из них было включено принятие решений об ошибках, исполнение слов происходило бы с более низкой скоростью. В Форте принято, что программист, а не язык несет ответственность за предотвращение ошибок. Форт дает вам полный контроль над компьютером, но в то же время и всю полноту ответственности. С другой стороны, разрабатывая программу, вы будете отлаживать каждое ее слово, легко обнаруживая ошибки. И поэтому Форт не нуждается в сложных и требующих больших затрат времени методах контроля ошибок, необходимых другим языкам.
Нетрудно догадаться по названию, что слово DUP (по-англ. "DUPlicate " -- копировать) делает копию верхнего элемента стека.
9 DUP .S
приводит к тому, что в стеке окажется
9 9 ok
Слово DUP, пожалуй, одно из наиболее часто употребляемых слов для манипуляций в стеке. Вы встречались с ним в одном из упражнений . Часто программе требуется одно и то же значение несколько раз. Слово SWAP также, судя по названию (от swap -- обменивать), переставляет местами два верхних элемента стека. Если ввести
3 18 .S
вы получите на экране
18 3 ok
что особенно полезно, если нужно сделать операцию вычитания или деления, но операнды стоят в неправильном порядке. После слова DUP оно применяется наиболее часто. Однако не допускайте, чтобы это сделало вас ленивым. Иногда проще применить слово SWAP перед операцией деления или вычитания вместо того, чтобы заранее предусмотреть в программе требуемый порядок следования чисел в стеке. Использование SWAP может ускорить составление программы, но на этапе исполнения увеличит время работы программы.
Действие слова OVER не очевидно из его названия (через). Попробуйте ввести
1 2 OVER .S
и вы увидите
1 2 1 ok
Можно понять, что второе число в стеке было скопировано на вершину стека. Другими словами, OVER как бы заставляет второй элемент стека "перепрыгнуть" через первый элемент на вершину. Оно очень похоже на слово SWAP, но в отличие от него не удаляет второй элемент стека. Слово может быть полезно во многих случаях, когда какое-либо число должно быть использовано несколько раз. В следующем примере дублируются два верхних элемента стека:
1 2 OVER OVER .S
при этом получается
1 2 1 2 ok
Дублирование двух верхних элементов стека очень полезно, когда в стеке нужно оставить для последующего использования два числа. Предположим, что вам нужно вывести на экран одновременно и сумму, и произведение чисел 5 и 7.
5 7 OVER OVER + . *
выдает в результате
12 35 ok
Конструкция OVER OVER полезна также для сравнения двух чисел.
Дальше мы узнаем, что существует слово 2DUP, которое выполняет ту же функцию, что и OVER OVER. Имеется целое семейство слов, которые оперируют в стеке парами чисел, но, так как они используются главным образом для операций с так называемыми числами двойной длины, мы их рассмотрим в .
Слово ROT (по-англ. "rotate" -- поворачивать) производит ротацию трех верхних чисел стека, т.е. перекладывает третье сверху число в стеке на его вершину, так что
1 2 3 ROT .8
приводит к
2 3 1 Ok
Третье сверху число в стеке перемещается на вершину стека, а следующие два числа продвигаются на одну позицию в глубь стека. Применения слова ROT поначалу не очевидны, но они также важны. Предположим, что вы хотите определить значение выражения (5+2) х (7+3), если в стеке находится 5 2 7 3.
Для решения введите
+ ROT ROT + х .
и вы получите
70 ok
Действие некоторых комбинаций слов, вроде приведенных в этом примере, достаточно трудно понять даже опытному программисту. Поэтому проследить, что происходит в стеке в последнем примере, вам поможет следующая таблица:
Операция | Содержимое стека | Содержимое стека | Операция |
(стек в начале) | (до - после) | (до - после) | |
(5 2 7 3 --) | + | (- 10 7) | |
+ | (-- 5 2 10) | х | (- 70). |
ROT | (- 2 10 5) | (- стек пуст) | |
ROT | (- 10 5 2) |
В отличие от предыдущих слов PICK требует аргумента -- числа, которое указывает, какое по счету число сверху вы хотите скопировать на вершину стека. Аргументом является номер числа, за начало принимается вершина стека, а счет ведется по направлению в глубь стека. Так, например, в стандарте Форт-79 операции
1 2 3 4 5 6 4 PICK .S
приводят к результату
1 2 3 4 5 6 3 ok
Но в стандарте Форт-83 вы увидели бы
1 2 3 4 5 б 2 ok
Это принципиальное и зачастую очень неудобное различие стандартов Форт-79 и Форт-83.
В Форт- 79 принято, что верхний элемент стека имеет номер 1, а в Форт-83 -- номер 0. Таким образом, в стандарте Форт-83 выражение
0 PICK
приводит к такому же результату, что и DUP, в то же время для Форт-79 стандартное слово DUP эквивалентно
1 PICK
Если ввести 0 PICK, в стеке окажется какая-нибудь чушь. Таким образом, PICK, доставая число из глубины стека, не производит в нем других изменений.
Слово ROLL похоже на PICK, но в отличие от него вынимаемое число при перемещении его на вершину на старом месте удаляется. Например, в стандарте Форт-79
1 2 3 4 5 6 5 ROLL .S
приведет к
1 3 4 5 6 2 Ok
в то же время та же операция в стандарте Форт-83 приведет к другому результату:
2 3 4 5 6 1 ok
Нумерация элементов в стеке в стандартах Форт-79 и Форт-83 отличается для оператора ROLL так же, как и для PICK. Эти различия в стандартах важно учитывать при переносе программ из одной версии в другую. Слова ROLL и PICK следует применять, если невозможно использовать ничего другого, так как они работают значительно медленнее, чем DUP, OVER и ROT (эти три слова определены не с помощью слов PICK и ROLL, а непосредственно в машинных кодах). К тому же слово ROLL работает значительно медленнее, чем PICK. Чтобы избежать путаницы, нужно держать в стеке не более четырех чисел, которые используются в данном слове, и, если вы будете придерживаться этого правила, вам редко потребуются слова PICK и ROLL.
А сейчас будет уместно сделать небольшой экскурс в стандарты языка Форт. Как уже указывалось во введении, существуют два известных стандарта -- Форт-79 и Форт-83. Ссылки на основные документы, описывающие стандарты, приведены в приложении В. Различия между стандартами зачастую невелики, однако имеют значение, как, например, в случае ROLL и PICK, и мы обратим на это ваше внимание, когда будем встречаться с этими различиями. Версия, с которой вы работаете, должна соответствовать одному из стандартов, и вы можете определить, какому именно, если введете 79-STANDARD или FORTH-83. Если без ошибки будет принято первое слово, то ваша версия совместима с Форт-79, если же без ошибки будет принято последнее слово, ваша версия соответствует стандарту Форт-83.
Слово ?DUP представляет собой специальный вариант слова DUP. Оно делает копию числа, находящегося на вершине стека, если оно не равно нулю, и не копирует число, если оно равно нулю, Например,
1 4 5 ?DUP .S
дает в стеке
1 4 5 5 ok,
в то время как
1 4 0 ?DUP .S
приводит в результате к
1 4 0 ok
Таким образом, в последнем случае слово ?DUP не производит никаких действий. Оно особенно полезно вместе с конструкцией IF...THEN, примеры этого будут приведены в последующих главах.
Последнее слово в , DEPTH, не производит никаких перестановок в стеке. Вы уже встречали его в . Оно подсчитывает количество чисел в стеке и выдает его на вершину стека. Если ввести
21 131 56 7 89 DEPTH
мы получим
5 ok
где 5 -- это число элементов, находившихся в стеке перед исполнением слова DEPTH. Мы увидим еще применение слова DEPTH в последующих упражнениях.