
6.5. Операторы завершения цикла
При выполнении тела цикла могут возникнуть ситуации, когда по тем или иным причинам требуется нарушить естественное выполнение цикла. Возможны два типовых способа такого нарушения:
1) полный выход из цикла с прекращением циклических вычислений или
2) выход только из текущей итерации цикла - прекратить вычисления в ней, но остаться в цикле и перейти к следующей его итерации.
Теоретически для выполнения обоих действия можно использовать оператор безусловного перехода GOTO, но в соответствии с принципами структурного программирования такой переход не желателен и для выполнения данных действий в Паскале следует использовать специальные стандартные операторы завершения цикла - break и continue.
Процедура Break выполняет безусловный выход из цикла и передачу управления следующему за циклом оператору, т.е. фактически вместо break можно было бы поставить оператор безусловного перехода GOTO, осуществляющий переход к метке, стоящей перед следующим за циклом оператором.
Процедура сontinue обеспечивает прерывание выполнения текущей итерации цикла и безусловный переход к началу следующей. Данную процедуру можно эквивалентно заменить оператором GOTO, осуществляющим переход к метке, стоящей перед закрывающей скобкой тела цикла end. При срабатывании его также пропускаются все действия в текущей итерации, но цикл продолжает выполняться.
Необходимость применения операторов завершения цикла может быть обусловлена двумя основными причинами:
1) возникновением нештатной ситуации в ходе расчетов,
2) вмешательством пользователя в выполнение алгоритма.
Первая причина чаще всего обусловлена тем, что:
1) возникает опасность некорректного выполнения расчетных операций, например, деление на очень малое число, расчет логарифма либо извлечение квадратного корня из отрицательного числа,
2) заранее получен искомый ответ в решаемой задаче, в том числе - выяснено, что его не существует и др.
Для анализа нештатной ситуации необходимо хорошо знать свойства алгоритма, применяемого в программе, и использовать проверки текущих значений величин в ходе расчетов.
Пример 1 тела цикла, в котором вводятся целые величины a,b и в процессе расчетов необходимо выполнять деление на выражение (b-2a). Анализ величин программы и применение оператора break позволяют предотвратить аварийное завершение программы:
begin
ReadLn(a,b);{ввод точности расчета }
if b=2a then break;
c:=100/(b-2a);
end;
При выполнении условия b=2a оператор break передаст управление следующему за циклом оператору, в котором должен быть предусмотрен анализ условий выхода из цикла и дальнейшее выполнение программы. Если не применять эту проверку и обращение к break, то при первом же выполнении условия b=2a произойдет аварийный останов выполнения программы по ошибке с номером 200 (деление на нуль) с выдачей сообщения вида: Runtime error 200 at xxxx:xxxx. Если бы вместо break был использован оператор сontinue (if b=2a then сontinue;), то его применение обеспечило бы автоматический пропуск каждой итерации с делением на ноль и дальнейшее продолжение цикла.
Вмешательство пользователя для досрочного завершения цикла требуется в тех случаях, когда условия данного завершения не могут быть заданы программно и для обеспечения завершения необходимо использовать средства интерфейса. Рассмотрим их более подробно.
Для выдачи необходимых запросов и передачи данных из программы к пользователю используют операторы write и writeln, которые не вызывают остановок выполнения программы.
Обратная передача данных путем считывания программой информации, вводимой пользователем с клавиатуры, может быть организована в Паскале двумя существенно различающимися способами. Первый заключается в использовании операторов read и readln, которые предусматривают останов программы до тех пор, пока пользователь не введет все символы и не нажмет клавишу "Enter". Такой способ назовем передачей с остановом программы. Его желательно применять для передачи сообщений, содержащих много символов, без которых программа не может продолжать свою работу. При его использовании нет необходимости искусственно вводить временные задержки в исполнение программы, поскольку такую задержку задают сами операторы read и readln.
Второй возможный способ получения сообщения от пользователя заключается в использовании функций библиотеки CRT KeyPressed (возвращает True, если в буфер клавиатуры введен хотя бы один символ, False - если буфер пуст) и ReadKey (возвращает содержимое буфера клавиатуры, если он пуст, функция ожидает нажатия на любую клавишу). В отличие от read и readln ввод не сопровождается эхо-повтором вводимых символов на экране и не происходит остановки выполнения программы. Данный способ назовем фоновым. Его практически применяют для возможного ввода одного символа в программу. Несмотря на то, что его ввод не останавливает выполнение программы, для его практической реализации в тело цикла должны вводиться обращения к процедуре delay(ms), которые задают паузы в работе программы на ms миллисекунд. Такие остановы необходимы пользователю, для того, чтобы успеть отреагировать на ход расчета.
Пример 2 использования операторов break и continue для завершения цикла по команде пользователя, которая вводится с применением оператора readln.
Необходимо разработать код программы, который обеспечивает последовательную выдачу на экран всех целых чисел i от 1 до 10 в виде строковых сообщений вида " End of current iteration i = ". В каждой итерации цикла пользователю последовательно выдаются два запроса, требующих от него ответной реакции:
1) номер итерации и запрос на прекращение работы всего цикла (ответ "y" - завершение, выход из цикла; "others" – продолжение),
2) на пропуск вывода сообщения в текущей итерации ("y" - пропуск; "others" – вывод сообщения),
Решение. Для получения ответа пользователя по первому и второму запросу введена символьная переменная s. Код дан со всеми необходимыми комментариями:
var i: integer; s:char;
begin
For i:=1 to 10 do {заголовок арифметического цикла по параметру i от 1 до 10}
begin {открытие составного оператора тела арифметического цикла}
Write(’ i=’,i,’ Enter request to cycle stop(y-yes, others-no)’);
Readln(s); {получение ответа от пользователя на запрос 1}
if(s='y')then begin Writeln(’LOOP EXIT!’); break end;{ выход из цикла For }
Write(’Enter request to current iteration stop(y-yes, others-no)’);
Readln(s); {получение ответа от пользователя на запрос 2}
if(s='y')then begin Writeln(’ITERATION EXIT!’);continue end;{выход из итерации};
Writeln(’ End of current iteration i =’,i);
end; {закрытие тела цикла For}
end.
Пример 3. Решить задачу из примера 2 с использованием функций KeyPressed и ReadKey. Перед началом выполнения цикла выдать общий запрос, в котором для завершения цикла предлагается ввести с клавиатуры букву “y”, а для завершения текущей итерации - букву “d”. При других вариантах ввода или его отсутствии цикл должен продолжать работу. Для того, чтобы цикл выполнялся с задержкой на каждой итерации, использовать в ней процедуру ожидания delay(1000).
Решение. Код дан со всеми необходимыми комментариями:
uses Crt; {подключение библиотеки CRT для работы с экраном}
var s:char; i:integer;
begin {начало раздела операторов}
ClrScr;{ очистка экрана}
Writeln('Enter request for cycle stop(y-yes)or current iteration stop(d-yes)');
For i:=1 to 10 do {заголовок арифметического цикла по параметру i от 1 до 10}
begin {открытие составного оператора тела арифметического цикла}
Writeln (' iteration number i=',i);
delay (1000);{ ожидание 1000 мсек}
If KeyPressed then
begin s:= ReadKey;
if(s='y')then begin Writeln(’LOOP EXIT!’);break end;{ выход из цикла For }
if(s='d') then begin Writeln(’ITER.EXIT!’);continue end;{выход из итерации};
end;{закрытие составного оператора в IF}
Writeln(’ End of current iteration i =’,i);
end;{закрытие тела цикла For}
ReadKey; {ожидание нажатия клавиши для перехода от пользовательского экрана в основной}
end. {конец раздела операторов и всей программы}
Вопросы для проверки знаний.
1. Назовите два типовых способа нарушения естественного хода выполнения цикла.
2. Назовите две основных причины применения операторов завершения цикла .
3. Какие операторы в Паскале используются для нарушения естественного хода выполнения цикла и в чем их различие ?
4. Как эквивалентно можно заменить процедуру break оператором GOTO ?
5. Как эквивалентно можно заменить процедуру сontinue оператором GOTO ?
6. В чем различие ввода информации с клавиатуры при помощи операторов read и readln и функций KeyPressed и ReadKey ?
7. Почему при фоновом вводе информации с клавиатуры в тело цикла необходимо вводить обращение к процедуре delay ?
Практическое задание.
1. Модифицировать код программы, в примере 2 п.6.5 так, чтобы программа принимала в качестве правильного ответа не только прописную, но и заглавную латинскую букву "Y"?