===== 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''(инкремент)((В языке Си, (также в **Java**, **python** под влиянием **Си**) для этих целей используется оператор в форме ''цПерем1++'' или ''++цПерем1''. А ещё возможна вот такая конструкция: ''—(цПерем1++)++''. Попробуйте угадать, что это означает, и какой будет результат? (Далеко не все компиляторы такие примочки обрабатывают одинаково). А операции инкремента и декремента (''INC'' и ''DEC'') — это прямые команды процессора. Можете объяснить: зачем было вводить такие языковые конструкции? Подробнее можно посмотреть в [[https://ru.wikipedia.org/wiki/%D0%98%D0%BD%D0%BA%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D1%82|Википедии]].)). Также следует обратить внимание на то, что в цикле с условием на входе (как и в условиях) сложные выражения группируются в круглые скобки. Кроме того, шаг в таком цикле можно сделать //любым//. Например, 0.001. Или ещё меньше. В самом цикле переменные, от которых зависит выход из цикла — можно менять как угодно. Если из тела цикла в примере убрать инкремент переменой ''цПерем1'' — такой цикл не закончится никогда. Иногда такие используются в программах. Но в 99,999% случаев в несистемном программировании такое зацикливание будет //ошибкой программирования//, и **КП** такие ошибки не анализирует. ==== 3. Цикл с условием на выходе ==== Это второй вид цикла, в котором условия выхода могут быть сформированы //каким угодно// способом. Как следует из названия, проверка условия производится //на выходе// из цикла. И здесь есть одно важное следствие: даже если и будет произведён выход из цикла — проход по телу цикла //будет гарантирован//, по крайней мере — //один раз//. Пример такого цикла приведён ниже: REPEAT цПерем1 := цПерем1 - 10; UNTIL цПерем1 < - 100; Цикл с условием на выходе начинается с ключевого слова ''REPEAT'' ("повторить"). Выход из цикла предваряется ключевым словом ''UNTIL''("пока не...") — пока //не будет// выполнено условие выхода. Обратите внимание ещё раз — //пока не будет// выполнено условие из выхода! Т. е. если цикл с условием на входе требует //истинности// условия (''ПОВТОРЯТЬ ПОКА ЕСТЬ''), то цикл с условием на выходе требует //отрицания на выходе// (''ПОВТОРЯТЬ ПОКА НЕ ЕСТЬ'')! Если забыть про эту тонкость — ваш цикл //не завершится никогда//((Если всё-таки вы с циклом попали в просак, и ваша программа не останавливается всё-таки ещё есть возможность остановить программу: необходимо одновременно нажать комбинацию клавиш ****. Это стандартная комбинация для остановки (или прерывания) программ и работает во многих приложениях. В **BlackBox** в том числе.)). ==== 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;''((В других языках часто используются ключевое слово ''Next'' (**Visual Basic**), отступы (**python**), или закрывающие фигурные скобки (**Java**). Использование ''Next'' в **Visual Basic** наименее оправдано, так как в цикле ''FOR'' итак используется несколько ключевых слов, зачем вводить новую сущность? Более того, если следовать логике, то ''NEXT''(ДАЛЬШЕ) --- предполагает, что цикл далее продолжится, а на самом деле с точность наоборот -- это его окончание. Отступы в **python** заставляют писать аккуратный и правильный код, но представьте себе, если вложенных условий или циклов будет //10 уровней//? Какой величины будут отступы? (а отступ в **python** принят 4 пробела — 40 пробелов подряд!). Фигурные скобки в **Java**, состоящие только из одного символа //не цепляют// взгляд и легко теряются на фоне множества таких же скобок. В **Компонентном Паскале**, как ориентированном на надёжность, принято не гнаться за краткостью (в ущерб пониманию), и не вводить излишних сущностей, если существующих хватает для описания структурной единицы.)). Также необходимо обратить внимание на то, что константа начинается с символа подчёркивания (//необязательно//, зато наглядно). Второй цикл ''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. Безусловный цикл ==== И этот цикл не зря называется так //по экстремистски//. Он действительно //безусловный//. Бывают такие программы, которые запускаются вместе с включением компьютера и завершают своё выполнение за одно мгновение до выключения((«Завершить работу за мгновение до выключения» это //вовсе не метафора//. Например, в песне из фильма «Семнадцать мгновений весны» есть строка: «они летят как пули у виска». Т. е. длительность мгновения определяется скоростью полёта пули. Современная пуля летит с такой скоростью, что для съёмки её полёта требуется «всего» 500 тыс. кадров в секунду. Для видеокамеры это //очень много//, но если переводить в частоту центрального процессора — то это будет всего 500 кГц. Чтобы понять на сколько это //мало// для компьютера, достаточно вспомнить, что современные центральные процессоры способны работать на частоте 5000000 кГц. Если учесть, что в **ЦП** может быть до 8 ядер, а конвеер **ЦП** может одновременно выполнять до 16 команд, то становится понятно, что за то время, что длится мгновение компьютер может успеть выполнить 960 тыс. операций. Согласитесь, это //очень много//. Кроме того, когда **Нео** в первой части фильма «Матрица» уклоняется от пуль, становится понятно, что либо **Нео** — читер, который воспользовался кодами, которые и замедлили работу Матрицы, либо все остальные в Зеоне были безграмотны и не умели использовать прерывания программы ).)). В таких программах просто //не нужен// выход из цикла. А если всё-таки наступает условие, по которому надо бесконечный цикл прервать (и при этом нужно избежать тех инструкций, что идут далее) — происходит такой же безусловный выход, не терпящий возражений. Код ниже: LOOP IF цПерем1 > 100 THEN EXIT END; IF цКоманда = _выход THEN EXIT END; INC(цПерем1) END; Безусловный цикл объявляется ключевым словом ''LOOP''. Внутри него выполняются любые действия. Как только будет выполнено условие (''цПерем1 > 1000''), выход из цикла будет выполнен. Во втором условии выполняется сравнение целочисленной переменной ''цКоманда'' и если целое-команда будет иметь значение ''_выход'', произойдёт выход из безусловного цикла. Веток, предусматривающих выход из цикла может быть множество. Хотелось бы обратить внимание ещё раз, что получить ''завешивание'' программы в таких конструкциях //очень легко//, и надо предусматривать возможность принудительного прерывания таких циклов (как во втором условии). ==== 7. Заключение ==== Итак, циклы бывают трёх видов: - С условием на входе, условием на выходе (универсальный) - Целочисленный и целочисленный с произвольным шагом (для обработки массивов, может быть быстрее чем, с условием на входе/выходе) - Безусловный цикл (для длительных процессов). И важное напоминание: неаккуратное обращение с циклами (кроме целочисленного) может обернуться "зависанием" программы.