ЦИКЛ DO...LOOP_
DO, LOOP. UNTIL, WHILE, EXIT IF
Beta Basic в дополнение к имеющемуся в базовой
версии циклу типа FOR...NEXT располагает еще одной подобной
конструкцией — циклом DO...LOOP. Как и его «собрат»,
он также состоит из оператора начала цикла, тела и оператора завершения
цикла. Кроме того, в теле цикла могут располагаться один или несколько
операторов выхода из цикла по условию. В общем виде конструкция цикла
DO...LOOP выглядит так:
DO [WHILE <пог. выражение) | [UNTIL Слог, выражение)]
[EXIT IF <лог. выражение)]
LOOP [WHILE Слог, выражение)] | [UNTIL Слог, выражение)]
Специфика цикла DO...LOOP заключается в том,
что выход из него происходит не после определенного количества
повторений тела цикла, как в FOR...NEXT, а по выполнении одного из
условий, заданных логическими выражениями в начале цикла после
оператора DO, в конце цикла после оператора LOOP или одним из
операторов EXIT IF в теле цикла. Во всех случаях после завершения цикла
управление передается оператору, стоящему после LOOP.
Beta Basic предусматривает два типа критериев выхода из цикла, похожих друг на друга «с точностью до наоборот»:
WHILE Слог, выражение) предписывает циклу повторяться, пока Слог, выражение) истинно. Таким образом, WHILE задает критерий продолжения цикла;
UNTIL Слог, выражение) допускает повторение цикла до тех пор, пока Слог,
выражение) не станет истинным, то есть предписывает прекратить цикл,
как только будет соблюдено условие. Тем самым UNTIL задает критерий
окончания цикла.
Налицо очевидная избыточность, поскольку UNTIL
Слог, выражение) и WHILE NOT Слог, выражение) — суть одно и то
же. Их одновременное присутствие в языке связано с желанием добиться
большей наглядности при написании программ.
Место расположения в цикле инструкций WHILE и
UNTIL определяет, где будет проверяться условие выхода из него. В
случае, если цикл делает заведомо больше одного прохода, абсолютно
безразлично, где именно будет размещен критерий его продолжения или
окончания. Если же заранее неизвестно, какое значение —
«истина» или «ложь» — будет иметь Слог,
выражение) в момент первого
вхождения в цикл, то важно, где это будет проверяться — в заголовке цикла (предусловие) или же в его конце (постусловие).
Во втором случае, независимо от значения <лог. выражения), цикл
сделает, по крайней мере, один проход, что может оказаться
нежелательным. Поэтому рекомендуем использовать циклы DO...LOOP
преимущественно с предусловием — это застрахует от возможных
ошибок. Тем не менее, в ситуации, когда анализируемый в Слог,
выражении) параметр вводится впервые в теле цикла, необходимо
завершение цикла по условию, заданному оператором LOOP. Именно такой
вариант мы и предлагаем ь приводимом ниже примере. В нем осуществляется
поиск одного из корней квадратного уравнения методом Ньютона (метод
касательных):
_ ,А, "B=";В,"С=";С 20 INPUT "Начальное приближение Х="; X 30 LET Eps=0.001: REM Погрешность 40 CLOCK "00:00:00": REM Установка времени 50 DO
60 IF A*X+B=0 THEN POP: GO TO 140: REM Выход из цикла, если
деление на ноль 70 LET F=(A*X*X+B*X+C)/(A*X+B) 80 LET Absent=(VAL (Т1МЕ$ ()(7 ТО 8)) > 30) 90 EXIT IF Absent: REM Выход, если зацикливание 100 LET Х=Х—F
110 LOOP UNTIL ABS (F) < Eps
120 IF Absent THEN PRINT "Уравнение корней не имеет":
ELSE PRINT "Корень уравнения Х=";Х 130 GO ТО 10
140 PRINT "Попытка деления на ноль!" 150 GO ТО 10
Запросив вначале все необходимые параметры,
программа задает погрешность вычислений и обнуляет часы. После этого в
цикле (строки 50... 110) производятся вычисления, а в конце цикла
проверяется, достигнута ли заданная точность. Как только погрешность
расчета станет меньше установленной величины Eps (строка 110),
цикл завершит свою работу. Если уравнение не имеет решений, программа
зациклится. На этот случай предусмотрен контроль времени (строка 80), и
если программа работает больше полминуты (время заведомо достаточное,
чтобы найти корень), происходит принудительный выход по EXIT IF (строка 90).
1п inipmt "unvfirh 1/г.2вм»мио ▲="
При входе в цикл интерпретатор помещает в стек GO SUB адрес начала цикла. По достижении оператора LOOP это значение снимается со стека, и по нему вновь отыскивается соответствующий данному LOOP заголовок DO. При выходе из цикла по EXIT IF адрес возврата автоматически снимается со стека. Выход же из тела цикла посредством GO ТО или GO ТО ON необходимо предварить принудительной чисткой вершины стека оператором POP
(см. далее). В нашей программе есть пример и такого перехода (строка
60)- он предотвращает аварийный останов программы при попытке деления
на ноль.
Во избежание конфликтов со стеком запрещены
переходы извне в тело цикла, минуя его заголовок Если пренебречь этим
замечанием, выполнение программы будет прервано с выдачей сообщения LOOP without DO или No POP data. Подобно циклам FOR...NEXT циклы DO...LOOP допускают многократное вложение одного в другой.
Ранее неоднократно упоминался оператор POP. Рассмотрим его подробнее. В общем виде он записывается гак:
POP [<числовая переменная)]
Этот оператор снимает с вершины стека GO SUB значение номера строки возврата и присваивает его <числовой переменной), если она указана. Оператор POP позволяет корректно выйти из подпрограммы, процедуры PROC или цикла DO...LOOP
без дезориентации стека. Пользуясь этим оператором, можно возвратиться
из процедуры, вызванной, в свою очередь, из другой процедуры, сразу к
месту первого вызова.
Снятый со стека и присвоенный <числовой переменной) адрес возврата можно в дальнейшем использовать для перехода в операторах GO ТО и GO ТО ON, чтобы попасть в то место, откуда была вызвана процедура.