Перейти к материалам ОГЭ/ЕГЭ
РУВИКИ для ОГЭ/ЕГЭ
Переходите на портал РУВИКИ, где собраны материалы для подготовки к ОГЭ и ЕГЭ
Перейти
Перейти к материалам ОГЭ/ЕГЭ
РУВИКИ для ОГЭ/ЕГЭ
Переходите на портал РУВИКИ, где собраны материалы для подготовки к ОГЭ и ЕГЭ
У этого термина существуют и другие значения, см. Цикл.
Цикл — разновидность управляющей конструкции в высокоуровневых языках программирования, предназначенная для организации многократного исполнения набора инструкций. Также циклом может называться любая многократно исполняемая последовательность инструкций, организованная любым способом (например, с помощью условного перехода).
Последовательность инструкций, предназначенная для многократного исполнения, называется телом цикла. Единичное выполнение тела цикла называется итерацией. Выражение, определяющее, будет в очередной раз выполняться итерация или цикл завершится, называется условием выхода или условием окончания цикла (либо условием продолжения в зависимости от того, как интерпретируется его истинность — как признак необходимости завершения или продолжения цикла). Переменная, хранящая текущий номер итерации, называется счётчиком итераций цикла или просто счётчиком цикла. Цикл не обязательно содержит счётчик, счётчик не обязан быть один — условие выхода из цикла может зависеть от нескольких изменяемых в цикле переменных, а может определяться внешними условиями (например, наступлением определённого времени), в последнем случае счётчик может вообще не понадобиться.
Исполнение любого цикла включает первоначальную инициализацию переменных цикла, проверку условия выхода, исполнение тела цикла и обновление переменной цикла на каждой итерации. Кроме того, большинство языков программирования предоставляет средства для досрочного управления циклом, например, операторы завершения цикла, то есть выхода из цикла независимо от истинности условия выхода (в языке Си — break
) и операторы пропуска итерации (в языке Си — continue
).
Безусловные циклы[править | править код]
Иногда в программах используются циклы, выход из которых не предусмотрен логикой программы. Такие циклы называются безусловными, или бесконечными. Специальных синтаксических средств для создания бесконечных циклов, ввиду их нетипичности, языки программирования не предусматривают, поэтому такие циклы создаются с помощью конструкций, предназначенных для создания обычных (или условных) циклов. Для обеспечения бесконечного повторения проверка условия в таком цикле либо отсутствует (если позволяет синтаксис, как, например, в цикле LOOP ... END LOOP
языка Ада), либо заменяется константным значением (while true do ...
в Паскале). В языке С используется цикл for(;;)
с незаполненными секциями или цикл while (1)
.
Цикл с предусловием[править | править код]
Цикл с предусловием — цикл, который выполняется, пока истинно некоторое условие, указанное перед его началом. Это условие проверяется до выполнения тела цикла, поэтому тело может быть не выполнено ни разу (если условие с самого начала ложно). В большинстве процедурных языков программирования реализуется оператором while, отсюда его второе название — while-цикл.
На языке Pascal цикл с предусловием имеет следующий вид:
while <условие> do begin <тело цикла> end;
На языке Си:
while (<условие>) { <тело цикла> }
Цикл с постусловием[править | править код]
Цикл с постусловием — цикл, в котором условие проверяется после выполнения тела цикла. Отсюда следует, что тело всегда выполняется хотя бы один раз. В языке Паскаль этот цикл реализует оператор repeat..until
; в Си — do…while
.
На языке Pascal цикл с постусловием имеет следующий вид::
repeat <тело цикла> until <условие выхода>
На языке Си:
do { <тело цикла> } while (<условие продолжения цикла>)
В трактовке условия цикла с постусловием в разных языках есть различия. В Паскале и языках, произошедших от него, условие такого цикла трактуется как условие выхода (цикл завершается, когда условие истинно, в русской терминологии такие циклы называют ещё «цикл до»), а в Си и его потомках — как условие продолжения (цикл завершается, когда условие ложно, такие циклы иногда называют «цикл пока»).
Цикл с выходом из середины[править | править код]
Цикл с выходом из середины — наиболее общая форма условного цикла. Синтаксически такой цикл оформляется с помощью трёх конструкций: начала цикла, конца цикла и команды выхода из цикла. Конструкция начала маркирует точку программы, в которой начинается тело цикла, конструкция конца — точку, где тело заканчивается. Внутри тела должна присутствовать команда выхода из цикла, при выполнении которой цикл заканчивается и управление передаётся на оператор, следующий за конструкцией конца цикла. Естественно, чтобы цикл выполнился более одного раза, команда выхода должна вызываться не безусловно, а только при выполнении условия выхода из цикла.
Принципиальным отличием такого вида цикла от рассмотренных выше является то, что часть тела цикла, расположенная после начала цикла и до команды выхода, выполняется всегда (даже если условие выхода из цикла истинно при первой итерации), а часть тела цикла, находящаяся после команды выхода, не выполняется при последней итерации.
Легко видеть, что с помощью цикла с выходом из середины можно легко смоделировать и цикл с предусловием (разместив команду выхода в начале тела цикла), и цикл с постусловием (разместив команду выхода в конце тела цикла).
Часть языков программирования содержит специальные конструкции для организации цикла с выходом из середины. Так, в языке Ада для этого используется конструкция LOOP ... END LOOP
и команда выхода EXIT
или EXIT WHEN
:
LOOP ... Часть тела цикла EXIT WHEN <условие выхода>; ... Часть тела цикла IF <условие выхода> THEN EXIT; END; ... Часть тела цикла END LOOP:
Здесь внутри цикла может быть любое количество команд выхода обоих типов. Сами команды выхода принципиально не различаются, обычно EXIT WHEN
применяют, когда проверяется только условие выхода, а просто EXIT
— когда выход из цикла производится в одном из вариантов сложного условного оператора.
В тех языках, где подобных конструкций не предусмотрено, цикл с выходом из середины может быть смоделирован с помощью любого условного цикла и оператора досрочного выхода из цикла (такого, как break
в Си, exit в Турбо Паскале и т. п.), либо оператора безусловного перехода goto
.
Цикл со счётчиком (или цикл для)[править | править код]
Цикл со счётчиком — цикл, в котором некоторая переменная изменяет своё значение от заданного начального значения до конечного значения с некоторым шагом, и для каждого значения этой переменной тело цикла выполняется один раз. В большинстве процедурных языков программирования реализуется оператором for
, в котором указывается счётчик (так называемая «переменная цикла»), требуемое количество проходов (или граничное значение счётчика) и, возможно, шаг, с которым изменяется счётчик. Например, в языке Оберон-2 такой цикл имеет вид:
FOR v := b TO e BY s DO ... тело цикла END
здесь v — счётчик, b — начальное значение счётчика, e — граничное значение счётчика, s — шаг).
Неоднозначен вопрос о значении переменной по завершении цикла, в котором эта переменная использовалась как счётчик. Например, если в программе на языке Паскаль встретится конструкция вида:
i := 100; for i := 0 to 9 do begin ... тело цикла end; k := i;
возникает вопрос: какое значение будет в итоге присвоено переменной k: 9, 10, 100, может быть, какое-то другое? А если цикл завершится досрочно? Ответы зависят от того, увеличивается ли значение счётчика после последней итерации и не изменяет ли транслятор это значение дополнительно. Ещё один вопрос: что будет, если внутри цикла счётчику будет явно присвоено новое значение? Различные языки программирования решают данные вопросы по-разному. В некоторых поведение счётчика чётко регламентировано. В других, например, в том же Паскале, стандарт языка не определяет ни конечного значения счётчика, ни последствий его явного изменения в цикле, но не рекомендует изменять счётчик явно и использовать его по завершении цикла без повторной инициализации. Программа на Паскале, игнорирующая эту рекомендацию, может давать разные результаты при выполнении на разных системах и использовании разных трансляторов.
Радикально решён вопрос в языках Ада и Kotlin: счётчик считается описанным в заголовке цикла, и вне его просто не существует. Даже если имя счётчика в программе уже используется, внутри цикла в качестве счётчика используется отдельная переменная. Счётчику запрещено явно присваивать какие бы то ни было значения, он может меняться только внутренним механизмом оператора цикла.
В результате конструкция на Аде:
i := 100; for i in (0..9) loop ... тело цикла end loop; k := i;
И на Котлине:
val i = 100; for (i in 0..9){ ... тело цикла } val k = i;
внешне аналогичная вышеприведённому циклу на Паскале, трактуется однозначно: переменной k будет присвоено значение 100, поскольку переменная i, используемая вне данного цикла, не имеет никакого отношения к счётчику i, который создаётся и изменяется внутри цикла. Подобное обособление счётчика удобно и безопасно: не требуется отдельное описание для него и минимальна вероятность случайных ошибок, связанных со случайным разрушением внешних по отношению к циклу переменных. Если программисту требуется включить в готовый код цикл со счётчиком, то он может не проверять, существует ли переменная с именем, которое он выбрал в качестве счётчика, не добавлять описание нового счётчика в заголовок соответствующей процедуры, не пытаться использовать один из имеющихся, но в данный момент «свободных» счётчиков. Он просто пишет цикл с переменной-счётчиком, имя которой ему удобно, и может быть уверен, что никакой коллизии имён не произойдёт.
Цикл со счётчиком всегда можно записать как условный цикл, перед началом которого счётчику присваивается начальное значение, а условием выхода является достижение счётчиком конечного значения; к телу цикла при этом добавляется оператор изменения счётчика на заданный шаг. Однако специальные операторы цикла со счётчиком могут эффективнее транслироваться, так как формализованный вид такого цикла позволяет использовать специальные процессорные команды организации циклов.
Никлаус Вирт одно время называл цикл со счётчиком «маргинальным», утверждая, что такая конструкция является излишней и должна быть исключена из синтаксиса языков программирования как несистемная. В соответствии с этим представлением в языке программирования Оберон цикла со счётчиком не было. Однако в языке Оберон-2, созданном Виртом и Мёссенбёком в развитие Оберона, цикл со счётчиком FOR
появился снова в интересах практического удобства использования[1].
В некоторых языках, например, Си и других, произошедших от него, цикл for
, несмотря на синтаксическую форму цикла со счётчиком, в действительности является циклом с предусловием. То есть в Си конструкция цикла:
for (i = 0; i < 10; ++i) { ... тело цикла }
фактически представляет собой другую форму записи конструкции[2]:
i = 0; while (i < 10) { ... тело цикла ++i; }
То есть в конструкции for
сначала пишется произвольное предложение инициализации цикла, затем — условие продолжения и, наконец, выполняемая после каждого тела цикла некоторая операция (это не обязательно должно быть изменение счётчика; это может быть правка указателя или какая-нибудь совершенно посторонняя операция). Для языков такого вида вышеописанная проблема решается очень просто: переменная-счётчик ведёт себя совершенно предсказуемо и по завершении цикла сохраняет своё последнее значение.
Совместный цикл[править | править код]
Ещё одним вариантом цикла является цикл, задающий выполнение некоторой операции для объектов из заданного множества, без явного указания порядка перечисления этих объектов. Такие циклы называются совместными (а также циклами по коллекции, циклами просмотра) и представляют собой формальную запись инструкции вида: «Выполнить операцию X для всех элементов, входящих во множество M». Совместный цикл, теоретически, никак не определяет, в каком порядке операция будет применяться к элементам множества, хотя конкретные языки программирования, разумеется, могут задавать конкретный порядок перебора элементов. Произвольность даёт возможность оптимизации исполнения цикла за счёт организации доступа не в заданном программистом, а в наиболее выгодном порядке. При наличии возможности параллельного выполнения нескольких операций возможно даже распараллеливание выполнения совместного цикла, когда одна и та же операция одновременно выполняется на разных вычислительных модулях для разных объектов, при том, что логически программа остаётся последовательной.
Совместные циклы имеются в некоторых языках программирования (C#, Eiffel, Java, JavaScript, Perl, Python, PHP, LISP, Tcl и др.) — они позволяют выполнять цикл по всем элементам заданной коллекции объектов. В определении такого цикла требуется указать только коллекцию объектов и переменную, которой в теле цикла будет присвоено значение обрабатываемого в данный момент объекта (или ссылка на него). В различных языках программирования синтаксис оператора различен:
C++:
for (type &item : set) //поддерживается, начиная со стандарта C++11 { //использование item }
C#:
foreach (type item in set) { //использование item }
Delphi:
for item in [1..100] do begin //Использование item (Работоспособность кода проверялась в Delphi 2010) end;
Perl (строгий порядок «от первого до последнего»):
foreach (@set) { #использование $_ } # или for (@set) { #использование $_ } # или foreach $item (@set) { #использование $item }
Eiffel:
across set as cursor loop -- использование cursor.item end
Java:
for (type item : set) { //использование item }
JavaScript:
for (txtProperty in objObject) { /* использование: objObject [txtProperty] */ }
PHP:
foreach ($arr as $item) { /* использование $item*/ } //или foreach ($arr as $key=>$value) { /* использование значений индекса $key и его значения $value*/ } //или foreach ($arr as &$item) { /*использование $item по ссылке*/ }
Visual Basic.NET:
For Each item As type In set 'использование item Next item
Windows PowerShell:
foreach ($item in $set) { # операции с $item }
или
$set | ForEach-Object { # операции с $_ }
Python
for item in iterator_instance: # использование item
Ruby
iterator_instance.each do |item| # использование item end
Многие языки программирования, имеющие в своём синтаксисе циклические конструкции, имеют также специфические команды, позволяющие нарушить порядок работы этих конструкций: команду досрочного выхода из цикла и команду пропуска итерации.
Досрочный выход из цикла[править | править код]
Команда досрочного выхода применяется, когда необходимо прервать выполнение цикла, в котором условие выхода ещё не достигнуто. Такое бывает, например, когда при выполнении тела цикла обнаруживается ошибка, после которой дальнейшая работа цикла не имеет смысла.
Команда досрочного выхода обычно называется EXIT
или break
, а её действие аналогично действию команды безусловного перехода (goto
) на команду, непосредственно следующую за циклом, внутри которого эта команда находится. Так, в языке Си два нижеприведённых цикла работают совершенно одинаково:
// Применение оператора break while(<условие>) { ... операторы if (<ошибка>) break; ... операторы } ... продолжение программы // Аналогичный фрагмент без break while(<условие>) { ... операторы if (<ошибка>) goto break_label; ... операторы } break_label: ... продолжение программы
В обоих случаях, если в теле цикла выполнится условие <ошибка>, будет произведён переход на операторы, обозначенные как «продолжение программы». Таким образом, оператор досрочного выхода из цикла, по сути, просто маскирует безусловный переход, однако использование break предпочтительнее, чем goto, поскольку поведение break чётко задано языком, потенциально менее опасно (нет, например, вероятности ошибиться с положением или названием метки). Кроме того, явный досрочный выход из цикла не нарушает принципов структурного программирования.
Обычный оператор досрочного выхода прерывает работу того цикла, в котором он непосредственно находится. В ряде языков программирования функциональность этого оператора расширена, он позволяет выходить из нескольких вложенных циклов (см. ниже). В таких случаях цикл, из которого требуется выйти, помечается меткой, а в операторе досрочного выхода указывается эта метка.
Пропуск итерации[править | править код]
Данный оператор применяется, когда в текущей итерации цикла необходимо пропустить все команды до конца тела цикла. При этом сам цикл прерываться не должен, условия продолжения или выхода должны вычисляться обычным образом.
В языке Си и его языках-потомках в качестве команды пропуска итерации используется оператор continue
в конструкции цикла. Действие этого оператора аналогично безусловному переходу на строку внутри тела цикла, следующую за последней его командой. Например, код на Си, находящий сумму элементов массива и сумму всех положительных элементов массива, может иметь следующий вид:
int arr[ARRSIZE]; ... // Суммирование отдельно всех и только положительных // элементов массива arr с применением continue. int sum_all = 0; int sum_pos = 0; for (int i = 0 ; i < ARRSIZE; ++i) { sum_all += arr[i]; if (arr[i] <= 0) continue; sum_pos += arr[i]; } // Аналогичный код c goto int sum_all = 0; int sum_pos = 0; for (int i = 0 ; i < ARRSIZE; ++i) { sum_all += arr[i]; if (arr[i] <= 0) goto cont_label; sum_pos += arr[i]; cont_label: }
Из второго фрагмента ясно видно, как работает continue
: он просто передаёт управление за последнюю команду тела цикла, пропуская выполнение команды суммирования, если очередной элемент массива не удовлетворяет условию. Таким образом, в sum_pos накапливается сумма лишь положительных элементов массива.
Необходимость[править | править код]
С точки зрения структурного программирования команды досрочного выхода из цикла и пропуска итерации являются избыточными, поскольку их действие может быть легко смоделировано чисто структурными средствами. Более того, по мнению ряда теоретиков программирования (в частности, Эдсгера Дейкстры), сам факт использования в программе неструктурных средств, будь то классический безусловный переход или любая из его специализированных форм, таких как break или continue, является свидетельством недостаточно проработанного алгоритма решения задачи.
Однако на практике код программы часто является записью уже имеющегося, ранее сформулированного алгоритма, перерабатывать который нецелесообразно по чисто техническим причинам. Попытка заменить в таком коде команду досрочного выхода на структурные конструкции часто оказывается неэффективной или громоздкой. Например, вышеприведённый фрагмент кода с командой break
может быть записан так:
// Досрочный выход из цикла без break bool flag = false; // флаг досрочного завершения while(<условие> && !flag) { ... операторы if (<ошибка>) { flag = true; } else { ... операторы } } ... продолжение программы
Легко убедиться, что фрагмент будет работать аналогично предшествующим, разница лишь в том, что в месте проверки на ошибку вместо непосредственного выхода из цикла устанавливается флаг досрочного выхода, который проверяется позже в штатном условии продолжения цикла. Однако для отказа от команды досрочного выхода пришлось добавить в программу описание флага и вторую ветвь условного оператора, к тому же произошло «размытие» логики программы (решение о досрочном выходе принимается в одном месте, а выполняется в другом). В результате программа не стала ни проще, ни короче, ни понятнее.
Несколько иначе обстоит дело с командой пропуска итерации. Она, как правило, очень легко и естественно заменяется на условный оператор. Например, приведённый выше фрагмент суммирования массива можно записать так:
int arr[ARRSIZE]; ... // Суммирование отдельно всех и только положительных // элементов массива arr с заменой continue int sum_all = 0; int sum_pos = 0; for (int i = 0 ; i < ARRSIZE; ++i) { sum_all += arr[i]; if (arr[i] > 0) // Условие заменено на противоположное! { sum_pos += arr[i]; } }
Как видим, достаточно было заменить проверяемое условие на противоположное и поместить заключительную часть тела цикла в условный оператор. Можно заметить, что программа стала короче (за счёт удаления команды пропуска итерации) и одновременно логичнее (из кода непосредственно видно, что суммируются положительные элементы).
Кроме того, использование команды пропуска итерации в цикле с условием (while-цикле) может также спровоцировать неочевидную ошибку: если тело цикла, как это часто бывает, завершается командами изменения переменной (переменных) цикла, то команда пропуска итерации пропустит и эти команды тоже, в результате чего (в зависимости от условия, по которому происходит пропуск) может произойти зацикливание или не соответствующий алгоритму повтор итерации. Так, если заменить в вышеприведённом примере цикл for на while, получится следующее:
int arr[ARRSIZE]; ... int sum_all = 0; int sum_pos = 0; int i = 0; while (i < ARRSIZE) // Цикл внешне аналогичен предыдущему for ... { sum_all += arr[i]; if (arr[i] <= 0) continue; sum_pos += arr[i]; ++i; // ... но эта команда будет пропущена при выполнении continue // и программа зациклится }
Несмотря на свою ограниченную полезность и возможность замены на другие языковые конструкции, команды пропуска итерации и, особенно, досрочного выхода из цикла в отдельных случаях оказываются крайне полезны, именно поэтому они сохраняются в современных языках программирования.
Существует возможность организовать цикл внутри тела другого цикла. Такой цикл будет называться вложенным циклом. Вложенный цикл по отношению к циклу, в тело которого он вложен, будет именоваться внутренним циклом, и наоборот, цикл, в теле которого существует вложенный цикл, будет именоваться внешним по отношению к вложенному. Внутри вложенного цикла, в свою очередь, может быть вложен ещё один цикл, образуя следующий уровень вложенности и так далее. Количество уровней вложенности, как правило, не ограничивается.
Полное число исполнений тела внутреннего цикла не превышает произведения числа итераций внутреннего и всех внешних циклов. Например, взяв три вложенных друг в друга цикла, каждый по 10 итераций, получим 10 исполнений тела для внешнего цикла, 100 для цикла второго уровня и 1000 в самом внутреннем цикле.
Одна из проблем, связанных с вложенными циклами — организация досрочного выхода из них. Во многих языках программирования есть оператор досрочного завершения цикла (break
в Си, exit
в Турбо Паскале, last
в Perl и т. п.), но он, как правило, обеспечивает выход только из цикла того уровня, откуда вызван. Вызов его из вложенного цикла приведёт к завершению только этого внутреннего цикла, внешний же цикл продолжит выполняться. Проблема может показаться надуманной, но она действительно иногда возникает при программировании сложной обработки данных, когда алгоритм требует немедленного прерывания в определённых условиях, наличие которых можно проверить только в глубоко вложенном цикле.
Решений проблемы выхода из вложенных циклов несколько.
- Простейший — использовать оператор безусловного перехода goto для выхода в точку программы, непосредственно следующую за вложенным циклом. Этот вариант критикуется сторонниками структурного программирования, как и все конструкции, требующие использования goto. Некоторые языки программирования, например, Модула-2, просто не имеют оператора безусловного перехода, и в них подобная конструкция невозможна.
- Альтернатива — использовать штатные средства завершения циклов, в случае необходимости устанавливая специальные флаги, требующие немедленного завершения обработки. Недостаток — усложнение кода, снижение производительности.
- Размещение вложенного цикла в процедуре. Идея состоит в том, чтобы всё действие, которое может потребоваться прервать досрочно, оформить в виде отдельной процедуры, и для досрочного завершения использовать оператор выхода из процедуры (если такой есть в языке программирования). В языке Си, например, можно построить функцию с вложенным циклом, а выход из неё организовать с помощью оператора return. Недостаток — выделение фрагмента кода в процедуру не всегда логически обосновано, и не все языки имеют штатные средства досрочного завершения процедур.
- Воспользоваться механизмом генерации и обработки исключений (исключительных ситуаций), который имеется сейчас в большинстве языков высокого уровня. В этом случае в нештатной ситуации код во вложенном цикле возбуждает исключение, а блок обработки исключений, в который помещён весь вложенный цикл, перехватывает и обрабатывает его. Недостаток — реализация механизма обработки исключений в большинстве случаев такова, что скорость работы программы уменьшается. Правда, в современных условиях это не особенно важно: практически потеря производительности столь мала, что имеет значение лишь для очень немногих приложений.
- Наконец, существуют специальные языковые средства для выхода из вложенных циклов. Так, в языке Ада программист может пометить цикл (верхний уровень вложенного цикла) меткой, и в команде досрочного завершения цикла указать эту метку. Выход произойдёт не из текущего цикла, а из всех вложенных циклов до помеченного, включительно[3]. Язык PHP предоставляет возможность указать число прерываемых циклов после команды
break
— такbreak 2
прервёт сам цикл и вышестоящий над ним, аbreak 1
эквивалентно простой записи командыbreak
[4].
Цикл Дейкстры[править | править код]
В теории программирования известна ещё одна, принципиально отличающаяся от «классических», форма циклической конструкции, получившая название «цикл Дейкстры», по имени Эдсгера Дейкстры, впервые её описавшего. В классическом дейкстровском описании такой цикл выглядит следующим образом:
do P1 → S1, … Pn → Sn od
Здесь do
— маркер начала конструкции цикла, od
— маркер завершения конструкции цикла, Pi — i-е охраняющее условие (логическое выражение, которое может иметь значение «истинно» или «ложно»), Si — i-я охраняемая команда. Цикл состоит из одной или нескольких ветвей (охраняемых выражений), каждая из которых представляет собой пару из охраняющего условия (или, коротко, «охраны») и охраняемой команды (понятно, что в реальности команда может быть сложной).
При выполнении цикла Дейкстры в каждой итерации происходит вычисление охраняющих условий. Если хотя бы одно из них истинно, выполняется соответствующая охраняемая команда, после чего начинается новая итерация (если истинны несколько охраняющих условий, выполняется только одна охраняемая команда). Если все охраняющие условия ложны, цикл завершается. Нетрудно заметить, что цикл Дейкстры с одним охраняющим условием и одной охраняемой командой представляет собой, по сути, обычный цикл с предусловием (цикл «пока»).
Хотя цикл Дейкстры был изобретён ещё в 1970-х годах, специальных конструкций для его создания в языках программирования не содержится. Единственным исключением стал недавно созданный Оберон-07 — первый реальный язык программирования, явно поддерживающий цикл с несколькими охраняемыми ветвями. Впрочем, цикл Дейкстры может быть без больших затруднений смоделирован с помощью традиционных конструкций структурных языков программирования. Вот пример его реализации одним из возможных способов на языке Ада:
loop if P1 then S1; ... elsif Pn then Sn; else exit; end if; end loop;
Здесь P1—Pn — охраняющие условия, а S1—Sn — соответствующие охраняемые команды.
Цикл Дейкстры удобен при реализации некоторых специфических повторяющихся вычислений, которые неудобно описывать с помощью более традиционных циклических конструкций. Например, этим циклом естественно представляется конечный автомат — каждая ветвь соответствует одному состоянию автомата, охраняемые условия строятся так, чтобы в текущей итерации выбиралась ветвь, соответствующая текущему состоянию автомата, а код охраняемой команды обеспечивает выполнение вычислений в текущем состоянии и переход в следующее (то есть такое изменение переменных, после которого на следующей итерации будет истинным охраняющее условие нужной ветви).
Цикл «паук»[править | править код]
Легко видеть, что цикл Дейкстры не содержит явного условия продолжения или выхода, что не всеми теоретиками программирования рассматривается как благо. Поэтому была предложена усложнённая конструкция цикла Дейкстры, получившая название «цикл-‘паук’». В той же нотации она выглядит следующим образом:
do P1→S1, … Pn→Sn out Q1→T1, … Qn→Tn else E od
Здесь после маркера out
добавлены ветви завершения, состоящие из условий выхода Qi и команд завершения Ti. Кроме того, добавлена ветвь альтернативного завершения else
с командой E.
Цикл-‘паук’ выполняется так:
- Вычисляются охраняющие условия. Если существует истинное охраняющее условие, выполняется соответствующая охраняемая команда.
- Вычисляются условия выхода. Если существует истинное условие выхода, выполняется соответствующая команда завершения, после чего выполнение цикла заканчивается. Если все условия выхода ложны, начинается следующая итерация, но только в том случае, если в текущей итерации было истинным хотя бы одно из охраняющих условий.
- Если в данной итерации оказались ложными и все охраняющие условия, и все условия выхода, выполняется команда альтернативного завершения E, после чего выполнение цикла прерывается.
Структура цикла-‘паука’ позволяет предельно строго описать условия выполнения цикла. Согласно теоретическим положениям, ветвь альтернативного завершения не должна использоваться в качестве одного из вариантов корректного прекращения работы цикла (все такие варианты должны быть оформлены в виде соответствующих ветвей завершения с явным условием), она служит только для того, чтобы отследить ситуацию, когда по каким-то причинам цикл начал выполняться нештатно. То есть команда альтернативного завершения может лишь анализировать причины ошибки и представлять результаты анализа.
Хотя явной поддержки на уровне синтаксиса для этого цикла не существует ни в одном языке программирования, цикл-‘паук’, как и цикл Дейкстры, может быть смоделирован с помощью традиционных структурных конструкций.
- эквивалентными преобразованиями исходного кода
- Разбиение цикла на блоки
- Размотка цикла
- Размыкание цикла
- компилятором
- Расщепление цикла
- Расщепление тела цикла
- Слияние циклов
- Инварианты цикла (используются при проектировании и верификации циклов)
- Рекурсия
- Итератор (программирование)
- ↑ Оберон — воплощение мечты Никлауса Вирта
- ↑ Строго говоря, тождество не полное, так как по-другому будет работать оператор continue.
- ↑ Loops/Nested at Rosetta Code (англ.)
- ↑ PHP Manual, break (англ.)
- Подробное описание циклических процессов в Паскале
Сейчас мы с вами посмотрим на управляющие инструкции, Т.е инструкции которые управляют ходом выполнения вашей программы. Что я имею ввиду?
Вы можете представить программу как последовательность некоторых инструкций(первая схема на изображении), но зачастую вы хотите чтобы какие-то инструкции выполнялись при выполнении каких-то условий(вторая схема на изображении) или чтобы инструкции повторялись пока какое-то условие выполняется(третья схема на изображении).
Ветвление (условная инструкция) — это конструкция языка программирования, обеспечивающая выполнение определённой команды или набора команд только при условии истинности некоторого логического выражения, либо выполнение одной из нескольких команд (наборов команд) в зависимости от значения некоторого выражения.
Цикл — это разновидность управляющей конструкции, предназначенная для организации многократного исполнения набора инструкций.
Конструкция if/else
Это конструкция ветвления, прочитывается она достаточно естественно.
Если (какое-то условие выполняется) {
выполнить следующее
} иначе {
выполнить что-то другое
}
А теперь уже более корректный пример:
if () {
do_something();
} else {
console.error(«Все плохо =(«);
do_something_else();
}
Вы можете опускать ветку else
… ну уж если вы ничего не хотите делать если условие не выполняется.
if (true) {
do_something();
}
Для того чтобы делать множественные проверки, вы можете добавить дополнительное условие в ветке else
.
var name = «Bob»;
if (name === «John») {
console.log(«Гость любит чай»);
} else if (name === «Mia») {
console.log(«Гость любит кофе»);
} else {
console.log(«Неизвестный гость»);
}
Конструкция switch/case
Конструкция switch
заменяет собой сразу несколько if
.
Она представляет собой более наглядный способ сравнить выражение сразу с несколькими вариантами.
var command = «camel»;
switch (
command
) {
case «up»:
alert(«Перевести строку в верхний регистр»);
break;
case «down»:
alert(«Перевести строку в нижний регистр»);
break;
case «camel»:
alert(«Перевести строку в вид: ‘CaMeLcAsE'»);
break;
case «turn»:
alert(«Поменять регист для каждой буквы на противоположный»);
break;
default:
alert(«Неизвестная команда! Вы ошиблись!»);
}
В примере выше мы инициализировали переменную содержащую строку «up». Конструкция switch case
позволяет сделать множественные сравнения переданного ей значения с значениями, которые мы указали в case
. Если значения совпали, то код указанный в этом кейсе будет выполнен.
В результате выполнения кода выше в диалоговом окне мы увидим сообщение «Перевести строку в верхний регистр»
Обратите внимание на инструкцию break
. Она обязательна… если мы пропустим ее в одном из «кейсов», то исполнения пойдет по следующим case
до ближайшего break
или до конца инструкций в «кейсах».
Циклы
Циклы – простой способ сделать какое-то действие несколько раз.
Итерация (лат. iteratio — повторяю) – повторение какого-либо действия.
info
Слово Итерация так же обозначет каждое выполнение тела цикла. Т.е «перейти на слудующую итерацию» обозначает запустить следующее выполнение тела цикла.. перейти на новый круг.
В Javascript у нас есть 3 основных типа циклов:
while
do while
for
Как уже сказано, циклы нужны для того чтобы повторять какие-то инструкции пока выполняется условие. И в действительности, мы могли бы жить только с циклом ну например while
, но для удобства, нам предоставляют несколько их видов.
Зачем нам нужны циклы?
Ну тут все просто.. если вам надо отправить 5 сообщений в телеграм, то вам надо повторить некоторые инструкции 5 раз. Для этого нам пригодятся циклы.
Обратите внимание
Помните я рассказывал вам, как выполняются инструкции на CPU, и что из себя представляют ассемблерные инструкции? Так вот, циклы которые нам предоставляют языки программирования являются — синтаксическим сахаром.
Ваш CPU ничего не знает про циклы, а разворачиваются они в последовательность каких-то инструкций. Для того чтобы инструкции выполнялись повторно, происходит выполнение инструкции jmp
.
Для наглядности вот вам интересная ссылочка. Советую ознакомиться.
Интересный факт
Существуют языки в которых циклы как таковые отсутствуют, это популярно в функциональных языках программирования, а повтор каких-то действий осуществляется через рекурсию.
Итерирование по последовательностям
Существует еще 2 инструкции for of
и for in
для интерирования по последовательностям. Более детально про них мы посмотрим когда начнем знакомиться с массивами.
while
Выглядит как:
Более наглядный пример:
var i = 1;
var sum = 0;
while (i <= 100) {
sum += i;
i++;
}
console.log(sum);
do while
То же самое, что и while
за исключением того, что сначала выполняется код в теле цикла, а только потом делается проверка стоит ли исполнять тело еще раз
do {
i += 1;
console.log(i);
} while (i < 5);
Цикл do while
гарантирует хотя бы одно срабатывание кода.
for
Не сильно отличается от обычного while
. В основном используют именно этот цикл, потому, что он позволяет в более элегантной форме создавать счетчик и инкрементировать его.
for ([начало]; [условие]; [шаг]) {
}
*Квадратные скобочки в описании синтаксиса инструкции говорит о том, что это выражение(параметр) не является обязательным.
Похожие квадратные скобочки мы будем видеть, когда будем читать описание функций*
Перепишем пример цикла который был приведен в описании инструкции while
использую инструкцию for
:
var sum = 0;
for (var i = 0; i <= 0; i++) {
sum += i;
}
console.log(sum);
Экономия только в 2 строчки кода, но зато мы аккуратно инициализировали счетчик и не забудем инкрементировать(тело цикла бывает очень объемное) его после каждой итерации. Цикл for
более предпочтителен чем while
Управление циклом break
/countinue
Мы посмотрели как пользоваться циклами, но сейчас у нас нет возможности управления ими.
break
Давайте представим что нам надо прервать выполнение цикла в определенной ситуации. Ну например у вас есть последовательность чисел:
let NUM_SEQ = [5, 17, 40, 22, 17];
Мы хотим итерироваться по этому списку, но как только мы нашли число больше чем 20
мы хотим прервать итерирование для этого мы можем воспользоваться инструкцией break
.
let i = 0;
let first_greater_then = null;
while (i < NUM_SEQ.length) {
if (NUM_SEQ[i] > 20) {
first_greater_then = NUM_SEQ[i];
break;
}
}
break
— инструкция которая приводит к прекращению выполнения текущего цикла вне зависимости от того, выполняется ли тест-условие или нет.
Вопрос на засыпку
У вас есть идеи как реализовать подобное поведение без использования инструкции break
?
continue
Мы вроде как знаем каким образом можно прекратить выполнение цикла, но как прекратить выполнение текущей итерации и перейти сразу к проверке и следующему кругу выполнения? Для этого у нас есть инструкция continue
.
Давайте какой-нибудь выдуманный пример. Допустим мы проходимся по числам от 0
и до 10
, и суммируем только те, что нечетны двум.
let acc = 0;
for (let i = 0; i < 10; i++) {
if (i % 2 == 0) continue;
acc += i;
}
break
— инструкция которая приводит к прекращению выполнения текущей итерации и сразу же выполняется следующая проверка условия.
Сторонние источники
Ответ на вопрос в сканворде (кроссворде) «Разновидность управляющей конструкции в высокоуровневых языках программирования, предназначенная для организации многократного исполнения набора инструкций», 4 буквы (первая — ц, последняя — л):
цикл
(ЦИКЛ) 👍 0 👎 0
Другие определения (вопросы) к слову «цикл» (143)
- Группа наук
- Закольцованная последовательность операций
- Виток круговорота
- Ряд произведений искусства на общую тему
- Круг работ на конвейере
- Repeat until
- Лекции на одну тему
- Законченная совокупность явлений
- «Полный набор» лекций
- Роман бразильского писателя Жозе Линс ду Регу «… сахарного тростника»
- Нулевой … (строит.)
- Совокупность явлений, процессов, составляющая кругооборот в течение определённого промежутка времени
- Серия стихов на одну тему
- Производственный, но не процесс
- Серия событий, повторяющихся по кругу
- Процесс, идущий по кругу
- «Круг» по-гречески
- Замкнутый круг явлений
- Ряд лекций на одну тему
- «Природный …»
- Курс лечения
- Серия книг на одну тему
- Четыре такта ДВС
- «Сериал» из лекций
- Нулевой … на стройке
- Нулевой этап стройки
- Круговерть по-научному
- Определенный промежуток времени периодического процесса
- Период, повторение, круговорот
- Круг лекций
- Звено в цепи круговорота
- Комплекс процедур
- Круговорот явлений
- Хоровод взаимосвязанных процессов
- Законченный ряд каких-нибудь произведений, чего-нибудь излагаемого, исполняемого
- Несколько литературных произведений на общую тему
- Законченный ряд каких-либо произведений
- Курс наук
- Виток по кругу
- Исторический круг наук
- Стихи по теме
- Совокупность взаимосвязанных явлений, процессов, образующих законченный круг развития в течение промежутка времени
- Единица измерения угла, а также фазы колебаний
- Роман Стивена Кинга «… оборотня»
- Концептуальный ряд песен
- Несколько ТВ-передач, объединенных общей темой
- Хоровод взаимосвяз. процессов
- Ряд лекций
- Повторяющаяся последовательность
- Нулевой … стройки
- Ряд произведений
- Стихи со сквозной темой
- Круг дел на производстве
- «Круговорот» лекций
- Кругооборот домны
- Законченный ряд каких-нибудь произведений
- Круг повторений
- Ряд песен о космосе
- Совокупность из нескольких лекций, посвященных сходной тематике
- Явления и процессы, повторяющиеся через некоторое время
- Ряд песен одной тематики
- Годовой … вращения Земли
- «Сериал» лекций
- Полный оборот
- Концерты из одной серии
- Двигатель, такты
- Совокупность процессов, образ. законченный круг развития
- Кругооборот времён года
- Совокупность процессов, образующих законченный круг развития
- Ряд работ на конвеере
- греч. период времени, срок, оборот дней, годов, круг
- Ход по кругу
- https://sinonim.org/sc
- Тематический круг лекций
- Процесс по кругу
- Обойма стихов
- Курс лекций с общей темой
- Единица фазы колебаний
- Законченный ряд каких-н. произведений, чего-н. излагаемого, исполняемого
- Кругооборот событий
- Группа наук, дисциплин
- Ряд работ на конвейере
- 5 лекций на одну тему
- Ряд каких-либо произведений, лекций, концертов и т. п., объединяемых общностью тематики
- Чередование по кругу
- Прослушать … лекций
- Вся «обойма» процедур
- Кругооборот явлений
- Повторяемый период
- Набор лекций
- Смена времен года
- Ряд тематических песен
- Последовательность явлений
- Последовательность процессов
- Виток бесконечного процесса
- Греческий «круг»
- «Сериал» из передач
- Серия стихов
- Ряд стихотворений на одну тему
- Литературный «гарнитур»
- Ряд повторяющихся действий
- Тот или иной круг наук
- Чередование сезонов
- Движение по кругу
- Курс лекций в вузе
- Нулевой на строительном объекте
- «Замкнутый …» на вредном производстве, чтобы не страдала окружающая среда
- Группа наук, учебных дисциплин
- Клеточный …
- Тематический ряд лекций
- Круг жизни
- Кругооборот
- Группа наук в ВУЗе
- Теории гомологий — цепь, граница которой равна 0
- Законченный ряд
- Менструальный …
- Нулевой … (начало стройки)
- Законченный ряд тематически близких произведений
- Полный круг процесса
- Ряд лекций с общей тематикой
- Законченный ряд лекций
- Стихотворные произведения общей тематики
- Ряд стихов или лекций
- Круговорот
- греч. период времени, срок, оборот дней, годов, круг. Круг луны, 19-летний; круг солнца, лет. Циклоида, кривая черта, которую описывает любая точка круга, катящегося по прямой линии
- Полный курс лекций
- Зима, весна, лето, осень
- Один оборот по кругу
- Серия лекций
- Что такое индикт
- Нулевой …
- Периодический процесс
- Замкнутый … производства
- Совокупность процессов
- Повторение
- Круговерть по науке
- Лекции из курса в курс
- «У любой идеи есть жизненный …»
- Весна, лето, осень, зима
- Законченный круг работ, явлений
- Ряд лекций по теме
- Законченный ряд произведений
- «Нулевой …» строительства
- Круг наук
- повторяющаяся во времени последовательность событий, процессов или явлений ◆ Я узнал, что кольца деревьев указывают на какую-то пульсацию климата, на какие-то циклы жизнедеятельности планеты, не совпадающие ни с периодом солнечной активности, ни с чем иным. Ю. О. Домбровский, «Хранитель древностей»
- совокупность взаимосвязанных дисциплин, работ или произведений, образующих законченную стройную систему ◆ В поэтическом цикле автор обычно знает или чувствует, какое именно стихотворение задаёт тональность всему циклу. Вадим Крейд, «Георгий Иванов в Йере», 2003 г. ◆ Она позволяет себе петь всё: джаз и спиричуэлз, вокальные циклы, оперные арии, — и всё это с необыкновенным достоинством настоящей королевы. С. З. Спивакова, «Не всё», 2002 г.
- матем. (математический термин), в теории графов — замкнутая последовательность смежных рёбер графа
- хим. (последовательность) конфигурация атомов в сложной молекуле, при которой линии связи между атомами образуют замкнутую ломаную линию ◆ Но для сложных терпеноидов, содержащих в своей структуре полизамещенные циклы, и в особенности — для среднециклических производных, стабильные конформации молекулы далеко не всегда в точности соответствуют заторможенным конформациям по всем подвижным фрагментам, и внутрициклические торсионные углы для стабильных конформаций могут принимать практически любые значения в интервале f=0-180°, в зависимости от структуры молекулы. А. Ткачёв, «Химия возобновляемого растительного сырья: исследование терпеноидов растений Сибири и Дальнего Востока»
- геол. (геологическое) последовательность смены режима накопления осадков, которая повторяется в ходе геологического развития территории
- то же, что оборот
- матем. (математический термин), в теории гомологий цепь, граница которой равна 0
- матем. (цепь) тип перестановки
- информ. (информатика и компьютерные технологии) разновидность управляющей конструкции в высокоуровневых языках программирования, предназначенная для организации многократного исполнения набора инструкций; любая многократно исполняемая последовательность инструкций, организованная любым способом
Значение слова
ЦИКЛ,
-а, мужской род
1.
Совокупность каких-либо явлений, процессов, работ, составляющих законченный круг действия, развития чего-либо
Одногодичный цикл развития листьев. Цикл двигателя внутреннего сгорания. Цикл переменного тока. Рабочий цикл станка. ◆ Григорий Петрович наметанным глазом определил, что цех действительно очень продуманно и четко организован, введено много новшеств, благодаря которым вдвое сокращен цикл сборки станка. Кетлинская, Дни нашей жизни.
2.
Совокупность связанных между собой явлений, последовательный ряд чего-либо
Цикл лекций по искусству. Цикл концертов.
||
Группа наук, дисциплин, объединенных по какому-либо общему принципу.
Биологический цикл. Исторический цикл.
||
Ряд художественных, музыкальных произведений одного жанра, объединенных общей темой, действующими лицами и т. п.
Киевский цикл былин. Цикл лирических стихотворений. ◆ [Пушкин] собрал целый цикл песен о Стеньке Разине. М. Горький, А. С. Пушкин.
[От греч. κύκλος — круг, окружность]
Цикл (латинское cyclus от древне-греческого κύκλος — окружность) может означать:
- Цикл, или оборот — единица измерения угла, а также фазы колебаний.
- Экономические циклы — колебания экономической активности.
- Исторический циклизм
- В математике
- Цикл (теория графов)
- В теории гомологий цикл — это цепь, граница которой равна 0.
- Цикл — тип перестановки
- Цикл (программирование)
- Термодинамические циклы
- Жизненный цикл
- Менструальный цикл
- Демографический цикл
- Литературный цикл — ряд литературных произведений на общую или близкую тематику, созданный одним автором или одной группой авторов.
- Музыкальный цикл — ряд отдельных музыкальных произведений или музыкальных альбомов, концертов и т. п., посвящённых какой-либо теме или образу.
- Машинный цикл — промежуток между двумя обращениями процессора к внешнему по отношению к нему устройству (например, памяти). Состоит из одного или нескольких тактов.
- Учебный цикл — учебно-методическое подразделение кафедры по своей дисциплине.
- Солнечная цикличность — периодические изменения в солнечной активности.
- Лунный цикл
- Осадочный цикл — в геологии, последовательность смены режима накопления осадков, которая повторяется в ходе геологического развития территории.
Что искали другие
- Часть мотка пряжи
- Упряжка с бубенцами
- Наследник резвого скакуна
- Военный трофей диких североамериканских индейцев
- Ссора, раздор; Отсутствие единства во взглядах
Случайное
- Тонкотелая морская рыба
- Выразительность изложения
- Загородный домик из дерева или плетёного камыша
- Капитан коммерческого судна
- Аптечный «коктейль»
Кроссворды — одна из популярных головоломок для всех возрастов. Их решение имеет немало плюсов:
- Они могут помочь расширить ваш словарный запас, знакомя вас с новыми словами и фразами.
- Помогают улучшить память, заставляя вас запоминать и вспоминать информацию.
- Они заставляют вас думать, это может помочь улучшить вашу гибкость ума.
- Некоторые люди считают, что работа над кроссвордами — это расслабляющее и приятное занятие, которое помогает снять стресс.
- Кроссворды требуют сосредоточенности и внимания к деталям, что может помочь улучшить вашу способность к концентрации.
- Занятия, которые бросают вызов мозгу, такие как разгадывание сканвордов, могут способствовать укреплению здоровья мозга и снизить риск снижения когнитивных способностей.
- Поиск занял 0.009 сек. Вспомните, как часто вы ищете ответы? Добавьте sinonim.org в закладки, чтобы быстро искать их, а также синонимы, антонимы, ассоциации и предложения.
Написать нам
Случайные страницы на сайте: синоним к отпадение, синоним к место хранения
Источник статьи
Автор24
— учеба по твоим правилам
Скачать статью
Определение 1
Простой и вложенные циклы, цикл с развилкой — это разновидности управляющих конструкций в высокоуровневых языках программирования, которые предназначены для организации многократного выполнения определенного набора инструкций.
Введение
Операторы циклов используются тогда, когда неизвестно заранее, какое количество раз будет необходимо повторять одни и те же вычислительные процедуры (или процедуры разных типов), а окончание этих процедур может определяться некоторым заранее определенным параметром или условием.
Основная структура «ЦИКЛ» предназначена для записи алгоритмов, в которых определенная часть алгоритма, а именно, тело цикла, должна исполняться некоторое количество раз. Вложенными циклами являются циклы, которые организованы в теле других циклов. Вложенный цикл в тело другого цикла, именуется еще внутренним циклом.
Простой и вложенные циклы. Цикл с развилкой
Количество повторений цикла может быть определено различными способами, в зависимости от которых различают следующие виды циклов:
- Цикл, имеющий предварительное условие, или цикл «пока».
- Цикл, имеющий некоторый параметр.
- Цикл, имеющий постусловие, или цикл «до».
При выполнении цикла с предварительным условием в начале осуществляется проверка условие его исполнения. Пока это условие исполняется, будет выполнятся повторения тела цикла. Отсюда возникло и иное его название, а именно цикл «пока». Если же условие не исполняется при первой проверке, то тело цикла не будет выполнено совсем. После завершения цикла управление должно передаваться следующей структуре. Для того чтобы не было зацикливания, то есть, бесконечного исполнения цикла, в теле цикла в обязательном порядке должны меняться параметры, которые записаны в условии. На рисунке ниже приведен алгоритм такого цикла.
Рисунок 1. Алгоритм. Автор24 — интернет-биржа студенческих работ
«Простой и вложенные циклы. Цикл с развилкой» 👇
Цикл с параметром удобно применять в тех случаях, когда заранее известно число повторений цикла. Здесь введено такое понятие, как счетчика цикла, который по умолчанию должен считаться равным или единице, или минус единице. В отдельных случаях изменение счетчика цикла (приращение) может быть указано в явной форме. Для того, чтобы организовать цикл следует задать верхнюю и нижнюю границы изменения счетчика цикла. Согласно значению верхней и нижней границы, должен определяться и шаг цикла, а именно, единица или минус единица, то есть значение счетчика цикла. На рисунке ниже приведен алгоритм цикла с параметром.
Рисунок 2. Алгоритм цикла с параметром. Автор24 — интернет-биржа студенческих работ
При реализации цикла с постусловием, или цикла «до», условие завершения цикла должно проверяться после тела цикла. В данном случае тело цикла всегда исполняется хотя бы один раз. Цикл будет исполняться до выполнения условия, отсюда и иное его наименование, а именно, цикл «до». А пока условие не исполнено, будет осуществляться повторение тела цикла, то есть, исполнение условия может считаться условием окончания цикла. В таком случае, как и в цикле «пока», следует предусмотреть в теле цикла изменение параметров условия цикла. На рисунке ниже приведен алгоритм цикла с постусловием.
Рисунок 3. Алгоритм цикла с постусловием. Автор24 — интернет-биржа студенческих работ
Для любого вида цикла предусматривается возможность досрочного выхода, то есть, прекращение исполнения цикла.
Кроме рассмотренных выше трех типов цикла существуют еще и так называемые итерационные циклы. Особенностью итерационного цикла считается тот факт, что количество повторений операторов тела цикла заранее не определено. Для его реализации может быть использован цикл типа «пока». Выход из итерационного цикла может быть осуществлен, если выполняется заданное условие. С каждым шагом вычислений осуществляется последовательное приближение и проверка условия достижения требуемого результата. Классическим примером может быть вычисление суммы ряда с необходимой точностью.
Алгоритм, в составе которого имеется итерационный цикл, именуется итерационным алгоритмом. Итерационные алгоритмы применяются при выполнении итерационных численных методов. В итерационном алгоритме следует обеспечить обязательное условие выхода из цикла, то есть, гарантировать сходимость итерационного процесса. В противном случае может произойти зацикливание алгоритма, то есть, не будет исполняться главное свойство алгоритма, а именно, результативность.
Возможны варианты, когда внутри тела цикла нужно выполнить повторение некоторой последовательности операторов, то есть, сформировать внутренний цикл. Такая структура называется циклом в цикле, или вложенным циклом. Глубина вложения циклов, то есть число вложенных друг в друга циклов, может быть разной.
При применении этой структуры для экономии времени исполнения следует вынести из внутреннего цикла во внешний все операции, результаты которых не имеют зависимости от параметра внутреннего цикла.
Рассмотрим конкретный пример. Необходимо определить сумму элементов для конкретной матрицы, то есть, таблицы, состоящей из пяти строк и трех столбцов, А (5,3).
Приведем алгоритм решения задачи на псевдокоде.
Рисунок 4. Алгоритм решения задачи на псевдокоде. Автор24 — интернет-биржа студенческих работ
Главная часть блок-схемы по определению суммы элементов матрицы имеет следующий вид.
Рисунок 5. Главная часть блок-схемы. Автор24 — интернет-биржа студенческих работ
Тут порядок исполнения вложенных циклов следующий:
- Счетчик внутреннего цикла меняется быстрее, то есть, для i = 1(внешний цикл), j пробегает значения 1, 2, 3 (внутренний цикл).
- Затем i = 2, j и снова пробегает значения 1, 2, 3 и так далее.
Разветвляющимся циклом или развилкой принято называть алгоритм, в котором предусмотрено прохождение разных вариантов работы в зависимости от исполнения или не исполнения определенного условия. В блок-схеме данное условие необходимо записывать в ромб-блок, который обозначает сравнение. На рисунке ниже представлена общая структурная организация ветвления.
Рисунок 6. Общая структурная организация ветвления. Автор24 — интернет-биржа студенческих работ
Очень часто та или иная операция должна быть исполнена в зависимости от значения логического выражения, которое выступает в качестве условия. В таких случаях используется развилка. Рассмотрим конкретный пример, в котором необходимо вычислить значение функции.
Рисунок 7. Функция. Автор24 — интернет-биржа студенческих работ
На рисунке ниже приведена блок-схема решения этой задачи.
Рисунок 8. Блок-схема. Автор24 — интернет-биржа студенческих работ
При тестировании алгоритмов с развилкой необходимо подбирать такие начальные данные, чтобы можно было проверить все ветви.
У этого термина существуют и другие значения, см. Цикл.
Цикл — разновидность управляющей конструкции в высокоуровневых языках программирования, предназначенная для организации многократного исполнения набора инструкций. Также циклом может называться любая многократно исполняемая последовательность инструкций, организованная любым способом (например, с помощью условного перехода).
Определения
Последовательность инструкций, предназначенная для многократного исполнения, называется телом цикла. Единичное выполнение тела цикла называется итерацией. Выражение, определяющее, будет в очередной раз выполняться итерация или цикл завершится, называется условием выхода или условием окончания цикла (либо условием продолжения в зависимости от того, как интерпретируется его истинность — как признак необходимости завершения или продолжения цикла). Переменная, хранящая текущий номер итерации, называется счётчиком итераций цикла или просто счётчиком цикла. Цикл не обязательно содержит счётчик, счётчик не обязан быть один — условие выхода из цикла может зависеть от нескольких изменяемых в цикле переменных, а может определяться внешними условиями (например, наступлением определённого времени), в последнем случае счётчик может вообще не понадобиться.
Исполнение любого цикла включает первоначальную инициализацию переменных цикла, проверку условия выхода, исполнение тела цикла и обновление переменной цикла на каждой итерации. Кроме того, большинство языков программирования предоставляет средства для досрочного управления циклом, например, операторы завершения цикла, то есть выхода из цикла независимо от истинности условия выхода (в языке Си — break
) и операторы пропуска итерации (в языке Си — continue
).
Виды циклов
Безусловные циклы
Иногда в программах используются циклы, выход из которых не предусмотрен логикой программы. Такие циклы называются безусловными, или бесконечными. Специальных синтаксических средств для создания бесконечных циклов, ввиду их нетипичности, языки программирования не предусматривают, поэтому такие циклы создаются с помощью конструкций, предназначенных для создания обычных (или условных) циклов. Для обеспечения бесконечного повторения проверка условия в таком цикле либо отсутствует (если позволяет синтаксис, как, например, в цикле LOOP ... END LOOP
языка Ада), либо заменяется константным значением (while true do ...
в Паскале). В языке С используется цикл for(;;)
с незаполненными секциями или цикл while (1)
.
Цикл с предусловием
Цикл с предусловием — цикл, который выполняется, пока истинно некоторое условие, указанное перед его началом. Это условие проверяется до выполнения тела цикла, поэтому тело может быть не выполнено ни разу (если условие с самого начала ложно). В большинстве процедурных языков программирования реализуется оператором while, отсюда его второе название — while-цикл.
На языке Pascal цикл с предусловием имеет следующий вид:
while <условие> do begin <тело цикла> end;
На языке Си:
while (<условие>) { <тело цикла> }
Цикл с постусловием
Цикл с постусловием — цикл, в котором условие проверяется после выполнения тела цикла. Отсюда следует, что тело всегда выполняется хотя бы один раз. В языке Паскаль этот цикл реализует оператор repeat..until
; в Си — do…while
.
На языке Pascal цикл с постусловием имеет следующий вид::
repeat <тело цикла> until <условие выхода>
На языке Си:
do { <тело цикла> } while (<условие продолжения цикла>)
В трактовке условия цикла с постусловием в разных языках есть различия. В Паскале и языках, произошедших от него, условие такого цикла трактуется как условие выхода (цикл завершается, когда условие истинно, в русской терминологии такие циклы называют ещё «цикл до»), а в Си и его потомках — как условие продолжения (цикл завершается, когда условие ложно, такие циклы иногда называют «цикл пока»).
Цикл с выходом из середины
Цикл с выходом из середины — наиболее общая форма условного цикла. Синтаксически такой цикл оформляется с помощью трёх конструкций: начала цикла, конца цикла и команды выхода из цикла. Конструкция начала маркирует точку программы, в которой начинается тело цикла, конструкция конца — точку, где тело заканчивается. Внутри тела должна присутствовать команда выхода из цикла, при выполнении которой цикл заканчивается и управление передаётся на оператор, следующий за конструкцией конца цикла. Естественно, чтобы цикл выполнился более одного раза, команда выхода должна вызываться не безусловно, а только при выполнении условия выхода из цикла.
Принципиальным отличием такого вида цикла от рассмотренных выше является то, что часть тела цикла, расположенная после начала цикла и до команды выхода, выполняется всегда (даже если условие выхода из цикла истинно при первой итерации), а часть тела цикла, находящаяся после команды выхода, не выполняется при последней итерации.
Легко видеть, что с помощью цикла с выходом из середины можно легко смоделировать и цикл с предусловием (разместив команду выхода в начале тела цикла), и цикл с постусловием (разместив команду выхода в конце тела цикла).
Часть языков программирования содержит специальные конструкции для организации цикла с выходом из середины. Так, в языке Ада для этого используется конструкция LOOP ... END LOOP
и команда выхода EXIT
или EXIT WHEN
:
LOOP ... Часть тела цикла EXIT WHEN <условие выхода>; ... Часть тела цикла IF <условие выхода> THEN EXIT; END; ... Часть тела цикла END LOOP:
Здесь внутри цикла может быть любое количество команд выхода обоих типов. Сами команды выхода принципиально не различаются, обычно EXIT WHEN
применяют, когда проверяется только условие выхода, а просто EXIT
— когда выход из цикла производится в одном из вариантов сложного условного оператора.
В тех языках, где подобных конструкций не предусмотрено, цикл с выходом из середины может быть смоделирован с помощью любого условного цикла и оператора досрочного выхода из цикла (такого, как break
в Си, exit в Турбо Паскале и т. п.), либо оператора безусловного перехода goto
.
Цикл со счётчиком (или цикл для)
Цикл со счётчиком — цикл, в котором некоторая переменная изменяет своё значение от заданного начального значения до конечного значения с некоторым шагом, и для каждого значения этой переменной тело цикла выполняется один раз. В большинстве процедурных языков программирования реализуется оператором for
, в котором указывается счётчик (так называемая «переменная цикла»), требуемое количество проходов (или граничное значение счётчика) и, возможно, шаг, с которым изменяется счётчик. Например, в языке Оберон-2 такой цикл имеет вид:
FOR v := b TO e BY s DO ... тело цикла END
здесь v — счётчик, b — начальное значение счётчика, e — граничное значение счётчика, s — шаг).
Неоднозначен вопрос о значении переменной по завершении цикла, в котором эта переменная использовалась как счётчик. Например, если в программе на языке Паскаль встретится конструкция вида:
i := 100; for i := 0 to 9 do begin ... тело цикла end; k := i;
возникает вопрос: какое значение будет в итоге присвоено переменной k: 9, 10, 100, может быть, какое-то другое? А если цикл завершится досрочно? Ответы зависят от того, увеличивается ли значение счётчика после последней итерации и не изменяет ли транслятор это значение дополнительно. Ещё один вопрос: что будет, если внутри цикла счётчику будет явно присвоено новое значение? Различные языки программирования решают данные вопросы по-разному. В некоторых поведение счётчика чётко регламентировано. В других, например, в том же Паскале, стандарт языка не определяет ни конечного значения счётчика, ни последствий его явного изменения в цикле, но не рекомендует изменять счётчик явно и использовать его по завершении цикла без повторной инициализации. Программа на Паскале, игнорирующая эту рекомендацию, может давать разные результаты при выполнении на разных системах и использовании разных трансляторов.
Радикально решён вопрос в языках Ада и Kotlin: счётчик считается описанным в заголовке цикла, и вне его просто не существует. Даже если имя счётчика в программе уже используется, внутри цикла в качестве счётчика используется отдельная переменная. Счётчику запрещено явно присваивать какие бы то ни было значения, он может меняться только внутренним механизмом оператора цикла.
В результате конструкция на Аде:
i := 100; for i in (0..9) loop ... тело цикла end loop; k := i;
И на Котлине:
val i = 100; for (i in 0..9){ ... тело цикла } val k = i;
внешне аналогичная вышеприведённому циклу на Паскале, трактуется однозначно: переменной k будет присвоено значение 100, поскольку переменная i, используемая вне данного цикла, не имеет никакого отношения к счётчику i, который создаётся и изменяется внутри цикла. Подобное обособление счётчика удобно и безопасно: не требуется отдельное описание для него и минимальна вероятность случайных ошибок, связанных со случайным разрушением внешних по отношению к циклу переменных. Если программисту требуется включить в готовый код цикл со счётчиком, то он может не проверять, существует ли переменная с именем, которое он выбрал в качестве счётчика, не добавлять описание нового счётчика в заголовок соответствующей процедуры, не пытаться использовать один из имеющихся, но в данный момент «свободных» счётчиков. Он просто пишет цикл с переменной-счётчиком, имя которой ему удобно, и может быть уверен, что никакой коллизии имён не произойдёт.
Цикл со счётчиком всегда можно записать как условный цикл, перед началом которого счётчику присваивается начальное значение, а условием выхода является достижение счётчиком конечного значения; к телу цикла при этом добавляется оператор изменения счётчика на заданный шаг. Однако специальные операторы цикла со счётчиком могут эффективнее транслироваться, так как формализованный вид такого цикла позволяет использовать специальные процессорные команды организации циклов.
Никлаус Вирт одно время называл цикл со счётчиком «маргинальным», утверждая, что такая конструкция является излишней и должна быть исключена из синтаксиса языков программирования как несистемная. В соответствии с этим представлением в языке программирования Оберон цикла со счётчиком не было. Однако в языке Оберон-2, созданном Виртом и Мёссенбёком в развитие Оберона, цикл со счётчиком FOR
появился снова в интересах практического удобства использования[1].
В некоторых языках, например, Си и других, произошедших от него, цикл for
, несмотря на синтаксическую форму цикла со счётчиком, в действительности является циклом с предусловием. То есть в Си конструкция цикла:
for (i = 0; i < 10; ++i) { ... тело цикла }
фактически представляет собой другую форму записи конструкции[2]:
i = 0; while (i < 10) { ... тело цикла ++i; }
То есть в конструкции for
сначала пишется произвольное предложение инициализации цикла, затем — условие продолжения и, наконец, выполняемая после каждого тела цикла некоторая операция (это не обязательно должно быть изменение счётчика; это может быть правка указателя или какая-нибудь совершенно посторонняя операция). Для языков такого вида вышеописанная проблема решается очень просто: переменная-счётчик ведёт себя совершенно предсказуемо и по завершении цикла сохраняет своё последнее значение.
Совместный цикл
Ещё одним вариантом цикла является цикл, задающий выполнение некоторой операции для объектов из заданного множества, без явного указания порядка перечисления этих объектов. Такие циклы называются совместными (а также циклами по коллекции, циклами просмотра) и представляют собой формальную запись инструкции вида: «Выполнить операцию X для всех элементов, входящих во множество M». Совместный цикл, теоретически, никак не определяет, в каком порядке операция будет применяться к элементам множества, хотя конкретные языки программирования, разумеется, могут задавать конкретный порядок перебора элементов. Произвольность даёт возможность оптимизации исполнения цикла за счёт организации доступа не в заданном программистом, а в наиболее выгодном порядке. При наличии возможности параллельного выполнения нескольких операций возможно даже распараллеливание выполнения совместного цикла, когда одна и та же операция одновременно выполняется на разных вычислительных модулях для разных объектов, при том, что логически программа остаётся последовательной.
Совместные циклы имеются в некоторых языках программирования (C#, Eiffel, Java, JavaScript, Perl, Python, PHP, LISP, Tcl и др.) — они позволяют выполнять цикл по всем элементам заданной коллекции объектов. В определении такого цикла требуется указать только коллекцию объектов и переменную, которой в теле цикла будет присвоено значение обрабатываемого в данный момент объекта (или ссылка на него). В различных языках программирования синтаксис оператора различен:
C++:
for (type &item : set) //поддерживается, начиная со стандарта C++11 { //использование item }
C#:
foreach (type item in set) { //использование item }
Delphi:
for item in [1..100] do begin //Использование item (Работоспособность кода проверялась в Delphi 2010) end;
Perl (строгий порядок «от первого до последнего»):
foreach (@set) { #использование $_ } # или for (@set) { #использование $_ } # или foreach $item (@set) { #использование $item }
Eiffel:
across set as cursor loop -- использование cursor.item end
Java:
for (type item : set) { //использование item }
JavaScript:
for (txtProperty in objObject) { /* использование: objObject [txtProperty] */ }
PHP:
foreach ($arr as $item) { /* использование $item*/ } //или foreach ($arr as $key=>$value) { /* использование значений индекса $key и его значения $value*/ } //или foreach ($arr as &$item) { /*использование $item по ссылке*/ }
Visual Basic.NET:
For Each item As type In set 'использование item Next item
Windows PowerShell:
foreach ($item in $set) { # операции с $item }
или
$set | ForEach-Object { # операции с $_ }
Python
for item in iterator_instance: # использование item
Ruby
iterator_instance.each do |item| # использование item end
Досрочный выход и пропуск итерации
Многие языки программирования, имеющие в своём синтаксисе циклические конструкции, имеют также специфические команды, позволяющие нарушить порядок работы этих конструкций: команду досрочного выхода из цикла и команду пропуска итерации.
Досрочный выход из цикла
Команда досрочного выхода применяется, когда необходимо прервать выполнение цикла, в котором условие выхода ещё не достигнуто. Такое бывает, например, когда при выполнении тела цикла обнаруживается ошибка, после которой дальнейшая работа цикла не имеет смысла.
Команда досрочного выхода обычно называется EXIT
или break
, а её действие аналогично действию команды безусловного перехода (goto
) на команду, непосредственно следующую за циклом, внутри которого эта команда находится. Так, в языке Си два нижеприведённых цикла работают совершенно одинаково:
// Применение оператора break while(<условие>) { ... операторы if (<ошибка>) break; ... операторы } ... продолжение программы // Аналогичный фрагмент без break while(<условие>) { ... операторы if (<ошибка>) goto break_label; ... операторы } break_label: ... продолжение программы
В обоих случаях, если в теле цикла выполнится условие <ошибка>, будет произведён переход на операторы, обозначенные как «продолжение программы». Таким образом, оператор досрочного выхода из цикла, по сути, просто маскирует безусловный переход, однако использование break предпочтительнее, чем goto, поскольку поведение break чётко задано языком, потенциально менее опасно (нет, например, вероятности ошибиться с положением или названием метки). Кроме того, явный досрочный выход из цикла не нарушает принципов структурного программирования.
Обычный оператор досрочного выхода прерывает работу того цикла, в котором он непосредственно находится. В ряде языков программирования функциональность этого оператора расширена, он позволяет выходить из нескольких вложенных циклов (см. ниже). В таких случаях цикл, из которого требуется выйти, помечается меткой, а в операторе досрочного выхода указывается эта метка.
Пропуск итерации
Данный оператор применяется, когда в текущей итерации цикла необходимо пропустить все команды до конца тела цикла. При этом сам цикл прерываться не должен, условия продолжения или выхода должны вычисляться обычным образом.
В языке Си и его языках-потомках в качестве команды пропуска итерации используется оператор continue
в конструкции цикла. Действие этого оператора аналогично безусловному переходу на строку внутри тела цикла, следующую за последней его командой. Например, код на Си, находящий сумму элементов массива и сумму всех положительных элементов массива, может иметь следующий вид:
int arr[ARRSIZE]; ... // Суммирование отдельно всех и только положительных // элементов массива arr с применением continue. int sum_all = 0; int sum_pos = 0; for (int i = 0 ; i < ARRSIZE; ++i) { sum_all += arr[i]; if (arr[i] <= 0) continue; sum_pos += arr[i]; } // Аналогичный код c goto int sum_all = 0; int sum_pos = 0; for (int i = 0 ; i < ARRSIZE; ++i) { sum_all += arr[i]; if (arr[i] <= 0) goto cont_label; sum_pos += arr[i]; cont_label: }
Из второго фрагмента ясно видно, как работает continue
: он просто передаёт управление за последнюю команду тела цикла, пропуская выполнение команды суммирования, если очередной элемент массива не удовлетворяет условию. Таким образом, в sum_pos накапливается сумма лишь положительных элементов массива.
Необходимость
С точки зрения структурного программирования команды досрочного выхода из цикла и пропуска итерации являются избыточными, поскольку их действие может быть легко смоделировано чисто структурными средствами. Более того, по мнению ряда теоретиков программирования (в частности, Эдсгера Дейкстры), сам факт использования в программе неструктурных средств, будь то классический безусловный переход или любая из его специализированных форм, таких как break или continue, является свидетельством недостаточно проработанного алгоритма решения задачи.
Однако на практике код программы часто является записью уже имеющегося, ранее сформулированного алгоритма, перерабатывать который нецелесообразно по чисто техническим причинам. Попытка заменить в таком коде команду досрочного выхода на структурные конструкции часто оказывается неэффективной или громоздкой. Например, вышеприведённый фрагмент кода с командой break
может быть записан так:
// Досрочный выход из цикла без break bool flag = false; // флаг досрочного завершения while(<условие> && !flag) { ... операторы if (<ошибка>) { flag = true; } else { ... операторы } } ... продолжение программы
Легко убедиться, что фрагмент будет работать аналогично предшествующим, разница лишь в том, что в месте проверки на ошибку вместо непосредственного выхода из цикла устанавливается флаг досрочного выхода, который проверяется позже в штатном условии продолжения цикла. Однако для отказа от команды досрочного выхода пришлось добавить в программу описание флага и вторую ветвь условного оператора, к тому же произошло «размытие» логики программы (решение о досрочном выходе принимается в одном месте, а выполняется в другом). В результате программа не стала ни проще, ни короче, ни понятнее.
Несколько иначе обстоит дело с командой пропуска итерации. Она, как правило, очень легко и естественно заменяется на условный оператор. Например, приведённый выше фрагмент суммирования массива можно записать так:
int arr[ARRSIZE]; ... // Суммирование отдельно всех и только положительных // элементов массива arr с заменой continue int sum_all = 0; int sum_pos = 0; for (int i = 0 ; i < ARRSIZE; ++i) { sum_all += arr[i]; if (arr[i] > 0) // Условие заменено на противоположное! { sum_pos += arr[i]; } }
Как видим, достаточно было заменить проверяемое условие на противоположное и поместить заключительную часть тела цикла в условный оператор. Можно заметить, что программа стала короче (за счёт удаления команды пропуска итерации) и одновременно логичнее (из кода непосредственно видно, что суммируются положительные элементы).
Кроме того, использование команды пропуска итерации в цикле с условием (while-цикле) может также спровоцировать неочевидную ошибку: если тело цикла, как это часто бывает, завершается командами изменения переменной (переменных) цикла, то команда пропуска итерации пропустит и эти команды тоже, в результате чего (в зависимости от условия, по которому происходит пропуск) может произойти зацикливание или не соответствующий алгоритму повтор итерации. Так, если заменить в вышеприведённом примере цикл for на while, получится следующее:
int arr[ARRSIZE]; ... int sum_all = 0; int sum_pos = 0; int i = 0; while (i < ARRSIZE) // Цикл внешне аналогичен предыдущему for ... { sum_all += arr[i]; if (arr[i] <= 0) continue; sum_pos += arr[i]; ++i; // ... но эта команда будет пропущена при выполнении continue // и программа зациклится }
Несмотря на свою ограниченную полезность и возможность замены на другие языковые конструкции, команды пропуска итерации и, особенно, досрочного выхода из цикла в отдельных случаях оказываются крайне полезны, именно поэтому они сохраняются в современных языках программирования.
Вложенные циклы
Существует возможность организовать цикл внутри тела другого цикла. Такой цикл будет называться вложенным циклом. Вложенный цикл по отношению к циклу, в тело которого он вложен, будет именоваться внутренним циклом, и наоборот, цикл, в теле которого существует вложенный цикл, будет именоваться внешним по отношению к вложенному. Внутри вложенного цикла, в свою очередь, может быть вложен ещё один цикл, образуя следующий уровень вложенности и так далее. Количество уровней вложенности, как правило, не ограничивается.
Полное число исполнений тела внутреннего цикла не превышает произведения числа итераций внутреннего и всех внешних циклов. Например, взяв три вложенных друг в друга цикла, каждый по 10 итераций, получим 10 исполнений тела для внешнего цикла, 100 для цикла второго уровня и 1000 в самом внутреннем цикле.
Одна из проблем, связанных с вложенными циклами — организация досрочного выхода из них. Во многих языках программирования есть оператор досрочного завершения цикла (break
в Си, exit
в Турбо Паскале, last
в Perl и т. п.), но он, как правило, обеспечивает выход только из цикла того уровня, откуда вызван. Вызов его из вложенного цикла приведёт к завершению только этого внутреннего цикла, внешний же цикл продолжит выполняться. Проблема может показаться надуманной, но она действительно иногда возникает при программировании сложной обработки данных, когда алгоритм требует немедленного прерывания в определённых условиях, наличие которых можно проверить только в глубоко вложенном цикле.
Решений проблемы выхода из вложенных циклов несколько.
- Простейший — использовать оператор безусловного перехода goto для выхода в точку программы, непосредственно следующую за вложенным циклом. Этот вариант критикуется сторонниками структурного программирования, как и все конструкции, требующие использования goto. Некоторые языки программирования, например, Модула-2, просто не имеют оператора безусловного перехода, и в них подобная конструкция невозможна.
- Альтернатива — использовать штатные средства завершения циклов, в случае необходимости устанавливая специальные флаги, требующие немедленного завершения обработки. Недостаток — усложнение кода, снижение производительности.
- Размещение вложенного цикла в процедуре. Идея состоит в том, чтобы всё действие, которое может потребоваться прервать досрочно, оформить в виде отдельной процедуры, и для досрочного завершения использовать оператор выхода из процедуры (если такой есть в языке программирования). В языке Си, например, можно построить функцию с вложенным циклом, а выход из неё организовать с помощью оператора return. Недостаток — выделение фрагмента кода в процедуру не всегда логически обосновано, и не все языки имеют штатные средства досрочного завершения процедур.
- Воспользоваться механизмом генерации и обработки исключений (исключительных ситуаций), который имеется сейчас в большинстве языков высокого уровня. В этом случае в нештатной ситуации код во вложенном цикле возбуждает исключение, а блок обработки исключений, в который помещён весь вложенный цикл, перехватывает и обрабатывает его. Недостаток — реализация механизма обработки исключений в большинстве случаев такова, что скорость работы программы уменьшается. Правда, в современных условиях это не особенно важно: практически потеря производительности столь мала, что имеет значение лишь для очень немногих приложений.
- Наконец, существуют специальные языковые средства для выхода из вложенных циклов. Так, в языке Ада программист может пометить цикл (верхний уровень вложенного цикла) меткой, и в команде досрочного завершения цикла указать эту метку. Выход произойдёт не из текущего цикла, а из всех вложенных циклов до помеченного, включительно[3]. Язык PHP предоставляет возможность указать число прерываемых циклов после команды
break
— такbreak 2
прервёт сам цикл и вышестоящий над ним, аbreak 1
эквивалентно простой записи командыbreak
[4].
Циклы с несколькими охраняемыми ветвями
Цикл Дейкстры
В теории программирования известна ещё одна, принципиально отличающаяся от «классических», форма циклической конструкции, получившая название «цикл Дейкстры», по имени Эдсгера Дейкстры, впервые её описавшего. В классическом дейкстровском описании такой цикл выглядит следующим образом:
do P1 → S1, … Pn → Sn od
Здесь do
— маркер начала конструкции цикла, od
— маркер завершения конструкции цикла, Pi — i-е охраняющее условие (логическое выражение, которое может иметь значение «истинно» или «ложно»), Si — i-я охраняемая команда. Цикл состоит из одной или нескольких ветвей (охраняемых выражений), каждая из которых представляет собой пару из охраняющего условия (или, коротко, «охраны») и охраняемой команды (понятно, что в реальности команда может быть сложной).
При выполнении цикла Дейкстры в каждой итерации происходит вычисление охраняющих условий. Если хотя бы одно из них истинно, выполняется соответствующая охраняемая команда, после чего начинается новая итерация (если истинны несколько охраняющих условий, выполняется только одна охраняемая команда). Если все охраняющие условия ложны, цикл завершается. Нетрудно заметить, что цикл Дейкстры с одним охраняющим условием и одной охраняемой командой представляет собой, по сути, обычный цикл с предусловием (цикл «пока»).
Хотя цикл Дейкстры был изобретён ещё в 1970-х годах, специальных конструкций для его создания в языках программирования не содержится. Единственным исключением стал недавно созданный Оберон-07 — первый реальный язык программирования, явно поддерживающий цикл с несколькими охраняемыми ветвями. Впрочем, цикл Дейкстры может быть без больших затруднений смоделирован с помощью традиционных конструкций структурных языков программирования. Вот пример его реализации одним из возможных способов на языке Ада:
loop if P1 then S1; ... elsif Pn then Sn; else exit; end if; end loop;
Здесь P1—Pn — охраняющие условия, а S1—Sn — соответствующие охраняемые команды.
Цикл Дейкстры удобен при реализации некоторых специфических повторяющихся вычислений, которые неудобно описывать с помощью более традиционных циклических конструкций. Например, этим циклом естественно представляется конечный автомат — каждая ветвь соответствует одному состоянию автомата, охраняемые условия строятся так, чтобы в текущей итерации выбиралась ветвь, соответствующая текущему состоянию автомата, а код охраняемой команды обеспечивает выполнение вычислений в текущем состоянии и переход в следующее (то есть такое изменение переменных, после которого на следующей итерации будет истинным охраняющее условие нужной ветви).
Цикл «паук»
Легко видеть, что цикл Дейкстры не содержит явного условия продолжения или выхода, что не всеми теоретиками программирования рассматривается как благо. Поэтому была предложена усложнённая конструкция цикла Дейкстры, получившая название «цикл-‘паук’». В той же нотации она выглядит следующим образом:
do P1→S1, … Pn→Sn out Q1→T1, … Qn→Tn else E od
Здесь после маркера out
добавлены ветви завершения, состоящие из условий выхода Qi и команд завершения Ti. Кроме того, добавлена ветвь альтернативного завершения else
с командой E.
Цикл-‘паук’ выполняется так:
- Вычисляются охраняющие условия. Если существует истинное охраняющее условие, выполняется соответствующая охраняемая команда.
- Вычисляются условия выхода. Если существует истинное условие выхода, выполняется соответствующая команда завершения, после чего выполнение цикла заканчивается. Если все условия выхода ложны, начинается следующая итерация, но только в том случае, если в текущей итерации было истинным хотя бы одно из охраняющих условий.
- Если в данной итерации оказались ложными и все охраняющие условия, и все условия выхода, выполняется команда альтернативного завершения E, после чего выполнение цикла прерывается.
Структура цикла-‘паука’ позволяет предельно строго описать условия выполнения цикла. Согласно теоретическим положениям, ветвь альтернативного завершения не должна использоваться в качестве одного из вариантов корректного прекращения работы цикла (все такие варианты должны быть оформлены в виде соответствующих ветвей завершения с явным условием), она служит только для того, чтобы отследить ситуацию, когда по каким-то причинам цикл начал выполняться нештатно. То есть команда альтернативного завершения может лишь анализировать причины ошибки и представлять результаты анализа.
Хотя явной поддержки на уровне синтаксиса для этого цикла не существует ни в одном языке программирования, цикл-‘паук’, как и цикл Дейкстры, может быть смоделирован с помощью традиционных структурных конструкций.
Методы оптимизации циклов
- эквивалентными преобразованиями исходного кода
- Разбиение цикла на блоки
- Размотка цикла
- Размыкание цикла
- компилятором
- Расщепление цикла
- Расщепление тела цикла
- Слияние циклов
См. также
- Инварианты цикла (используются при проектировании и верификации циклов)
- Рекурсия
- Итератор (программирование)
Примечания
- ↑ Оберон — воплощение мечты Никлауса Вирта
- ↑ Строго говоря, тождество не полное, так как по-другому будет работать оператор continue.
- ↑ Loops/Nested at Rosetta Code (англ.)
- ↑ PHP Manual, break (англ.)
Ссылки
- Подробное описание циклических процессов в Паскале