Стек операндов PostScript: манипуляции и математические операции
-Вторая статья в серии "PostScript"-
Капля камень точит
-Поговорка-
Аннотация:
Автор описывает стек операндов языка PostScript. Представлены операции
манипуляции со стеком и математические операции. Статья охватывает не
все свойства стека операндов. Следующие статьи продолжат эту тему.
Введение
Это --- вторая в серии статей о PostScript. Основная ее цель
заключается в рассмотрении операций со стеком. Стек операндов,
возможно, самая важная часть PostScript. Присваивания, арифметические
или математические операции, циклы и логические операции --- все
работает с этой специальной частью памяти. Она используется
PostScript для выполнения практически всего того, что мы от него
хотим. Стек хранит информацию так, что данные, положенные последними,
снимаются первыми. Вы можете представить себе его в виде трубы с одним
закрытым концом. Когда вы помещаете что-нибудь в эту трубу, оно
проталкивает все ее содержимое к закрытому концу, чтобы освободить
себе место. Таким образом последний загруженный элемент всегда будет
ближайшим к открытому концу. Элементами стека могут быть строчки,
числовые константы, ключи, блоки, и так далее.
Манипулирование стеком
Несмотря на то, что элементы на стеке упорядочены, существует
несколько стековых операций, позволяющих нам их
переупорядочить. Операции применяются к одному или нескольким
элементам на стеке. Операторы, по определению, работают с элементами
на стеке. Они могут принимать аргументы (операнды в терминологии
PostScript), а могут --- нет, в зависимости от операции. Если они
требуют операндов, они должны быть положены на стек перед их
вызовом. Оператор затем работает с этими операндами. Здесь мы приводим
список таких операторов с достаточными пояснениями. Затем мы дадим
несколько примеров для прояснения подробностей.
pop: Этот оператор удаляет верхний (положенный последним)
элемент со стека операндов.
exch: Этот оператор меняет местами два верхних элемента
стека операндов.
dup: Этот оператор создает копию верхнего элемента стека
операндов и кладет его на стек. Другими словами, он дублирует верхний элемент.
copy: Этот оператор требует целого операнда (параметра),
который должен быть помещен на стек операндов до вызова
оператора. Скажем, если параметр равен n, то оператор должен
записываться как n copy. После его выполнения n
верхних элементов стека будут скопированы и положены на стек. Другими
словами, copy дублирует набор элементов.
index: Этот оператор требует целого операнда (параметра),
который должен быть помещен на стек операндов до вызова
оператора. Скажем, если параметр равен n, то оператор должен
записываться как n index. После его выполнения n-ный
элемент стека, считая сверху, будет скопирован и копия положена сверху
на стек. Другими словами, index выбирает внутренний элемент и
кладет на стек его копию. Индексы элементов начинаются с 0
для самого верхнего элемента.
roll: Этот оператор требует двух целых операндов
(параметров), которые должны быть помещены на стек
операндов до вызова оператора. Скажем, если параметры
равны n и m, то оператор должен
записываться как m n index. Здесь m
задает количество участвующих в операции элементов, а
n --- количество шагов. Каждый шаг
определяется так, что верхний элемент стека становится
m-ным, а набор из m-1 элементов под
самым верхним сдвигается на одно место вверх. Если
n равно 2, то выполняется два таких шага. Иными
словами, m 2 roll эквивалентно m 1 roll m 1
roll. Параметр n может принимать
отрицательные значения. В этом случае он изменяет
направление перемещения элементов на обратное. Это значит,
что команда m n roll m -n roll оставляет стек
операндов неизменным. Индексы элементов начинаются с
0 для самого верхнего элемента.
clear: Этот оператор снимает все элементы со стека
операндов.
count: Этот оператор считает элементы на стеке
операндов. Результат кладется на стек в виде нового
элемента. Если вам не нужен этот новый элемент, вы можете
выполнить составную команду count pstack pop, где
pop удаляет новый элемент, созданный командой
count, после того, как результат подсчета
печатается при помощи файлового оператора pstack.
mark: Этот оператор кладет на стек элемент-маркер
(-marktype-). Такой элемент можно использовать для
деления множества элементов на подмножества. Два других
оператора cleartomark и counttomark ищут
этот элемент и генерируют ошибку, если не находят.
cleartomark: Этот оператор удаляет со стека все
элементы, начиная с верхнего и до первого
элемента-маркера. Сам объект -marktype- тоже
удаляется. Если на стеке нет такого объекта, происходит
ошибка.
counttomark: Этот оператор считает элементы,
начиная с верхнего и до первого встреченного
маркера. Результатом является целое значение, которое
кладется на стек верхним элементом. Сам объект-маркер не
включается в подсчет. Если на стеке нет такого объекта,
происходит ошибка.
Если вы хотите увидеть перечисленные операторы в работе,
вам нужно запустить интерпретатор PostScript. Как
упоминалось в первой статье, в мире Linux обычно
используется общедоступный интерпретатор
Ghostscript. Ghostscript можно запускать
по-разному, задавая соответствующие параметры в командной
строчке. Обычно вы просто запускаете команду gs в
среде X Window. Иногда это не работает из-за проблем с
конфигурацией X. Тогда выводится сообщение о невозможности
создать графическую консоль. Тогда нужно устранять проблемы
или просто заставить ghostscript использовать устройство
x11. Для этого введите gs
-sDEVICE=x11. Эта команда, или просто команда
gs, создает белое пустое окно, которое используется
для вывода во время работы. Так как в нашей статье не
затрагиваются операторы рисования, это окно нам не нужно и
можно от него избавиться. Для этого интерпретатор нужно
запустить с параметром -dNODISPLAY. При этом
выводится заголовок и потом --- приглашение
GS>. К этому моменту ghostscript готов выполнять
ваши команды. Стек операндов пуст.
Чтобы увидеть содержимое стека операндов, используется
файловый оператор pstack. Он называется файловым,
потому что посылает информацию о содержимом стека в
стандартный выходной файл, по умолчанию --- на
экран. Если вы введете эту команду, ничего не показывается,
и появляется новое приглашение GS>. Это значит, что
стек операндов пуст.
Чтобы положить элементы на стек операндов, вводите
элементы после приглашения. Например, если вы хотите
положить 1, просто вводите 1. После
этого выводится новое приглашение. Но, на этот раз,
приглашение выглядит так: GS<1>. Эта форма
приглашения отражает число элементов на стеке
операндов. Так что, если в какой-то момент вашей сессии
приглашение будет выглядеть как GS<123>, то
это будет значить, что на стеке операндов у вас 123
элемента.
Вы можете вводить сразу несколько элементов. Для этого
вводите элементы подряд, разделенные пробелами. Например,
если вы введете после приглашения 1 2 3 4 5 6, то
на стек кладутся элементы 1, 2,
3, 4, 5 и 6. Если вы
теперь выполните команду pstack, то будут
показаны все элементы, так что элемент, введенный
последним, будет выведен первым. Вот как выглядит такая
сессия:
GS>1 2 3 4
5 6
GS<6>pstack
6
5
4
3
2
1
GS<6>
Точно так же можно вводить элементы и печатать содержимое
стека одной командой. Все, что нужно сделать, --- это
дать команду pstack после введенных элементов. То
есть:
GS>1 2 3 4 5 6 pstack
6
5
4
3
2
1
GS<6>
До сих пор в качестве элементов мы использовали числа. Можно
использовать другие типы элементов, например, переменные,
или ключи, строчки, блоки и т.д. Позже мы вернемся к ним в
подробностях. Сейчас лишь отметим, что если вы попытаетесь
ввести одиночную букву a или строчку abc,
то получите сообщение об ошибке. Если вам нужно вводить
строчки, нужно заключать их в круглые скобки. Выше мы
говорили о специальном элементе-маркере. Вот пример сессии с
его демонстрацией:
GS>1 2 3 mark 4 5 6 pstack
6
5
4
-marktype-
3
2
1
GS<7>
Посмотрим теперь на некоторые примеры использования
операторов манипуляции со стеком.
GS>1 2 3 mark 4 5 6 pstack
6
5
4
-marktype-
3
2
1
GS<7>pop pstack
5
4
-marktype
3
2
1
GS<6>exch pstack
4
5
-marktype
3
2
1
GS<6>dup pstack
4
4
5
-marktype-
3
2
1
GS<7>2 copy pstack
4
4
4
4
5
-marktype
3
2
1
GS<9>5 index pstack
-marktype-
4
4
4
4
5
-marktype
3
2
1
GS<10>cleartomark cleartomark pstack
3
2
1
GS<3>3 1 roll pstack
2
1
3
GS<3>count pstack
3
2
1
3
GS<4>mark 7 8 pstack
8
7
-marktype-
3
2
1
3
GS<7>counttomark pstack
2
8
7
-marktype-
3
2
1
3
GS<8>clear pstack
GS>
Математические операторы
Кроме операций манипуляции со стеком, существуют также
арифметические и математические операторы. Ниже дан их
перечень. Примеры не приводятся в качестве упражнения читателю,
который должен теперь уметь их воспроизвести, глядя в вышеприведенный
пример сессии.
add: Эта команда принимает два числовых параметра. Если их
значения, скажем, m и n, то команда
задается как m n add. При этом сначала
m, а затем n кладется на стек. Потом над
ними выполняется оператор add. Он создает новый
элемент со значением, равным сумме m и
n. После завершения операции, m и
n не остаются на стеке. Вместо них верхним
элементом стека становится результат.
div: Эта команда принимает два числовых параметра,
участвующих в операции деления. Если их значения, скажем,
m и n, то команда задается как m n
div. Механизм тот же, что и для
add. Операция делит числа с плавающей
точкой. После ее выполнения на стеке остается только ее
результат, m и n удаляются.
idiv: Эта команда принимает два числовых
параметра,
участвующих в операции целочисленного деления. Если их
значения, скажем, m и n, то команда
задается как m n div. Все так же, как у
div, кроме того, что деление выполняется в целых
числах. Она работает так же, даже если параметры не
целочисленные.
mod: Эта команда принимает два целых
параметра. Она
вычисляет остаток от деления первого параметра на
второй. Если любой из параметров не является целым числом,
операция не работает. На стеке остается только результат.
mul: Аналогична add и
div. Бинарный оператор, требующий двух числовых
значений. Результатом является их произведение, сохраняемое
на стеке.
sub: Аналогична add, div и
mul. Отличие только в типе операции: она вычитает
значение второго параметра из значения первого. Результат
сохраняется на стеке операндов.
exp: Бинарный математический оператор. Принимает
два параметра. Первый --- основание, второй ---
степень. Он возводит значение основание в степень,
являющуюся значение второго операнда. Параметры должны быть
в пределах ограничений операции возведения в
степень. Результатом является число с плавающей точкой,
сохраняемое на стеке.
atan: This is another binary mathematical operator
for the evaluation of an angle. The angle is given in
degrees between 0 and 360. It needs two
parameters. The ratio of the first parameter to the second
equals the tangent of the angle to be evaluated. Any one of
the parameters may be zero but both cannot be given
zero values. The signs of the parameters determine the
quadrant where the result will lie. The positive values in
the first parameter corresponds to positive y
plane. Whereas, the positive values in the second parameter
mean positive x plane.
abs: Унарный математический оператор. Он принимает
один параметр, чье абсолютное значение является его
результатом. Так же, как и прежде, результат сохраняется как
новый элемент на стеке операндов.
neg: Меняет знак своего единственного
аргумента. Унарный арифметический оператор.
ceiling: Унарный оператор, вычисляющий ближайшее
сверху к операнду целое число.
floor: Унарный оператор, вычисляющий ближайшее
снизу к операнду целое число.
round: Унарный оператор, вычисляющий ближайшее
к операнду целое число.
truncate: Унарный оператор, удаляющий у своего
аргумента дробную часть.
sqrt: Унарный оператор, вычисляющий квадратный
корень своего аргумента.
cos: Унарный оператор, вычисляющий косинус своего
аргумента. Значение аргумента задается в градусах.
sin: Унарный оператор, вычисляющий синус своего
аргумента. Значение аргумента задается в градусах.
ln: Унарный оператор, вычисляющий натуральный
логарифм своего аргумента.
log: Унарный оператор, вычисляющий десятичный
логарифм своего аргумента.
Прежде, чем завершить эту статью, еще одно
замечание. Хотя мы уже, возможно, неявно, упоминали об этом,
параметры (операнды в терминологии PostScript) команд могут
создавать некоторые неприятные проблемы. Команда (оператор в
терминологии PostScript) ищет свои параметры на стеке. После
этого они используются и удаляются со стека операндов. Так
что, когда, специально или по ошибке, команде, требующей
параметров, передается стек без значений параметров, она или
сигнализирует об ошибке, если то, что окажется на вершине
стека, не подходит по типу, или просто удалит верхние
элементы стека. Пользователь должен быть очень внимателен.
Тем, кто хочет совершенствоваться в PostScript, мы
рекомендуем писать более сложные программы. В следующих
статьях этой серии мы продолжим рассказ о языке
PostScript. Ваши вопросы и комментарии будут с удовольствием
выслушаны.
|