В гл. 4 мы обсуждали вопрос о том, как нужно составлять программы, чтобы компьютер «принимал решения», выбирая одну из альтернатив определения в зависимости от результата проверки конкретного условия. Условный переход является одним из тех достоинств, которые делают компьютер незаменимым.
Теперь вы должны научиться писать определения, позволяющие повторять тот или иной фрагмент несколько раз в зависимости от заданных условий. Такой вид структуры управления называется циклом. Способность выполнять циклы — это второе и, вероятно, наиболее важное достоинство компьютеров. Если вы можете составить программу проверки одной платежной ведомости, то вы можете составить программу проверки и тысячи подобных ведомостей.
В настоящей главе мы рассмотрим циклы, в которых выполняются простые действия, например вывод чисел на вашем терминале,
Одна из циклических структур называется циклом со счетчиком. Здесь вы сами определяете число повторений цикла. На Форте для этого нужно задать начальное и конечное числа перед словом DO (в обратном порядке), а затем поместить слова, выполнение которых вы хотите повторять, между словами DO (ВЫПОЛНИТЬ) и LOOP (ЦИКЛ). Например:
: ТЕСТ 10 0 DO CR ." ПРИВЕТ " LOOP ;будет осуществлять возврат каретки и выводить слово ПРИВЕТ десять раз, потому что, если вычесть нуль из 10, вы получите 10.
ТЕСТ ПРИВЕТ ПРИВЕТ ПРИВЕТ ПРИВЕТ ПРИВЕТ ПРИВЕТ ПРИВЕТ ПРИВЕТ ПРИВЕТ ПРИВЕТКак и оператор IF ... THEN, относящийся к операторам управления, оператор DO ... LOOP должен размещаться в пределах (одного) определения. Число 10 в приведенном примере называется границей цикла (limit), а нуль — индексом (index). Общий вид оператора цикла со счетчиком: граница индекс DO ... LOOP
Теперь посмотрим, что происходит внутри цикла
DO ... LOOP:
Слово DO1 берет из стека данных границу и индекс и подготавливает их для слова
LOOP, которое по этим данным будет управлять циклическим процессом.
Далее выполняются слова, находящиеся внутри цикла.
1 Единоутробный братец птички ДОДО.
Получив управление, LOOP продвигается на единицу, после чего управление возвращается к DO. Когда LOOP «пересекает» финишную черту, электронный глаз переключает стрелку, разрешая тем самым выполняться операторам, следующим за словом LOOP.
Слово I помещает в стек данных текущее значение индекса (значение, на котором в этот момент стоит LOOP) на каждом шаге циклического процесса. Рассмотрим определение
: ДЕКАДА 10 0 DO I . LOOP ;
которое выполняется так, как показано ниже:
ДЕКАДА 0 1 2 3 4 8 6 7 8 9 ok
Заметьте, что цикл выполняется всего 10 раз — от 0 до 9. Мы не добираемся до 10, потому что LOOP пересекает границу в тот момент, когда 9 сменяется числом 10.
Вы можете выбрать любые числа, но так, чтобы значение границы превышало значение индекса:
: ПРИМЕР -243 -250 DO I . LOOP ; ПРИМЕР -250 -249 -24B -247 -246 -245 -244 ok
И в этом примере значение границы (-243) больше, чем начальный индекс (-250).
Есть несколько важных правил, которых нужно придерживаться, программируя циклы DO. Прежде всего в операторе DO начальный индекс никогда не должен совпадать с границей. Если вы хотите, чтобы цикл не был выполнен ни разу, и определили его следующим образом:
: ОШИБКА 10 10 DO I . LOOP ;
то не получите ожидаемого результата. Результат будет таков:
К сожалению, LOOP уже переступил финишную черту и не попадет под электронный глаз в течение длительного времени (до тех пор, пока не вернется назад через 65535 шагов)1.
Если вы не уверены в том, что граница и индекс в каком-то вашем определении не совпадут, используйте вместо DO слово ?DO, которое, как и первое, берет из стека (границу индекс --), но сразу передает управление LOOP в том случае, когда граница и индекс совпадают.
Возвращаясь к слову STARS из гл. 1, напишем его правильное определение:
: STARS ( число-звездочек) 0 ?DO 42 EMIT LOOP ;
При отсутствии в вашей системе слова ?DO используйте следующее определение:
: STARS ( число-звездочек) ?DUP IF О DO 42 EMIT LOOP THEN ;
С какими неприятностями вы еще можете столкнуться? В большинстве Форт-систем при выполнении цикла DO текущие значения счетчика хранятся в стеке возвратов (введенном в гл. 5). Это при-
1 Для пользователей систем, созданных до принятия Стандарта-83. В более старых системах цикл выполнился бы один раз (вместо 64К раз), но и это не то, что вам нужно.
водит к некоторым ограничениям. В частности, слово I можно употреблять только в том определении, в котором используются слова DO и LOOP. Вы не имеете права ввести такой текст:
: ТЕСТ I . ; : ДЕКАДА 10 0 DО ТЕСТ LOOP ;
Если вспомогательное определение (ТЕСТ) требует значения индекса, вы должны передать его через стек, например:
: ТЕСТ ( индекс -- ) . ; : ДЕКАДА 10 0 DO I ТЕСТ LOOP ;
Отметим еще несколько ограничений. Вы не должны перед словом DO помещать в стек возвратов с помощью слова >R промежуточные значения с тем, чтобы использовать их внутри цикла. Если же вы запрограммировали внесение некоторого значения в стек возвратов внутри цикла, то обязаны перед выходом из цикла, до начала выполнения LOOP (или слова LEAVE, которое мы рассмотрим ниже), запрограммировать его удаление словом R>.
Вы можете оставить в стеке какое-нибудь число, которое будет служить аргументом для некоторого объекта внутри оператора цикла. Например,
: ПРОИЗВЕДЕНИЯ ( n ) CR 11 1 DO DUP I * . LOOP DROP ;
даст вам следующий результат:
7 ПРОИЗВЕДЕНИЯ 7 14 21 28 35 42 49 56 A3 70 ок
или
8 ПРОИЗВЕДЕНИЯ 8 16 24 32 40 48 56 64 72 80 ok
Здесь вы просто умножаете текущее значение индекса на n на каждом шаге цикла. Заметьте, что необходимо размножить число n с помощью DUP внутри цикла, чтобы его копия была всегда доступна и убрать это число посредством DROP посае выхода из цикла.
Существует несколько «хитрых» примеров работы со стеком внутри цикла DO. Рассмотрим их на примере начисления сложных процентов. При заданном начальном остатке, скажем 1000 дол. и норме процента, допустим 6%, требуется написать определение для создания и вывода на печать таблицы, приведенной ниже:
1000 6 СЛОЖНЫЕ-ПРОЦЕНТЫ
Год 1 Сумма 1060
Год 2 Сумма 1124
Год 3 Сумма 1191
и т.д.
и т. д. на двадцать лет. Сначала мы загружаем слово R%, специфицированное в гл. 5, а затем создаем определение
: СЛОЖНЫЕ-ПРОЦЕНТЫ ( вклад процент -- )
SWAP 21 1 DO
CR ." Год " I . 3 SPACES 2DUP R% + DUP ." Сумма " .
LOOP 2DROP ;
На каждом шаге выполнения цикла мы применяем операцию 2DUP и таким образом обеспечиваем значение текущего остатка и неизменной нормы процента для следующего шага. После выхода из цикла по окончании вычислений мы убираем эти значения с помощью операции 2DROP.
Индекс может выступать в качестве некоторого условия для оператора IF. С учетом этой возможности вы можете предпринимать конкретные действия только на определенных шагах цикла, например:
: ПРЯМОУГОЛЬНИК
256 0 DО I 16 MOD 0= IF
CR THEN ." *" LOOP ;
Слово ПРЯМОУГОЛЬНИК выведет на печать 256 звездочек, причем после каждой 16-й звездочки он будет осуществлять возврат каретки на вашем терминале. В результате вы получите
**************** **************** **************** **************** **************** **************** **************** **************** **************** **************** **************** **************** **************** **************** **************** ****************
Ранее мы определили слово с именем ПРОИЗВЕДЕНИЯ, в котором содержался цикл DO. При желании можно было бы поместить слово ПРОИЗВЕДЕНИЯ внутрь другого цикла DO, например:
: ТАБЛИЦА CR 11 1 DO I ПРОИЗВЕДЕНИЯ LOOP ;
В результате мы получили бы таблицу умножения в таком виде:
1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20
3 6 9 12 15 18 21 24 27 30
и т.д.
10 20 30 40 50 60 70 80 90 100
так как I во внешнем цикле обеспечивает аргумент для слова ПРОИЗВЕДЕНИЯ.
Циклы тоже можно вкладывать один в другой только в пределах одного определения:
: ТАБЛИЦА CR 11 1 DO
11 1 DO I J * 4 .R LOOP CR LOOP ;
Обращаем ваше внимание на тот случай, когда во внутреннем цикле находится фрагмент
I J *
Если I копирует индекс того цикла, внутри которого само расположено, то слово J копирует индекс следующего внешнего цикла. Поэтому выражение I J * приведет к перемножению двух индексов. Таким образом мы создаем значения нашей таблицы.
Что можно сказать теперь о выражении
4 .R
Это всего лишь видоизмененный оператор, который используется для печати чисел в табличной форме с выравниванием их по вертикали. Цифра 4 определяет заданное число позиций в столбце. Новую таблицу можно теперь распечатать так:
1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 и. д.
Под каждое число выделено четыре позиции независимо от того, сколько цифр содержится в данном числе. Слово .R означает «вывод числа, выравненного вправо».
+LOOP
Если вы хотите, чтобы значение индекса на каждом шаге выполнения цикла изменялось не на единицу, а на некоторую другую величину, то вместо LOOP можно использовать слово +LOOP. Для этого слова в стеке должно находиться число, значение которого является приращением индекса на каждом шаге. Например, в определении
: ЧЕРЕЗ-ПЯТЬ 50 0 DO I . 5 +LOOP ;
индекс всякий раз будет увеличиваться на пять, что приведет к следующему результату:
ЧЕРЕЗ-ПЯТЬ 0 5 10 15 20 25 3В 35 40 45 ок
(Представляете, о каких гигантских шагах идет речь.)
Отрицательное приращение заставляет цикл выполняться в обратном направлении, например:
: СНИЖЕНИЕ -10 0 DO I . -1 +LOQP ;
дает такой результат:
СНИЖЕНИЕ 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 ок
Слово +LOOP начнет с нуля и с шагом, равным
-1, «пойдет» к финишной черте, пока не пересечет ее. Заметьте, что в этом направлении мы выполняем цикл фактически 11 раз, поскольку финишная черта всегда лежит между значением границы и ее значением
-1 независимо от того, в какую сторону перемещается
+LOOP. Завершение же цикла вызывает переход финишной черты.
Приращение может быть получено каким угодно образом, но на каждом шаге выполнения оно должно находиться в стеке. Рассмотрим пример:
: ПРИРАЩЕНИЕ ( приращение граница индекс -- ) DO I . DUP +LOOP DROP ;
Внутри этого определения непосредственно нет приращения. Оно будет взято из стека при выполнении слова ПРИРАЩЕНИЕ наряду с границей и индексом. Посмотрите, что происходит в данном случае:
1 5 0 ПРИРАЩЕНИЕ 0 1 2 3 4 ок 2 5 0 ПРИРАЩЕНИЕ 0 2 4 ок -3 -10 10 ПРИРАЩЕНИЕ 10 7 4 1 -2 -5 -8 ок
Наш следующий пример демонстрирует изменение приращения на каждом шаге выполнения цикла:
: УДВАИВАНИЕ CR 32767 1 DO I . I +LODP ;
Здесь индекс непосредственно используется в качестве приращения. Начиная с единицы он всякий раз удваивается, как это показано ниже:
УДВАИВАНИЕ 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 ок
Если бы в этом примере аргумент для +LOOP принял хотя бы один раз значение 0, то мы никогда не вышли бы из цикла — получился бы так называемый
бесконечный цикл1.

1 Для специалистов. Некоторые Форт-системы, созданные до принятия Стан-дарта-83, включают
/LOOP, которое, как и +LOOP, выбирает из стека приращение, но оно должно быть положительным. Это может привести к тому, что индекс превысит значение 32767, скажем, при индексации по адресам или номерам блоков, так как указанные числа принимают только положительные значения в отличие от тех ситуаций, когда выполняются операции над числами со знаком. Слово 4-
LOOP, удовлетворяющее Стандарту-83, снимает данную проблему.
Как уже отмечалось, слова DO и LOOP являются операторами управления и, следовательно, должны быть выполнены в пределах одного определения. Это означает, что вы не можете выполнить отладку определений, содержащих цикл, в режиме калькулятора, за исключением тех случаев, когда вы моделируете цикл сами.
Рассмотрим, каким образом «оперившийся» программист может заняться отладкой определения СЛОЖНЫЕ-ПРОЦЕНТЫ (из первого раздела настоящей главы). Прежде чем добавить сообщения ." , программист может кратко записать такой вариант на листе бумаги:
: СЛОЖНЫЕ-ПРОЦЕНТЫ ( вклад процент — )
SWAP 21 1 DO CR I . 2DUP R% + DUP . LOOP 2DROP ;
Он может вести отладку этого варианта за терминалом, используя . или .S для проверки результата на каждом шаге цикла. Такой «диалог» выглядит следующим образом:
При моделировании мы не используем фразы "граница счетчик DO", "I" и "DUP".
1000 6 SWAP .S<return> 6 1000 ok 2DUP .S<return> Шаг 1 6 1000 6 1000 ok R% .S<return> 6 1000 60 ok + .S<return> 6 1060 ok 2DUP R% + .S<return> 6 1124 ok 2DROP .S<return> Шаг 2 Стек ПУСТ ok
Цикл отлажен. Считая, что выполнен последний шаг, проверяем, не пуст ли стек.
Полезный прием. Как очистить стек и при этом эмоционально разрядиться. Иногда начинающий программист может случайно написать цикл, который после себя оставляет в стеке множество значений, например:
: ПЯТЕРКИ ( -- ) 100 0 DO I 5 . LOOP ;
вместо
: ПЯТЕРКИ ( — ) 100 0 DO I 5 * . LOOP ;
Если вы увидите, что кто-то попал в такую ситуацию (с вами-то, конечно, подобное никогда не произойдет), и, для того чтобы очистить стек, набирает бесконечную последовательность точек, посоветуйте ему не делать этого, а несколько раз наугад ударить по клавиатуре и нажать клавишу возврата каретки. В результате получится что-нибудь типа
ASDFKJ
что, естественно, не является словом Форта и приведет к тому, что текстовый интерпретатор вызовет ABORT, который, помимо всего прочего, почистит оба стека. (Но объясните новичку, что не нужно бить по клавишам при каждой ошибке.)
Наряду с циклами DO, которые называются циклами со счетчиком, Форт поддерживает циклы с условием. Цикл такого рода выполняется либо неопределенно долго, либо до тех пор, пока не произойдет некоторое событие. Один из видов цикла с условием —
BEGIN ... UNTIL
Этот цикл выполняется до тех пор, пока некоторое условие истинно. Форма его применения:
BESIN xxx ? UNTIL
Здесь ххх обозначает слова, которые должны повторяться в рамках цикла, а ? — некоторый флаг. Пока флаг принимает значение «ложь», цикл будет повторяться, но как только флаг примет значение «истина», цикл завершится.
Оператор цикла BEGIN ... UNTIL используется в одном из определений в примере со стиральной машиной, который рассматривался ранее:
: ДО-НАПОЛНЕНИЯ BEGIN ?ПОЛОН UNTIL ;
где данное определение применяется в другом определении более высокого уровня:
: НАЛИТЬ-ВОДУ КРАНЫ ОТКРЫТЬ ДО-НАПОЛНЕНИЯ КРАНЫ ЗАКРЫТЬ ;
Слово ?ПОЛОН будет определено таким образом, чтобы осуществлять с помощью электронной схемы проверку индикатора уровня воды в емкости машины, который сигнализирует о заполнении емкости, выдавая значение «истина» в стеке. Слово ДО-НАПОЛНЕНИЯ периодически осуществляет проверку снова и снова (тысячи раз в секунду) до тех пор, пока индикатор, наконец, не подаст свой сигнал, после чего цикл завершится. Затем слово ; в ДО-НАПОЛНЕНИЯ направит на выполнение оставшиеся слова в НАЛИТЬ-ВОДУ, и вода будет перекрыта.
Иногда программист вынужден создавать бесконечный цикл. На Форте это лучше всего сделать следующим образом:
ВЕGIN xxx FALSE UNTIL
(Слово FALSE выполняет те же функции, что и число 0, поэтому, если в вашей системе нет этого слова, используйте вместо него 0) Так как флаг, относящийся к UNTIL, всегда имеет значение
«ложь», цикл будет повторяться бесконечно. В некоторых системах слово AGAIN заменяет выражение FALSE UNTIL.
Новички обычно стараются избегать бесконечных циклов, поскольку вход в такой цикл означает, что они потеряли управление над компьютером (в том смысле, что при этом выполняются только слова, заключенные внутри цикла). Но бесконечные циклы имеют право на существование. Например, интерпретатор текста является частью бесконечного цикла под названием QUIT. Он ожидает поступления входных данных, интерпретирует и обрабатывает их, выводит "ok", а затем ожидает ввода следующей порции входных данных. В большинстве устройств, управляемых с помощью микропроцессоров, определения самого высокого уровня содержат бесконечный цикл, описывающий работу данного устройства.
Известна еще одна форма организации цикла с условием1:
BEGIN xxx ? WHILE yyy REPEAT
В таком варианте проверка на выход из цикла осуществляется в теле цикла, а не в его конце. Если условие проверки истинно, то оставшаяся часть цикла выполняется, а затем происходит возврат в начало цикла. Если же результат проверки ложный, цикл заканчивается.
Заметьте, что результат проверки условия в цикле описываемой конструкции противоположен результату проверки в цикле BEGIN ... UNTIL. В первом случае цикл повторяется до тех пор, пока некоторое условие истинно (а не до тех пор, пока оно не станет истинным).
Предположим, у нас имеется некоторая база данных, заполненная именами. Мы уже определили слово НОМЕР-ИМЕНИ, помещающее в стек либо номер следующего имени из списка, либо
1 REPEAT — ПОВТОРЯТЬ, WHILE — ПОКА. — Примеч. пер.
значение «ложь», если список исчерпан. Кроме того, у нас есть слово .ИМЯ, которое выводит само имя по его номеру в стеке.
: НОМЕР-ИМЕНИ ( -- номер-имени-или-ложь ) ... ; : .ИМЯ ( номер-имени -- ) ... ;
Допустим, нам нужно вывести список всех имен. До тех пор, пока не распечатан список, мы не знаем, сколько там имен, и поэтому не можем воспользоваться конструкцией DO LOOP, но можем написать определение:
: .ВСЕ-ИМЕНА
BEGIN НОМЕР-ИМЕНИ DUP WHILE CR .ИМЯ REPEAT PROP ;
А что произойдет, если список окажется пустым? На первом шаге выполнения цикла НОМЕР-ИМЕНИ поместит в стек нуль и вторая часть цикла никогда не будет исполнена. Слова WHILE исключает необходимость в специальной проверке.
Существует способ написания цикла со счетчиком, при котором выполнение цикла может закончиться раньше, чем будет достигнуто заданное значение границы. Для этого нужно с помощью слова LEAVE запрограммировать внутри цикла DO изменение условия с «истины» на «ложь». LEAVE заставляет цикл немедленно завершиться1.
Перепишем наше прежнее определение слова СЛОЖНЫЕ-ПРОЦЕНТЫ. Теперь мы не будем запускать цикл ровно 20 раз, а организуем завершение этого цикла либо после его 20-го выполнения, либо после удвоения денежной суммы — в зависимости от того, какое из событий произойдет раньше.
Добавим следующую фразу:
2000 > IF LEAVE THEN
как в следующем тексте:
: УДВОЕНО ( вклад процент — )
SWAP 21 1 DО
CR ." Год " I 2 .R 3 SPACES 2DUP R% + DUP
." Сумма " . DUP 2000 > IF
CR ." Более чем удвоено через" I . ." лет " LEAVE
THEN
LOOP 2DROP ;
1 Для пользователей систем, разработанных до принятия Стандарта-83. Ранее слово LEAVE вызывало завершение цикла при выполнении очередного слова LOOP или +LOOP.
В результате получим:
1000 6 УДВОЕНО
Год 1 Сумма 1060 Год 2 Сумма 1124 Год 3 Сумма 1191 Год 4 Сумма 1262 Год 5 Сумма 1338 Год 6 Сумма 1418 Год 7 Сумма 1503 Год 8 Сумма 1593 Год 9 Сумма 1689 Год 10 Сумма 1790 Год 11 Сумма 1897 Год 12 Сумма 2011 Более чем удвоено через 12 лет
В одном из упражнений в конце главы вам предлагается переработать слово УДВОЕНО таким образом, чтобы оно выбирало из стека в виде аргументов норму процента и начальную сумму и выполняло бы вычисления до получения удвоенной начальной суммы, после чего слово LEAVE завершало бы эти вычисления.
Слово LEAVE ведет себя, как и положено при использовании его во вложенных операторах цикла DO: оно просто «покидает» цикл, в котором находится. В одном цикле может употребляться несколько слов LEAVE. Цикл завершается при встрече первого из них.
Согласно Стандарту-83, любой код, размещенный после сочетания IF LEAVE THEN, не будет исполнен на последнем шаге. Как правило, лучше применять это сочетание в качестве последнего выражения перед LOOP.
Еще одно предупреждение: как и все условные операторы, слово LEAVE должно находиться в том же определении, что и условия, по которым оно покидает цикл (DO и LOOP). Запомните, что нельзя помещать LEAVE в определении слова, вызываемого из цикла. Приведем пример неправильного использования слова LEAVE:
: ВЫБОР ВАРИАНТ1 IF ВАРИАНТ2 LEAVE THEN ; : НЕПРАВИЛЬНЫЙ-ЦИКЛ 1000 0 DO ВЫЧИСЛЕНИЯ ВЫБОР LOOP ;
Здесь LEAVE используется вне цикла. Эти определения будут скомпилированы, но при выполнении приведут к непредсказуемому результату (возможно, даже к разрушению системы).
Два полезных приема. PAGE и QUIT. Для того чтобы придать более аккуратный вид данным, выводимым циклически (таким как таблицы и геометрические фигуры), перед выводом информации вам, возможно, придется очистить экран с помощью слова PAGE (СТРАНИЦА). Вы можете использовать слово PAGE непосредственно всякий раз, когда нужно очистить экран
PAGE ПРЯМОУГОЛЬНИК
При этом экран будет очищаться перед выводом прямоугольника, который вы определили ранее. А можно поместить слово PAGE один раз в начало определения:
: ПРЯМОУГОЛЬНИК РAGЕ 256 0 DO
I 16 MOD 0= IF CR THEN ." *" LOOP ;
Если вы не хотите, чтобы по завершении вычисления на экране появилось приглашение ok, примените слово QUIT (ВЫЙТИ). Вы можете использовать QUIT непосредственно:
ПРЯМОУГОЛЬНИК QUIT
а можете сделать его последним словом определения (перед точкой с запятой).
Ниже дается перечень слов Форта, приводимых в настоящей главе.
DO ... LOOP DO: ( граница Организация цикла со счетчиком по задан-
индекс -- ) ному диапазону индексов.
LOOP: ( -- )
DO ... +LOOP DO: ( граница Аналогично DO ... LOOP . Только к ин-
индекс — ) дексу на каждом шаге добавляется значение
+LOOP: ( n -- ) n (а не как всегда единица ).
LEAVE ( -- ) Немедленное завершение выполнения цикла
LOOP или +LOOP. (Используетея только
внутри цикла.)
BEGIN ... UNTIL: ( ? -- ) Организация цикла с условием, который за- UNTIL вершается, когда ? принимает значение истина.
BEGIN ххх WHILE: ( ? -- ) Организация цикла с условием, причем ххх WHILE ууу выполняется всегда, а ууу—только REPEAT если ? истинно.
.R ( u ширина- Вывод числа одинарной точности без зна-
поля -- ) кa. Число выровнено справа по границе поля.
PAGE ( -- ) Чистка экрана дисплея и установка курсора
в верхний левый угол.
QUIT ( -- ) Завершение выполнения текущей задачи и
возврат управления на терминал.
Бесконечный цикл. Циклическая структура, позволяющая,повторять слова, содержащиеся внутри цикла, без зозможности внешнего прерывания. Остановить такие вычисления можно только отключением или перезагрузкой компьютера.
Цикл со счетчиком. Циклическая структура, позволяющая повторять слова, содержащиеся внутри цикла, определенное число раз. В Форте это число зависит от начального и конечного счетчиков (индекса и границы), которые помещаются в стек до выполнения слова DO.
Цикл с условием. Циклическая структура, при которой слова, содержащиеся внутри цикла, повторяются до тех пор, пока не изменится некоторое условие истинности (с истины на ложь или со лжи на истину). В Форте бесконечный цикл начинается со слова BEGIN.
В задачах с 6.1 и по 6.6 вы должны создать определения нескольких слов, которые будут выводить шаблоны из звездочек. Это потребует применения циклов DO и BEGIN ... UNTIL.
6.1. Создайте слово с именем STARS, которое выводит п звездочек в одной строке, п задается в стеке.
6.2. Определите слово БРУСОК, которое выводит прямоугольник из звездочек. Ширина и высота (число строк) задаются в стеке в следующем порядке: ( ширина высота -- ).
10 3 БРУСОК ********** ********** ********** ok
6.3. Создайте слово \STARS, которое выводит наклонные (ромбовидные) массивы из звездочек. Высота задается в стеке. Используйте цикл DO и для простоты примите ширину постоянной и равной 10 звездочкам.
3 \STARS ********** ********** ********** ok
6.4. Определите слово, которое выводит массивы из звездочек с наклоном в другую сторону, и назовите его /STARS. Высота должна задаваться в стеке, а ширину возьмите постоянной и равной 10 звездочкам. Используйте оператор DO.
6.5. Переопределите последнее слово с использованием оператора цикла BEGIN ... UNTIL.
|
6.6. Напишите определение с именем РОМБЫ, которое выводит заданное число ромбов, как показано в следующем примере:
6.7 В гл. 3 было введено слово THRU. Сможете ли вы написать определение этого слова с помощью вызова LOAD? Если хотите, расширьте функции слова так, чтобы при использовании его во время загрузки блока выводился бы номер последнего. 6.8. При рассмотрении слова LEAVE мы приводили пример, в котором вычислялись сложные проценты при норме процента 6% и начальном остатке 1000 дол. за 20 лет или же до тех пор, пока начальный остаток не удвоится, в зависимости от того, какое из событий наступит раньше. Перепишите это определение таким образом, чтобы значения начальной суммы и нормы процента находились в стеке и процесс вычисления завершился с помощью LEAVE как только сумма начального остатка удвоится. 6.9. Определите слово с именем **, которое вычисляло бы степень, например: 7 2 ** . 49 ok ( семь в квадрате ) 2 4 ** . 16 ок ( два в четвертой степени ) Для простоты показатель степени примите положительным, но убедитесь в том, что ** выполняется правильно, если показатель равен нулю (в результате должна получиться единица) или единице (в результате должно получиться само число). |
2 РОМБЫ *
***
*****
*******
*********
***********
*************
***************
*****************
*******************
*******************
*****************
***************
*************
***********
*********
*******
*****
***
*
*
***
*****
*******
*********
***********
*************
***************
*****************
*******************
*******************
*****************
***************
*************
***********
*********
*******
*****
***
*
|