Цикл (программирование) — Википедия
У этого термина существуют и другие значения, см. Цикл. Пример цикла While.Цикл — разновидность управляющей конструкции в высокоуровневых языках программирования, предназначенная для организации многократного исполнения набора инструкций. Также циклом может называться любая многократно исполняемая последовательность инструкций, организованная любым способом (например, с помощью условного перехода).
Последовательность инструкций, предназначенная для многократного исполнения, называется телом цикла. Единичное выполнение тела цикла называется итерацией. Выражение, определяющее, будет в очередной раз выполняться итерация или цикл завершится, называется условием выхода или условием окончания цикла (либо условием продолжения в зависимости от того, как интерпретируется его истинность — как признак необходимости завершения или продолжения цикла). Переменная, хранящая текущий номер итерации, называется
Исполнение любого цикла включает первоначальную инициализацию переменных цикла, проверку условия выхода, исполнение тела цикла и обновление переменной цикла на каждой итерации. Кроме того, большинство языков программирования предоставляет средства для досрочного управления циклом, например, операторы завершения цикла, то есть выхода из цикла независимо от истинности условия выхода (в языке Си — break
) и операторы пропуска итерации (в языке Си — continue
).
Безусловные циклы[править | править код]
Иногда в программах используются циклы, выход из которых не предусмотрен логикой программы. Такие циклы называются безусловными, или бесконечными. Специальных синтаксических средств для создания бесконечных циклов, ввиду их нетипичности, языки программирования не предусматривают, поэтому такие циклы создаются с помощью конструкций, предназначенных для создания обычных (или условных) циклов. Для обеспечения бесконечного повторения проверка условия в таком цикле либо отсутствует (если позволяет синтаксис, как, например, в цикле LOOP ... END LOOP
языка Ада), либо заменяется константным значением (while true do ...
в Паскале). В языке С используется цикл for(;;)
с незаполненными секциями или цикл while (1)
.
Цикл с предусловием[править | править код]
Цикл с предусловием — цикл, который выполняется, пока истинно некоторое условие, указанное перед его началом. Это условие проверяется до выполнения тела цикла, поэтому тело может быть не выполнено ни разу (если условие с самого начала ложно). В большинстве процедурных языков программирования реализуется оператором
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*/ }
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
Многие языки программирования, имеющие в своём синтаксисе циклические конструкции, имеют также специфические команды, позволяющие нарушить порядок работы этих конструкций: команду досрочного выхода из цикла и команду пропуска итерации.
Досрочный выход из цикла[править | править код]
Команда досрочного выхода применяется, когда необходимо прервать выполнение цикла, в котором условие выхода ещё не достигнуто. Такое бывает, например, когда при выполнении тела цикла обнаруживается ошибка, после которой дальнейшая работа цикла не имеет смысла.
Команда досрочного выхода обычно называется 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, после чего выполнение цикла прерывается.
Структура цикла-‘паука’ позволяет предельно строго описать условия выполнения цикла. Согласно теоретическим положениям, ветвь альтернативного завершения не должна использоваться в качестве одного из вариантов корректного прекращения работы цикла (все такие варианты должны быть оформлены в виде соответствующих ветвей завершения с явным условием), она служит только для того, чтобы отследить ситуацию, когда по каким-то причинам цикл начал выполняться нештатно. То есть команда альтернативного завершения может лишь анализировать причины ошибки и представлять результаты анализа.
Хотя явной поддержки на уровне синтаксиса для этого цикла не существует ни в одном языке программирования, цикл-‘паук’, как и цикл Дейкстры, может быть смоделирован с помощью традиционных структурных конструкций.
- эквивалентными преобразованиями исходного кода
- компилятором
Циклы в Паскале
При решении задач может возникнуть необходимость повторить одни и те же действия несколько или множество раз. В программировании блоки кода, которые требуется повторять не единожды, оборачиваются в специальные конструкции – циклы. У циклов выделяют заголовок и тело. Заголовок определяет, до каких пор или сколько раз тело цикла будет выполняться. Тело содержит выражения, которые выполняются, если в заголовке цикла выражение вернуло логическую истину (True, не ноль). После того как достигнута последняя инструкция тела, поток выполнения снова возвращается к заголовку цикла. Снова проверяется условие выполнения цикла. В зависимости от результата тело цикла либо повторяется, либо поток выполнения переходит к следующему выражению после всего цикла.
В языке программирования Паскаль существует три вида циклических конструкций.
Цикл for
Часто цикл for называют циклом со счетчиком. Этот цикл используется, когда число повторений не связано с тем, что происходит в теле цикла. Т.е. количество повторений может быть вычислено заранее (хотя оно не вычисляется).
В заголовке цикла указываются два значения. Первое значение присваивается так называемой переменной-счетчику, от этого значения начинается отсчет количества итераций (повторений). Отсчет идет всегда с шагом равным единице. Второе значение указывает, при каком значении счетчика цикл должен остановиться. Другими словами, количество итераций цикла определяется разностью между вторым и первым значением плюс единица. В Pascal тело цикла не должно содержать выражений, изменяющих счетчик.
Цикл for существует в двух формах:
for счетчик:=значение to конечное_значение do тело_цикла;
for счетчик:=значение downto конечное_значение do тело_цикла;
Счетчик – это переменная любого из перечисляемых типов (целого, булевого, символьного, диапазонного, перечисления). Начальные и конечные значения могут быть представлены не только значениями, но и выражениями, возвращающими совместимые с типом счетчика типы данных. Если между начальным и конечным выражением указано служебное слово to, то на каждом шаге цикла значение параметра будет увеличиваться на единицу. Если же указано downto, то значение параметра будет уменьшаться на единицу.
Количество итераций цикла for известно именно до его выполнения, но не до выполнения всей программы. Так в примере ниже, количество выполнений цикла определяется пользователем. Значение присваивается переменной, а затем используется в заголовке цикла. Но когда оно используется, циклу уже точно известно, сколько раз надо выполниться.
var i, n: integer; begin write ('Количество знаков: '); readln (n); for i := 1 to n do write ('(*) '); readln end.
Цикл while
Цикл while является циклом с предусловием. В заголовке цикла находится логическое выражение. Если оно возвращает true, то тело цикла выполняется, если false – то нет.
Когда тело цикла было выполнено, то ход программы снова возвращается в заголовок цикла. Условие выполнения тела снова проверяется (находится значение логического выражения). Тело цикла выполнится столько раз, сколько раз логическое выражение вернет true. Поэтому очень важно в теле цикла предусмотреть изменение переменной, фигурирующей в заголовке цикла, таким образом, чтобы когда-нибудь обязательно наступала ситуация false. Иначе произойдет так называемое зацикливание, одна из самых неприятных ошибок в программировании.
var i, n: integer; begin write ('Количество знаков: '); readln (n); i := 1; while i <= n do begin write ('(*) '); i := i + 1 end; readln end.
Цикл repeat
Цикл while может не выполниться ни разу, если логическое выражение в заголовке сразу вернуло false. Однако такая ситуация не всегда может быть приемлемой. Бывает, что тело цикла должно выполниться хотя бы один раз, не зависимо оттого, что вернет логическое выражение. В таком случае используется цикл repeat – цикл с постусловием.
В цикле repeat логическое выражение стоит после тела цикла. Причем, в отличие от цикла while, здесь всё наоборот: в случае true происходит выход из цикла, в случае false – его повторение.
var i, n: integer; begin write ('Количество знаков: '); readln (n); i := 1; repeat write ('(*) '); i := i + 1 until i > n; readln end.
В примере, даже если n будет равно 0, одна звездочка все равно будет напечатана.
Цикл while pascal ABC — цикл с предусловием, цикл-пока
При составлении программ часто возникает необходимость сократить запись кода при повторении однообразных команд. А когда количество их повторений заранее неизвестно и определяется некоторым условием, то можно использовать структуру — цикл с предусловием или цикл с постусловием.
Цикл — одна из форм управляющих конструкций в языках программирования. Используется для многократного выполнения набора команд.
Цикл с предусловием
На рисунке ниже представлена блок-схема цикла с предусловием. Другие названия цикла: цикл-пока, цикл с заданным условием продолжения работы.
Особенность данного вида цикла: операторы цикла будут повторяться до тех пор, пока условие истинно. Его проверка делается каждый раз перед выполнением операторов цикла. Цикл может ни разу не выполниться, если его условие ложно.
Цикл while pascalНа рисунке показано два варианта записи данной структуры на языке программирования Паскаль:
- с использованием операторных скобок (begin end), выполнение двух и более команд;
- запись с одной командой.
Примеры задач
Задание 1. Даны два положительных числа A и B (A > B). На отрезке длины A размещено максимально возможное количество отрезков длины B (без
наложений). Не используя операции умножения и деления, найдите длину незанятой части отрезка A.
Решение:
Вариант 1
program cycle1; var a,b:real; {a,b - вещественные числа } begin read(a,b); { ввод исходных значений длин отрезков } while a > b do a:=a-b; { цикл - вычисление остатка отрезка А } write(a); { вывод остатка отрезка А } end.
Вариант 2 (с подсказками)
program cycle1; var a,b:real; begin write('Введите длину отрезка А '); { вывод текстовой подсказки на экран } readln(a); { ввод исходного значения длины отрезка А } write('Введите длину отрезка В '); { вывод текстовой подсказки на экран } readln(b); { ввод исходного значения длины отрезка В } while a > b do a:=a-b; { цикл - вычисление остатка отрезка А } write('Длина незанятой части отрезка А равна ',a); { вывод остатка отрезка А } end.
Цикл с постусловием
На рисунке ниже представлена блок-схема цикла с постусловием. Другие названия цикла: цикл-до, цикл с заданным условием окончания работы.
Особенность данного вида цикла: операторы цикла будут повторяться до тех пор, пока условие ложно. Его проверка делается каждый раз после выполнения операторов цикла. Команды, входящие в тело цикла, всегда выполняются хотя бы один раз.
Цикл с постусловием ПаскальЗадание 2. Дано целое число N (> 0). Найти наименьшее целое положительное число K, квадрат которого превосходит N: K2 > N. Функцию извлечения квадратного корня не использовать.
Решение:
program cycle2; var n,k:integer; { n - заданное целое, k - искомое число} begin read(n); { ввод значения числа N } repeat k:=k+1; { вычисление проверяемого числа } until k*k > n; { проверка найденного числа на соответствие условию завершения цикла} write(k); { вывод найденного числа } end.
Циклы в Паскале
Когда в алгоритме некоторое действие нужно выполнить несколько раз, используются циклы. В программирование цикл — это многократное повторение определенных инструкций. Циклы состоят из заголовка и тела. Заголовок содержит в себе условия, которые определяют работу цикла, а тело – повторяющиеся действия. В ЯП Pascal есть три вида циклов:
цикл с параметром;
цикл с предусловием;
цикл с постусловием.
Их алгоритмы выполнения различны, но есть и общее: после выполнения тела цикла, проверяется условие, и в зависимости от него работа цикла заканчивается, либо снова выполняется тело.
For — цикл с параметром
Цикл с параметром, известный также как цикл со счетчиком, используется при известном количестве повторений. Он имеет две формы записи:
- For <счетчик>:=< начальное значение> To <конечное значение> Do <тело цикла>;
- For <счетчик>:=<начальное значение> Downto <конечное значение> Do <тело цикла>;
Счетчик – это переменная порядкового типа. Начальное и конечное значение должны быть того же типа, что и счетчик. Тело выполняется до тех пор пока условие истинно.
Формы записи, представленные выше, отличаются словами To и Downto. Если Вы используете цикл с To, то значение счетчика с каждым шагом будет увеличиваться на единицу, а если с Downto, то уменьшаться. Из этого следует, что в первом варианте начальное значение не должно превышать конечное, во втором — верно противоположное. В программе ниже, указанное пользователем количество раз, будут выводиться символы.
1 | program for_primer; |
Здесь тело цикла не заключено в Begin-End, так как оператор всего один. Но если их будет больше, то операторные скобки обязательны. Стоит также отметить, что счетчик по выходу из цикла не будет иметь определенного значения, но если цикл закончиться раньше положенного, то счетчик сохранит последнее, записанное в него значение.
While – цикл с предусловием
Оператор While – начинает описание цикла с предусловием. Такой вид цикла нужен, в тех алгоритмах, где число повторений неизвестно заранее. В общем виде он выглядит так:
While <выражение> Do <тело цикла>;
Если выражение истинно, то тело выполняется, иначе цикл завершается. Поэтому нужно составить такой код, чтобы на какой-то из итераций выражение стало ложным, и цикл не выполнялся бесконечно.
Пример программы написанный с использованием цикла While:
1 | program while_primer; |
В данном коде использовался составной оператор Begin-End, так как операторов в теле цикла несколько.
Repeat – цикл с постусловием
Главной особенностью цикла с постусловием (часто встречается название: цикл-ДО) является выполнение его тела минимум один раз. Это связано с тем, что условие записывается в конце и соответственно вначале выполнится тело, а затем провериться условие. Формально он выглядит так:
Repeat
<тело цикла>
Until <условие>
В противоположность двум рассмотренным прежде циклам, этот прекращает свое выполнение тогда, когда условие становиться истинным, т. е. чтобы итерации продолжались условие должно быть ложно. Рассмотрим работу цикла с постусловием на примере:
1 | program repeat_primer; |
Программа продолжает работать до тех пор, пока пользователь не допустит ошибку в ответе. Думаю, Вы заметили, что в примере (непосредственно в цикле) составной оператор Begin-End, несмотря на количество операторов не поставлен. Просто его роль выполняют слова repeat и until.
Для переходных манипуляций с циклом обычно используются три оператора:
Goto – переходит в отмеченную область;
Break – производит безусловный выход из цикла;
Continue – осуществляет переход к новой итерации.
Похожие записи:
Пример: цикл «До»
Начальные
присваивания
Тело цикла тело цикла — это последовательность действий,
которые выполняется многократно (в цикле).
да Условие
нет
Пример: цикл «пока»
Начальные присваивания
нет
Условие
да
Тело цикла
4. Запись текста программы на языке программирования.
Используя разработанный алгоритм, можно переходить к написанию текста программы на языке программирования. Приведем краткое описание ЯВУ(языка высокого уровня) Бейсик.
Наименование BASIC является сокращением английских слов или абривиатура «Beginner’s All-purpose Symbolic Instruction Code», что в переводе означает «многоцелевой язык символических команд для начинающих».
Алфавит БЕЙСИКа содержит 26 прописных латинских букв, цифры от «0» до «9», точку,
запятую, кавычки, знаки арифметических операций +, -, /, *, ^, знаки отношения: <, >, =, >=, <=. Русский алфавит используется только для написания комментариев.
Представление данных.
Константы
Константы–данные, имеющие в процессе выполнения программы единственное значение, определенное в исходном тексте программы.
Переменные–данные, которые могут изменять свои значения в процессе выполнения программы.
Типы данных:
Константы. Арифметические константы.
целочисленные константы – целые числа от -32768 до +32767 (2 байта).
константы с фиксированной точкой. Имеют в своем составе десятичную точку, т.е. имеют целую и дробную часть числа
константы с плавающей точкой. Состоят из мантиссы (mx) – целое или с фиксированной точкой, порядка числа ( р ) и q – система счисления.
X=mx*qp
Знак числа определяет мантисса.
Они могут быть представлены с простой точностью (Е) и двойной (D).
Пример1: Целочисленная константа.
235 235 или +235
Пример2: Константа с фиксированной точкой.
27,45 27.45
+0,258 +0.258 или .258
Пример3: Константа с плавающей точкой.
2,349 0.2349 Е1 или 2.349Е0
—0,00025 -2.5Е-4
5,4*10-8 5.4Е-8
Строковая константа представляет собой заключенную в кавычки последовательность любых символов.
Переменные также делятся на:
Имена переменных могут быть до 40 символов (латинские буквы, цифры).
Существует 2 способа определения типа переменной.
Первый способ. Тип простой переменной определяется последним символом в имени.
$ — строковая
% — целочисленная (2 байта памяти).
! – числовая переменная с простой точностью (4 байта памяти, 6 цифр после запятой).
# — числовая переменная с двойной точностью (8 байт памяти, 14 цифр после запятой).
& — длинное целое
Пример:
BAX! – переменная с простой точностью
BAX% — целочисленная переменная
RUBL% — целочисленная переменная
Второй способ. Использование специальных операторов (операторы описания).
DEFINT A – целое %
DEFLNG A – длинное целое &
DEFSNG A – обычная точность !
DEFDBL A – двойная точность #
DEFSTR A – строковая $
Переменная с индексом позволяет обращаться к элементам массива.
Массив – упорядоченная совокупность однотипных величин.
Чтобы обратится к массиву, нужно указать имя массива.
Пример:
Имя массива – ГР0605у(24),
где 24 – количество элементов массива.
Элемент массива:
ГР0605у(2) Иванов
ГР0605у(11) Петров
Задание в программе имени массива, тип элементов и их количество называется объявлением массива и обязательно производится до первого использования переменных массива. Оператор DIM выполняет эту задачу.
Пример:
DIM GR%(24) – одномерный массив;
DIM GR!(5,10) – двумерный массив.
Цикл (программирование) — это… Что такое Цикл (программирование)?
У этого термина существуют и другие значения, см. цикл. Пример цикла While.Цикл — разновидность управляющей конструкции в высокоуровневых языках программирования, предназначенная для организации многократного исполнения набора инструкций. Также циклом может называться любая многократно исполняемая последовательность инструкций, организованная любым способом (например, с помощью условного перехода).
Определения
Последовательность инструкций, предназначенная для многократного исполнения, называется телом цикла. Единичное выполнение тела цикла называется итерацией. Выражение определяющее, будет в очередной раз выполняться итерация, или цикл завершится, называется условием выхода или условием окончания цикла (либо условием продолжения в зависимости от того, как интерпретируется его истинность — как признак необходимости завершения или продолжения цикла). Переменная, хранящая текущий номер итерации, называется счётчиком итераций цикла или просто счётчиком цикла. Цикл не обязательно содержит счётчик, счётчик не обязан быть один — условие выхода из цикла может зависеть от нескольких изменяемых в цикле переменных, а может определяться внешними условиями (например, наступлением определённого времени), в последнем случае счётчик может вообще не понадобиться.
Исполнение любого цикла включает первоначальную инициализацию переменных цикла, проверку условия выхода, исполнение тела цикла и обновление переменной цикла на каждой итерации. Кроме того, большинство языков программирования предоставляют средства для досрочного управления циклом, например, операторы завершения цикла, то есть выхода из цикла независимо от истинности условия выхода (в языке Си — break
) и операторы пропуска итерации (в языке Си — continue
).
Виды циклов
Безусловные циклы
Иногда в программах используются циклы, выход из которых не предусмотрен логикой программы. Такие циклы называются безусловными, или бесконечными. Специальных синтаксических средств для создания бесконечных циклов, ввиду их нетипичности, языки программирования не предусматривают, поэтому такие циклы создаются с помощью конструкций, предназначенных для создания обычных (или условных) циклов. Для обеспечения бесконечного повторения проверка условия в таком цикле либо отсутствует (если позволяет синтаксис, как, например, в цикле LOOP…END LOOP языка Ада), либо заменяется константным значением (while true do … в Паскале). В языке С используется цикл for(;;) с незаполненными секциями.
Цикл с предусловием
Цикл с предусловием — цикл, который выполняется пока истинно некоторое условие, указанное перед его началом. Это условие проверяется до выполнения тела цикла, поэтому тело может быть не выполнено ни разу (если условие с самого начала ложно). В большинстве процедурных языков программирования реализуется оператором 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, может быть, какое-то другое? А если цикл завершится досрочно? Ответы зависят от того, увеличивается ли значение счётчика после последней итерации и не изменяет ли транслятор это значение дополнительно. Ещё один вопрос: что будет, если внутри цикла счётчику будет явно присвоено новое значение? Различные языки программирования решают данные вопросы по-разному. В некоторых поведение счётчика чётко регламентировано. В других, например, в том же Паскале, стандарт языка не определяет ни конечного значения счётчика, ни последствий его явного изменения в цикле, но не рекомендует изменять счётчик явно и использовать его по завершении цикла без повторной инициализации. Программа на Паскале, игнорирующая эту рекомендацию, может давать разные результаты при выполнении на разных системах и использовании разных трансляторов.
Радикально решён вопрос в языке Ада: счётчик считается описанным в заголовке цикла, и вне его просто не существует. Даже если имя счётчика в программе уже используется, внутри цикла в качестве счётчика используется отдельная переменная. Счётчику запрещено явно присваивать какие бы то ни было значения, он может меняться только внутренним механизмом оператора цикла. В результате конструкция
i := 100; for i in (0..9) loop ... тело цикла end loop; k := i;
внешне аналогичная вышеприведённому циклу на Паскале, трактуется однозначно: переменной k будет присвоено значение 100, поскольку переменная i, используемая вне данного цикла, не имеет никакого отношения к счётчику i, который создаётся и изменяется внутри цикла. Подобное обособление счётчика удобно и безопасно: не требуется отдельное описание для него и минимальна вероятность случайных ошибок, связанных со случайным разрушением внешних по отношению к циклу переменных. Если программисту требуется включить в готовый код цикл со счётчиком, то он может не проверять, существует ли переменная с именем, которое он выбрал в качестве счётчика, не добавлять описание нового счётчика в заголовок соответствующей процедуры, не пытаться использовать один из имеющихся, но в данный момент «свободных» счётчиков. Он просто пишет цикл с переменной-счётчиком, имя которой ему удобно, и может быть уверен, что никакой коллизии имён не произойдёт.
Цикл со счётчиком всегда можно записать как условный цикл, перед началом которого счётчику присваивается начальное значение, а условием выхода является достижение счётчиком конечного значения; к телу цикла при этом добавляется оператор изменения счётчика на заданный шаг. Однако специальные операторы цикла со счётчиком могут эффективнее транслироваться, так как формализованный вид такого цикла позволяет использовать специальные процессорные команды организации циклов.
В некоторых языках, например, Си и других, произошедших от него, цикл for, несмотря на синтаксическую форму цикла со счётчиком, в действительности является циклом с предусловием. То есть в Си конструкция цикла:
for (i = 0; i < 10; ++i) { ... тело цикла }
фактически представляет собой другую форму записи конструкции[1]:
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*/ }
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
Досрочный выход и пропуск итерации
Многие языки программирования, имеющие в своём синтаксисе циклические конструкции, имеют также специфические команды, позволяющие нарушить порядок работы этих конструкций: команду досрочного выхода из цикла и команду пропуска итерации.
Досрочный выход из цикла
Команда досрочного выхода применяется, когда необходимо прервать выполнение цикла, в котором условие выхода ещё не достигнуто. Такое бывает, например, когда при выполнении тела цикла обнаруживается ошибка, после которой дальнейшая работа цикла не имеет смысла.
Команда досрочного выхода обычно называется 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. Недостаток — выделение фрагмента кода в процедуру не всегда логически обосновано, и не все языки имеют штатные средства досрочного завершения процедур.
- Воспользоваться механизмом генерации и обработки исключений (исключительных ситуаций), который имеется сейчас в большинстве языках высокого уровня. В этом случае в нештатной ситуации код во вложенном цикле возбуждает исключение, а блок обработки исключений, в который помещён весь вложенный цикл, перехватывает и обрабатывает его. Недостаток — реализация механизма обработки исключений в большинстве случаев такова, что скорость работы программы уменьшается. Правда, в современных условиях это не особенно важно: практически потеря производительности столь мала, что имеет значение лишь для очень немногих приложений.
- Наконец, существуют специальные языковые средства для выхода из вложенных циклов. Так, в языке Ада программист может пометить цикл (верхний уровень вложенного цикла) меткой, и в команде досрочного завершения цикла указать эту метку. Выход произойдёт не из текущего цикла, а из всех вложенных циклов до помеченного, включительно[2]. Язык PHP предоставляет возможность указать число прерываемых циклов после команды
break
— такbreak 2
прервёт сам цикл и вышестоящий над ним, аbreak 1
эквивалентно простой записи командыbreak
[3].
Циклы с несколькими охраняемыми ветвями
Цикл Дейкстры
В теории программирования известна ещё одна, принципиально отличающаяся от «классических», форма циклической конструкции, получившая название «цикл Дейкстры», по имени Эдсгера Дейкстры, впервые её описавшего. В классическом дейкстровском описании такой цикл выглядит следующим образом:
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, после чего выполнение цикла прерывается.
Структура цикла-‘паука’ позволяет предельно строго описать условия выполнения цикла. Согласно теоретическим положениям, ветвь альтернативного завершения не должна использоваться в качестве одного из вариантов корректного прекращения работы цикла (все такие варианты должны быть оформлены в виде соответствующих ветвей завершения с явным условием), она служит только для того, чтобы отследить ситуацию, когда по каким-то причинам цикл начал выполняться нештатно. То есть команда альтернативного завершения может лишь анализировать причины ошибки и представлять результаты анализа.
Хотя явной поддержки на уровне синтаксиса для этого цикла не существует ни в одном языке программирования, цикл-‘паук’, как и цикл Дейкстры, может быть смоделирован с помощью традиционных структурных конструкций.
Интересные факты
- Никлаус Вирт одно время называл цикл со счётчиком «маргинальным», утверждая, что такая конструкция является излишней и должна быть исключена из синтаксиса языков программирования как несистемная. В соответствии с этим представлением в языке программирования Оберон цикла со счётчиком не было. Однако в языке Оберон-2, созданном Виртом и Мёссенбёком в развитие Оберона, цикл со счётчиком FOR появился снова в интересах практического удобства использования[4].
См. также
Методы оптимизации циклов
Примечания
Ссылки
как считать, какая норма и сколько дней длится цикл месячных?
Менструальный цикл — нормальный процесс женской физиологии, но в реальности часто оказывается, что он протекает не так гладко, как должен бы. Головная боль, отеки, скачки артериального давления — вот неполный список проблем, с которыми сталкиваются накануне «этих дней» от 70 до 90% женщин. Треть из них вынуждены обращаться к врачу из-за выраженных симптомов[1]. Проблемы, доставляемые менструальным циклом, не минуют и девочек-подростков: в период взросления от 50 до 75% девушек обращаются к детскому гинекологу из-за нерегулярного цикла[2]. Есть ли возможность справиться с подобными проблемами, не прибегая к тяжелой артиллерии в виде гормональных препаратов? Давайте разбираться.
Что такое цикл месячных и как его считать?
Цикл месячных, если говорить медицинским языком, — это комплекс циклических изменений в репродуктивной системе женщины. Он регулируется гормонами гипоталамуса, гипофиза и яичников, под влиянием которых возникают функциональные изменения в маточных трубах, матке, влагалище и молочных железах. Менструация — лишь видимое проявление этих изменений.
Первый день менструального кровотечения считается первым днем цикла. Нормальная продолжительность цикла — от 21 до 35 дней. Чаще всего до первого дня следующей менструации проходит 28 дней — такая длительность называется идеальным циклом. Она совпадает с продолжительностью лунного месяца, а в древности считалось, что женская физиология регулируется Луной.
Первая менструация в жизни девушки называется менархе. Обычно она возникает в возрасте от 11 до 15 лет, чаще всего в 12–14. По статистике, чем лучше питается девушка, чем она выше и крупнее, тем раньше наступает менархе.
Изменения, которые происходят в женском организме во время менструального цикла можно разделить на 2 этапа.
Первая, или фолликулярная, фаза цикла начинается с первого дня менструации и продолжается обычно 11–14 дней, заканчиваясь овуляцией — выходом яйцеклетки из яичника.
В это время под действием особых гормонов гипофиза в яичнике начинает созревать один из фолликулов — особых образований, содержащих зачаток яйцеклетки. Первичные, или примордиальные, фолликулы формируются еще во время внутриутробного периода развития девочки. К моменту полового созревания их остается от 200 до 450 тысяч, и лишь 400–500 из них в течение жизни сформируются окончательно, выпустив зрелую яйцеклетку. Остальные постепенно отмирают — атрофируются.
На первые 3–5 дней этого времени приходится, собственно, менструация, во время которой матка отторгает слизистую оболочку и та покидает организм. Созревающий фолликул выделяет гормоны, которые стимулируют восстановление слизистой оболочки матки. В идеале фолликулярная фаза продолжается 14 дней, на ее исходе созревший фолликул раскрывается, выпуская готовую к зачатию яйцеклетку. Этот процесс называется овуляцией.
В первую фазу цикла особенно высока потребность в витаминах группы B, фолиевой кислоте и цинке. Витамины группы В и фолиевая кислота регулируют работу системы «гипоталамус-гипофиз-яичники», то есть слаженность «гормонального оркестра», — в это время рекомендуются их удвоенные дозировки. Цинк способствует росту и созреванию яйцеклеток.
После овуляции — появления созревшей яйцеклетки — наступает вторая, или лютеиновая, фаза менструального цикла. Она продолжается 12–14 дней.
На месте фолликула в яичнике формируется особое образование — желтое тело, которое начинает производить гормон прогестерон, известный также как гормон беременности. Его функция — подготовить организм к вынашиванию ребенка и обеспечить нормальное течение беременности. Под действием прогестерона слизистая оболочка матки утолщается, готовясь принять оплодотворенную яйцеклетку. Если имплантация проходит успешно, желтое тело продолжает развиваться, поддерживая состоявшуюся беременность. Если же зачатие не состоялось, желтое тело начинает постепенно уменьшаться в размерах — атрофироваться. Непосредственно перед менструацией в организме стремительно падает уровень половых гормонов, из-за чего сосуды матки резко сокращаются. Лишенная нормального кровотока слизистая матки отмирает — начинается новая менструация.
В лютеиновую фазу цикла возрастает потребность в витаминах С и Е, которые совместно усиливают действие друг друга; в магнии, дефицит которого может способствовать развитию ПМС; в кальции и витамине D, низкий уровень которых часто сопровождает снижение настроения в конце второй фазы цикла. Сохраняется высокая потребность в витаминах группы В, фолиевой кислоте и цинке.
Такие изменения в женском организме происходят постоянно на протяжении всего времени от менархе до менопаузы.
Нормальная менструация безболезненна. Могут ощущаться небольшие спазмы, не нарушающие общего самочувствия. Длительность самой менструации в норме — 3–5 дней, за это время организм теряет от 50 до 80 мл крови. Кровь должна отходить равномерно, не формируя сгустки.
Любые отклонения от этой картины могут быть признаком патологии, поэтому рекомендуется обратиться к врачу за консультацией.
Нарушения менструального цикла в репродуктивном периоде
Отклонения от нормального протекания менструального цикла в репродуктивном периоде могут иметь различный характер. Рассмотрим вкратце основные виды этих нарушений.
Физиологическая аменорея — отсутствие менструаций — наступает во время беременности, кормления грудью, во время менопаузы. Отсутствие менструации до менархе также считается физиологической аменореей.
Патологическая аменорея может быть первичной или вторичной.
При первичной аменорее менструаций не было изначально, менархе не наступило по достижению возраста 16 лет. Это может быть связано с нарушением гормонального баланса — врожденным (неправильный хромосомный набор) или приобретенным (нарушение функции гипофиза, яичников, надпочечников, щитовидной железы). Кроме того, причиной может служить врожденная патология развития половой системы (заращение девственной плевы, влагалища, отсутствие матки и пр.).
Вторичная аменорея вызывается как гормональным дисбалансом (синдром поликистозных яичников, метаболический синдром, патология надпочечников или щитовидной железы), так и другими причинами. Нередко аменорея возникает под влиянием продолжительного сильного стресса или нарушения питания — такая ситуация особенно характерна для молодых женщин, активно стремящихся похудеть.
Дисменорея, или, как ее иногда называют, альгоменорея (болезненные менструации), так же как и аменорея, бывает первичной или вторичной. Вторичная дисменорея вызвана каким-либо патологическим процессом в матке: воспалением, опухолью и так далее. Первичная дисменорея не имеет обнаружимых патологических предпосылок и возникает на фоне нарушения нормального соотношения между эстрогенами — гормонами, главенствующими в первую половину цикла, и прогестероном, контролирующим вторую половину цикла. На фоне относительно невысокого количества прогестерона эффекты эстрогена становятся более выраженными. Так возникает не только болезненность во время менструации, вызванная излишне активными сокращениями матки, но и проявления ПМС, или предменструального синдрома.
Предменструальный синдром — дискомфорт, возникающий в лютеиновую фазу цикла, за несколько дней до менструации, и прекращающийся с наступлением критических дней. Как уже было упомянуто, в той или иной мере от него страдают до 90% женщин. Многие девушки не обращают внимания на повышенную раздражительность или плохое настроение в «эти дни», считая это вариантом нормы (а глупые шутки про ПМС поддерживают подобное мнение). На самом деле раздражительность и плаксивость перед менструацией — это ненормально. Не говоря уж о более серьезных проблемах.
Существует несколько клинических форм ПМС:
- Нейровегетативная (или психовегетативная) — проявляется резкой сменой настроений, агрессивностью, депрессиями, сонливостью или, наоборот, бессонницей.
- Отечная. Нагрубают и становятся болезненными молочные железы. Отекают ноги, вплоть до того, что приходится надевать обувь другого размера. Вздувается живот. В организме женщины с отечной формой ПМС может задерживаться до 4–8 л жидкости.
- Цефалгическая — проявляется головной болью, причем боль эта может проявляться по-разному: имитировать мигрень (болит половина головы, боль сопровождается тошнотой, светобоязнью, шумом в ушах), головные боли напряжения (каска или обруч, сжимающий голову) или сосудистые боли (пульсирующие, распирающие).
- Кризовая — имитирует паническую атаку, которая начинается с повышения артериального давления, сердцебиения. Появляется озноб, боли за грудиной, учащается дыхание, возникает чувство беспричинного страха.
Возможны и атипичные формы ПМС:
- Гипертермическая — повышение температуры тела без изменений в крови, характерных для воспаления.
- Офтальмоплегическая — односторонний паралич глаза на фоне мигрени.
- Циклические аллергические реакции — крапивница, проявления бронхиальной астмы, неукротимая рвота, отек Квинке.
Если же говорить о наиболее распространенных проявлениях предменструального синдрома, то это:
- болезненность и нагрубание груди — 90%,
- вздутие живота — 90%,
- повышенная утомляемость — 90%,
- раздражительность, угнетенное состояние — 80%,
- повышенный аппетит — 70%,
- рассеянность, забывчивость — 50%,
- головокружение — 20%,
- учащенное сердцебиение — 15%[3].
В легкой форме предменструальный синдром возникает за 2–10 дней до начала менструации и проявляется не более чем 4 симптомами, причем ярко выражены 1–2 из них. В тяжелой форме ПМС появляется за 3–14 дней до менструации, проявляется 5–12 симптомами, причем резко выражены 2–5 признаков.
Но в легкой, в тяжелой ли форме, предменструальный синдром — состояние, требующее коррекции. Точно так же в коррекции нуждается и нерегулярный цикл, и болезненность во время менструации.
Причины нерегулярного цикла и последствия
Нерегулярный менструальный цикл и предменструальный синдром — результат гормонального дисбаланса. Но нарушение нормального соотношения гормонов может быть вызвано различными причинами, самая частая из которых — хронический недостаток витаминов. Уже в детстве 90% населения России страдает от гиповитаминоза[4] — недостаточного поступления витаминов в организм, и с возрастом ситуация лишь усугубляется.
Между тем доказано, что витамины и минералы — главные регуляторы обмена веществ, в том числе и гормональных взаимодействий. Так, недостаток витаминов А, С, фолиевой кислоты и витамина В6 нарушает процесс инактивации эстрогенов печенью, что может повлиять на соотношение эстрогенов и прогестерона. Привести к нарушению менструального цикла вплоть до аменореи способен дефицит витаминов В1, В2, В6, В9, В12. Доказано, что у женщин с предменструальным синдромом снижен уровень витамина D, количество магния в лютеиновую фазу цикла, нарушено нормальное соотношение между магнием и кальцием. Со временем нарушения накапливаются, провоцируя более серьезные заболевания.
Как сделать цикл регулярным?
Нерегулярный менструальный цикл — это не просто неудобно. Это также нарушает нормальное течение жизни: почти все женщины сталкивались с ситуацией, когда внезапно начавшаяся менструация вынуждала бросать все дела и бежать за прокладкой. Приходится переносить встречи, отказываться от посещения спортзала или бассейна, а планирование отпуска при нерегулярном цикле и вовсе превращается в лотерею. Поэтому бороться с проблемой необходимо. Не нужно терпеть, не нужно полагать, что боль и дискомфорт — это нормально.
Существует два подхода в борьбе с этим состоянием. Первый — это коррекция витаминно-минерального баланса в организме, который, в свою очередь, влияет на гормональный баланс. Витамины и минералы практически не имеют противопоказаний и побочных эффектов. При этом принимать их рекомендуется не просто так, а с учетом разной потребности в различные периоды цикла: в фолликулярную фазу необходимо поддерживать активность эстрогена, а в лютеиновую — прогестерона.
Второй подход — непосредственное изменение гормонального баланса препаратами, содержащими синтетические аналоги эстрогенов и прогестерона. Такие средства имеют определенный список противопоказаний и побочных эффектов, и их можно принимать только по назначению врача и под медицинским контролем. Впрочем, у гормональных препаратов есть и свои преимущества: они жестко регулируют цикл, делая его максимально предсказуемым, позволяют «пропустить» менструацию, если она приходится, к примеру, на морской отпуск.
По данным врачей, до 5% пациенток с ПМС временно теряют трудоспособность из-за проявлений этого заболевания[5]. Поэтому ПМС — это совсем не смешно. И это не нормальное состояние женщины, а болезнь, требующая лечения.