1.10 Введение в циклы
1. Понятие о цикле
Циклом называется повторяющееся действие или их набор. Как правило, повтор циклов производится более одного, а содержимое цикла даже может несколько изменяться, в зависимости от включенных в него условий рассмотренных в прошлой главе.
2. Цикл с условием на входе
Такой цикл сочетает в себе и условие, и сам цикл. Поскольку условие находится на входе, называется этот цикл — цикл с условием на входе (и это, как можно догадаться один из нескольких видов циклов). Начало такого цикла описывается ключевым словом WHILE
. Поскольку этот цикл — частью условие, после окончания описания условий указывается ключевое слово DO
. Оно похоже на THEN
, с той лишь разницей, что после THEN
идёт однократное исполнение инструкций, а после DO
— пока не будет выполнено условие выхода из цикла (для этого и было введено два разных слова, чтобы показать разницу, хотя возможно в будущих вариантах КП останется только THEN
). В ранних версиях языков для окончания цикла WHILE
использовалось ключевое слово LOOP
. Но в Компонентном Паскале это слово посчитали лишним, и теперь этот цикл заканчивается по END;
. Вот простой пример использования цикла WHILE:
WHILE цПерем1 < 50 DO
INC(цПерем1)
END;
В примере на входе для переменной цПерем1
проверяется условие (должна быть меньше 50). И пока это условие выполняется производится наращивание цПерем1
на единицу через ключевое слово INC
(инкремент)1). Также следует обратить внимание на то, что в цикле с условием на входе (как и в условиях) сложные выражения группируются в круглые скобки. Кроме того, шаг в таком цикле можно сделать любым. Например, 0.001. Или ещё меньше. В самом цикле переменные, от которых зависит выход из цикла — можно менять как угодно. Если из тела цикла в примере убрать инкремент переменой цПерем1
— такой цикл не закончится никогда. Иногда такие используются в программах. Но в 99,999% случаев в несистемном программировании такое зацикливание будет ошибкой программирования, и КП такие ошибки не анализирует.
3. Цикл с условием на выходе
Это второй вид цикла, в котором условия выхода могут быть сформированы каким угодно способом. Как следует из названия, проверка условия производится на выходе из цикла. И здесь есть одно важное следствие: даже если и будет произведён выход из цикла — проход по телу цикла будет гарантирован, по крайней мере — один раз. Пример такого цикла приведён ниже:
REPEAT
цПерем1 := цПерем1 - 10;
UNTIL цПерем1 < - 100;
Цикл с условием на выходе начинается с ключевого слова REPEAT
(«повторить»). Выход из цикла предваряется ключевым словом UNTIL
(«пока не…») — пока не будет выполнено условие выхода. Обратите внимание ещё раз — пока не будет выполнено условие из выхода! Т. е. если цикл с условием на входе требует истинности условия (ПОВТОРЯТЬ ПОКА ЕСТЬ
), то цикл с условием на выходе требует отрицания на выходе (ПОВТОРЯТЬ ПОКА НЕ ЕСТЬ
)! Если забыть про эту тонкость — ваш цикл не завершится никогда2).
4. Целочисленный цикл
Этот цикл выделен в отдельную структуру, так как имеет реализацию в командах процессора. Поскольку, для описания параметров целочисленного цикла используются целочисленные значения, то его очень удобно применять для обработки массивов с заранее известным размером. Почему нельзя использовать дробные числа? Да потому что не может быть элемента массива с порядковым номером «2,5». Либо «2», либо «3». Небольшой пример, показывающий использование целочисленного цикла:
Привет5.odc
MODULE КнигаПривет5;
(* Этот пример показывает как использовать
целочисленный цикл с массивами *)
IMPORT мЛог := Log, Math;
PROCEDURE Старт*;
CONST
_разм = 5;
VAR
лмцИзмер: ARRAY(_разм) OF INTEGER;
лцИтер: INTEGER;
BEGIN
FOR лцИтер := 0 TO(_разм - 1) DO
лмцИзмер[лцИтер] := лцИтер * 5
END;
FOR лцИтер := 0 TO(_разм - 1) DO
мЛог.String('лмцИзмер['); мЛог.Int(лцИтер);
мЛог.String(']='); мЛог.Int(лмцИзмер[лцИтер]);
мЛог.Ln
END;
мЛог.Ln
END Старт;
BEGIN
END КнигаПривет5.
(!)КнигаПривет5.Старт
В этом примере видно, что целочисленный цикл начинается с ключевого слова FOR
. В качестве начала целочисленного счётчика цикла используется переменная лцИтер
(локальное целое «итератор»), соответствующего типа. Эта переменная определена в секции VAR
процедуры Старт
(а это значит, что модуль КнигаПривет5
понятия не имеет о её существовании). Верхняя граница цикла устанавливается после ключевого слова TO
в виде скорректированной константы _разм
. Уменьшение этой константы на единицу меньше объяснимо тем, что индексация массива лмцИзмер
начинается с нуля, а не с 1
. Поэтому, последний номер элемента массива лмцИзмер
будет 4
, а не 5
, как это объявлено в секции VAR
с помощью константы _разм
. Если такую корректировку верхней границы целочисленного цикла не провести, то в ходе исполнения программы будет преодолена верхняя граница массива, и программа «вылетит с ошибкой». Заканчивается целочисленный цикл FOR
традиционно — ключевым словом END;
3). Также необходимо обратить внимание на то, что константа начинается с символа подчёркивания (необязательно, зато наглядно).
Второй цикл FOR
по своему объявлению полностью повторяет первый. Но содержание отличается. Так в первом цикле происходит заполнение массива целочисленных ячеек целочисленными значениями переменной лцИтер
с шагом в 5
. Во втором же цикле происходит вывод значений ячеек массива без их изменения. Если всё сделано правильно, то можно убедиться в том, что у каждой ячейки своё значение:
компилируется "КнигаПривет5" 140 0
лмцИзмер[ 0]= 0
лмцИзмер[ 1]= 5
лмцИзмер[ 2]= 10
лмцИзмер[ 3]= 15
лмцИзмер[ 4]= 20
Во втором цикле в строку скомбинирован вывод строк и целочисленных значений. При желании, можно выводить всё-что угодно и такой способ бывает удобен, чтобы оперативно посмотреть, какие значения принимают переменные в ходе выполнения программы. Остаётся один вопрос: переменная лцИтер
ни в первом цикле, ни во втором — не меняется. Каким образом происходит её изменение? Правильный ответ состоит в том, что центральный процессор сам наращивает значение лцИтер
. Об этом программисту беспокоиться не надо, и в целочисленном цикле: раньше или позже — но обязательно наступит завершение.
5. Целочисленный цикл с начальным произвольным шагом
Этот цикл является расширением предыдущего и почти от него не отличается. Пример представлен ниже:
Привет6.odc
MODULE КнигаПривет6;
(* Этот пример показывает как использовать
целочисленный цикл с массивами c произвольным
шагом *)
IMPORT мЛог := Log, Math;
PROCEDURE Старт*;
CONST
_разм = 5;
VAR
лмцИзм: ARRAY(_разм) OF INTEGER;
лцИтер: INTEGER;
BEGIN
FOR лцИтер := 0 TO(_разм - 1) BY 2 DO
лмцИзм[лцИтер] := лцИтер * 5
END;
FOR лцИтер := 0 TO(_разм - 1) DO
мЛог.String('лмцИзм['); мЛог.Int(лцИтер); мЛог.String(']=');
мЛог.Int(лмцИзм[лцИтер]); мЛог.Ln
END;
мЛог.Ln
END Старт;
BEGIN
END КнигаПривет6.
(!)КнигаПривет6.Старт
Чтобы задать шаг, необходимо после задания числа по окончанию цикла указать ключевое слово BY
. В случае примера выше — это 2
. С таким шагом всё нечётные ячейки массива будут пропущены, обратите внимание на вывод работы этой программы:
компилируется "КнигаПривет6" 140 0
лмцИзм[ 0]= 0
лмцИзм[ 1]= 1693148453
лмцИзм[ 2]= 10
лмцИзм[ 3]= 35749088
лмцИзм[ 4]= 20
Так и есть! В нечётных ячейках находится непонятно что! В ходе выполнения цикла значения им не присваивались. Отсюда следует правило: перед использованием числовых массивов их очень желательно обнулять.
6. Безусловный цикл
И этот цикл не зря называется так по экстремистски. Он действительно безусловный. Бывают такие программы, которые запускаются вместе с включением компьютера и завершают своё выполнение за одно мгновение до выключения4). В таких программах просто не нужен выход из цикла. А если всё-таки наступает условие, по которому надо бесконечный цикл прервать (и при этом нужно избежать тех инструкций, что идут далее) — происходит такой же безусловный выход, не терпящий возражений. Код ниже:
LOOP
IF цПерем1 > 100 THEN
EXIT
END;
IF цКоманда = _выход THEN
EXIT
END;
INC(цПерем1)
END;
Безусловный цикл объявляется ключевым словом LOOP
. Внутри него выполняются любые действия. Как только будет выполнено условие (цПерем1 > 1000
), выход из цикла будет выполнен. Во втором условии выполняется сравнение целочисленной переменной цКоманда
и если целое-команда будет иметь значение _выход
, произойдёт выход из безусловного цикла. Веток, предусматривающих выход из цикла может быть множество. Хотелось бы обратить внимание ещё раз, что получить завешивание
программы в таких конструкциях очень легко, и надо предусматривать возможность принудительного прерывания таких циклов (как во втором условии).
7. Заключение
Итак, циклы бывают трёх видов:
С условием на входе, условием на выходе (универсальный)
Целочисленный и целочисленный с произвольным шагом (для обработки массивов, может быть быстрее чем, с условием на входе/выходе)
Безусловный цикл (для длительных процессов).
И важное напоминание: неаккуратное обращение с циклами (кроме целочисленного) может обернуться «зависанием» программы.