К управляющим конструкциям относятся операторы
цикла и условного исполнения. Некоторые из них нам уже встречались,
сейчас мы их рассмотрим более полно и систематично.
Самый простой цикл повторений реализует уже
знакомое нам слово repeat, которое снимает со стека
число повторений n и выполняемое действие (текст,
заключенный в фигурные скобки) и вызывает это действие n раз. Напомним из прошлого занятия:
/star{4 {ray 90 rotate} repeat} def
В этой процедуре в цикле рисуются четыре луча.
После каждого луча координаты поворачиваются на 90 градусов. Можно
таким способом нарисовать квадрат
4 {dup 0 rlineto 90 rotate} repeat
Слово dup здесь нужно для
дублирования лежащей в стеке длины стороны квадрата.
Более привычный при обычном программировании цикл
со счетчиком выполняется словом for, которое
снимает со стека три числа - b, d
и e и выполняемое действие и организует цикл, в
котором участвует счетчик, принимающий, как обычно, значения b, b+d, ... пока значение счетчика не
выйдет за предел e. При этом каждое выполнение тела
цикла начинается с того, что в стек помещается счетчик цикла. Таким
образом, цикл
0 1 2 11 {dup mul add} for
просуммирует квадраты неотрицательных чисел от 1
до 11 и сохранит результат (376) в стеке.
| Упражнение #1. |
| a) |
Нарисуйте “нотный стан” ≈ несколько групп
горизонтальных линий по пять линий в каждой. |
| b) |
Нарисуйте разграфленный лист бумаги.
|
| c) |
Нарисуйте прямоугольник, внутренность
которого заштрихована пунктирными линиями под углом 30
градусов. |
| d) |
Вот известная демонстрация оптической иллюзии
(косая штриховка нарушает ощущение параллельности линий).
Напишите программу, которая делает эту картинку.
|
Повторим использованные нами условные операторы.
Слово if снимает со стека булево
значение и действие и выполняет действие, если булево значение
истинно. Например, в таком цикле
100 100 moveto
1 1 10 {box
3 eq {gsave 1 0 0 setrgbcolor
fill grestore} if currentpoint
stroke shift}for
если предположить, что слово box рисует прямоугольник, а слово shift сдвигает начальную точку, мы получим линию из 10
прямоугольников, причем третий будет выкрашен в красный цвет.
А остальные прямоугольники? Они дожидаются
конструкции if ... else:
100 100 moveto
1 1 10 {box
gsave 3 eq {1 0 0} {0 0 1}
ifelse setrgbcolor fill grestore
currentpoint stroke shift}for
Заметьте, что конструкцию окраски в данном случае
можно было бы написать и так:
gsave 0 0 1 3 eq {3 1 roll} if setrgbcolor
fill grestore
Но таких “трюков” современное программирование не
одобряет!
Условные операторы открывают возможность еще одной
разновидности циклов - рекурсивно исполняемые процедуры. Подробное
обсуждение механизма рекурсии нам еще предстоит в этой
главе, пока же рассмотрим программу, которая рисует так называемую
ломаную Гильберта
David Hilbert, 1862 - 1943, выдающийся немецкий математик).
Обратите внимание, что ломаная составлена из
четырех одинаковых квадрантов - таких же кривых меньшего “порядка”,
повернутых, зеркально отраженных и соединенных перемычками.
Вот полный текст рисующей программы, даже с
минимальным комментарием (и, кстати, обратите внимание на начало
постскриптовского файла - первые его два символа):
%! Joseph Romanovsky, 1995 (yet)
% inspired by Pascal code by N.Wirth
(A+DS=P) and
% Metafont code by Kees van der Laan
/S{0 R rlineto currentpoint stroke moveto}def
/T{90 rotate}def /TM{T 1 -1 scale}def
/H {TM dup
0 gt {1 sub H S TM H S H T S -1 1
scale H 180 rotate 1 add} if
TM}def
/R 8 def
100 100 moveto
4 H pop % Order of the curve
showpage
Нас интересует сейчас, конечно, рекурсивная
процедура H. Распишем ее с подробным комментарием
/H{ % n -> n
% так описывают содержимое стека до и после
вызова процедуры
% в стеке лежит и сохраняется порядок
TM % это преобразование пространства
dup % копирование порядка
0 gt % условие выполнения действия
{1 sub H S TM H S H T S -1 1 scale H
180 rotate 1 add} % Само действие см. ниже
if
TM % повторное преобразование
}def
Поначалу важно заметить, что основную часть
процедуры составляет некое действие, выполняемое только при
положительном “порядке” - параметре, лежащем в стеке. Это действие
начинается и заканчивается изменением этого порядка - он на единицу
уменьшается, а потом его значение восстанавливается. Условно можно
записать так
{1 sub <core> 1 add}
Теперь распишем центральную часть процедуры
{1 sub
H % первый фрагмент
S % первый мостик
TM % поворот с отражением
H % второй фрагмент
S % второй мостик
H % третий фрагмент
T % поворот на 90 градусов
S % третий мостик
-1 1 scale % зеркальное отражение
H % четвертый фрагмент
180 rotate % поворот
1 add}
Рисующая мостики процедура S
/S{0 R rlineto currentpoint stroke moveto}def
сделана так, чтобы при рисовании мостика его
конечная точка оставалась текущей точкой контура. Параметр R определяет длину мостика.
Назад