В информатике , управление потоком (или поток управления ) это порядок , в котором отдельные заявления , инструкция или вызовы функций из в императивной программе будут выполнены или оценены. Акцент на явном потоке управления отличает императивный язык программирования от декларативного языка программирования .
В императивном языке программирования оператор потока управления - это оператор, в результате которого делается выбор, по какому из двух или более путей следовать. Для нестрогих функциональных языков существуют функции и языковые конструкции для достижения того же результата, но они обычно не называются операторами потока управления.
Набор операторов, в свою очередь, обычно структурирован как блок , который помимо группировки также определяет лексическую область видимости .
Прерывания и сигналы механизмы низкого уровня , которые могут изменить поток управления таким образом , похожей на подпрограмму , но обычно возникают в ответ на какой - то внешний стимул или событие (которое может произойти асинхронно ), а не исполнение с в линии оператор потока управления.
На уровне машинного языка или языка ассемблера инструкции потока управления обычно работают путем изменения счетчика программ . Для некоторых центральных процессоров (ЦП) единственными доступными инструкциями потока управления являются условные или безусловные инструкции перехода , также называемые переходами .
Категории
Типы операторов потока управления, поддерживаемые разными языками, различаются, но их можно разделить на категории по их эффекту:
- Продолжение в другом заявлении (безусловная ветвь или прыгать)
- Выполнение набора операторов только при соблюдении некоторого условия (выбор - т.е. условная ветвь )
- Выполнение набора операторов ноль или более раз, пока не будет выполнено какое-либо условие (т. Е. Цикл - то же, что и условный переход )
- Выполнение набора удаленных операторов, после которого поток управления обычно возвращается ( подпрограммы , сопрограммы и продолжения )
- Остановка программы, предотвращение дальнейшего выполнения (безусловная остановка)
Примитивы
Этикетки
Метка является явным именем или номер , присвоенное фиксированной позиции в пределах исходного кода , и которые могут ссылаться заявление управления потоком появляется в другом месте в исходном коде. Метка отмечает позицию в исходном коде и не имеет никакого другого эффекта.
Номера строк являются альтернативой именованной метке, используемой в некоторых языках (например, BASIC ). Это целые числа, помещенные в начале каждой строки текста в исходном коде. Языки, в которых они используются, часто накладывают ограничение, состоящее в том, что номера строк должны увеличиваться в значении в каждой следующей строке, но могут не требовать, чтобы они были последовательными. Например, в BASIC:
10 ПОЗВОНИТЬ X = 3 20 ПЕЧАТЬ X
В других языках, таких как C и Ada , метка - это идентификатор , обычно появляющийся в начале строки и сразу за которым следует двоеточие. Например, в C:
Успех : printf ( «Операция прошла успешно. \ N » );
Язык АЛГОЛ 60 допускает как целые числа, так и идентификаторы в качестве меток (оба связаны двоеточием со следующим утверждением), но немногие, если вообще какие-либо другие варианты АЛГОЛА допускают целые числа. Ранние компиляторы Fortran допускали в качестве меток только целые числа. Начиная с Fortran-90, также разрешены буквенно-цифровые метки.
Перейти к
Оператор goto (комбинация английских слов go и to , произносимых соответствующим образом) является самой простой формой безусловной передачи управления.
Хотя ключевое слово может быть в верхнем или нижнем регистре в зависимости от языка, обычно оно записывается как:
метка goto
Эффект оператора goto заключается в том, что следующий оператор, который будет выполняться, будет оператором, появляющимся на (или сразу после) указанной метке.
Многие компьютерные ученые, особенно Дейкстра, считали утверждения Goto вредными .
Подпрограммы
Терминология для подпрограмм варьируется; в качестве альтернативы они могут называться подпрограммами, процедурами, функциями (особенно если они возвращают результаты) или методами (особенно если они принадлежат классам или классам типов ).
В 1950 - х годах, компьютерные воспоминания были очень маленькими по современным стандартам , так Подпрограммы использовались в основном [ править ] , чтобы уменьшить размер программы. Кусок кода был написан один раз, а затем многократно использовался из разных мест в программе.
Сегодня подпрограммы чаще используются, чтобы помочь сделать программу более структурированной, например, путем изолирования некоторого алгоритма или сокрытия некоторого метода доступа к данным. Если много программистов работают над одной программой, подпрограммы - это один из видов модульности, который может помочь разделить работу.
Последовательность
В структурированном программировании упорядоченная последовательность последовательных команд считается одной из базовых структур управления, которая используется как строительный блок для программ наряду с итерацией, рекурсией и выбором.
Минимальный структурированный поток управления
В мае 1966 года Бём и Якопини опубликовали статью [1] в « Коммуникациях ACM», в которой было показано, что любую программу с goto s можно преобразовать в форму без goto, включающую только выбор (IF THEN ELSE) и циклы (WHILE condition DO xxx ), возможно, с дублированным кодом и / или добавлением логических переменных (флаги true / false). Позднее авторы показали, что выбор можно заменить циклами (и тем более логическими переменными).
То, что такой минимализм возможен, не означает, что он обязательно желателен; в конце концов, компьютерам теоретически нужна только одна машинная инструкция (вычтите одно число из другого и выполните переход, если результат отрицательный), но на практике компьютеры имеют десятки или даже сотни машинных инструкций.
Статья Бема и Якопини показала, что все программы могут быть свободными от goto. Другое исследование показало, что управляющие структуры с одним входом и одним выходом были намного проще для понимания, чем любые другие формы, [ цитата необходима ] в основном потому, что их можно было использовать где угодно как утверждение, не нарушая потока управления. Другими словами, их можно было компоновать . (Более поздние разработки, такие как нестрогие языки программирования - и в последнее время составные программные транзакции - продолжили эту стратегию, сделав компоненты программ еще более свободно компонуемыми.)
Некоторые ученые придерживались пуристского подхода к результату Бема-Якопини и утверждали, что даже инструкции типа break
и return
из середины циклов являются плохой практикой, поскольку они не нужны в доказательстве Бема-Якопини, и поэтому они выступали за то, чтобы все циклы имели единый точка выхода. Этот пуристический подход воплощен в языке Pascal (разработанный в 1968–1969), который до середины 1990-х годов был предпочтительным инструментом для обучения вводному программированию в академических кругах. [2] Прямое применение теоремы Бема-Якопини может привести к добавлению дополнительных локальных переменных в структурированную диаграмму, а также может привести к некоторому дублированию кода . [3] На Паскаль влияют обе эти проблемы, и согласно эмпирическим исследованиям, процитированным Эриком С. Робертсом , студентам-программистам было трудно сформулировать правильные решения на Паскале для нескольких простых задач, включая написание функции для поиска элемента в массиве. Исследование 1980 года Генри Шапиро, цитируемое Робертсом, показало, что при использовании только управляющих структур, предоставленных Паскалем, правильное решение было дано только 20% испытуемых, в то время как ни один из испытуемых не написал неправильный код для этой проблемы, если бы разрешили написать ответ из середина петли. [2]
Управляющие структуры на практике
Большинство языков программирования с управляющими структурами имеют начальное ключевое слово, которое указывает тип задействованной управляющей структуры. [ требуется пояснение ] Затем языки разделяются на предмет наличия или отсутствия у управляющих структур последнего ключевого слова.
- Нет последнего ключевого слова: ALGOL 60 , C , C ++ , Haskell , Java , Pascal , Perl , PHP , PL / I , Python , PowerShell . Таким языкам нужен какой-то способ группировки операторов вместе:
- АЛГОЛ 60 и Паскаль:
begin
...end
- C, C ++, Java, Perl, PHP и PowerShell: фигурные скобки
{
...}
- PL / I:
DO
...END
- Python: использует уровень отступа (см. Автономное правило )
- Haskell: можно использовать либо уровень отступа, либо фигурные скобки, и их можно свободно смешивать
- Lua: использует
do
...end
- АЛГОЛ 60 и Паскаль:
- Последнее ключевое слово: Ада , АЛГОЛ 68 , Модула-2 , Фортран 77 , Мифрил , Visual Basic . Формы последнего ключевого слова различаются:
- Ada: последнее ключевое слово - это
end
+ пробел + начальное ключевое слово, например,if
...end if
,loop
...end loop
- АЛГОЛ 68, мифрил: начальное ключевое слово написано в обратном порядке, например,
if
...fi
,case
...esac
- Fortran 77: последнее ключевое слово
END
+ начальное ключевое слово, например,IF
...ENDIF
,DO
...ENDDO
- Модула-2: одно и то же последнее ключевое слово
END
для всего - Visual Basic: каждая структура управления имеет собственное ключевое слово.
If
...End If
;For
...Next
;Do
...Loop
;While
...Wend
- Ada: последнее ключевое слово - это
Выбор
Если-то- (еще) утверждения
Условные выражения и условные конструкции - это особенности языка программирования, которые выполняют различные вычисления или действия в зависимости от того, оценивается ли заданное программистом логическое условие как истинное или ложное.
IF..GOTO
. Форма, найденная в неструктурированных языках, имитирующая типичную инструкцию машинного кода, перескакивает на (GOTO) метку или номер строки, когда выполняется условие.IF..THEN..(ENDIF)
. Вместо того, чтобы ограничиваться переходом, любой простой оператор или вложенный блок может следовать за ключевым ключевым словом THEN. Это структурированная форма.IF..THEN..ELSE..(ENDIF)
. То же, что и выше, но со вторым действием, которое должно быть выполнено, если условие ложно. Это одна из самых распространенных форм с множеством вариаций. Некоторым требуется терминалENDIF
, другим - нет. C и родственные языки не требуют ключевого слова терминала или слова «then», но требуют заключения условия в круглые скобки.- Условные операторы могут быть и часто вложены в другие условные операторы. Некоторые языки допускают
ELSE
иIF
могут быть объединеныELSEIF
, избегая необходимости иметь рядENDIF
или других заключительных операторов в конце составного оператора.
Паскаль : | Ада : | C : | Сценарий оболочки : | Python : | Лисп : |
---|---|---|---|---|---|
если > 0 , то WriteLn ( " да " ) еще WriteLn ( " нет " ) ; | если a > 0, то Put_Line ( "да" ); иначе Put_Line ( "нет" ); конец, если ; | если ( а > 0 ) { printf ( "да" ); } else { printf ( "нет" ); } | если [ $ a -gt 0 ] ; затем эхо "да" иначе эхо "нет" фи | если a > 0 : print ( «да» ) else : print ( «нет» ) | ( princ ( if ( plusp a ) «да» «нет» )) |
Менее распространенные варианты включают:
- Некоторые языки, такие как Фортран , имеют трехстороннее или арифметическое значение if , проверяющее, является ли числовое значение положительным, отрицательным или нулевым.
- Некоторые языки имеют функциональную форму
if
оператора, например, Лиспcond
. - Некоторые языки имеют оператор форму
if
заявления, такие как C в тройном операторе . - Perl дополняет стиль Си
if
с помощьюwhen
иunless
. - В Smalltalk для реализации условных выражений используются сообщения
ifTrue
иifFalse
сообщения, а не какие-либо фундаментальные языковые конструкции.
Операторы case и switch
Операторы переключения (или операторы case , или многосторонние ветки ) сравнивают заданное значение с указанными константами и предпринимают действия в соответствии с первой совпадающей константой. Обычно существует условие для действия по умолчанию («иначе», «иначе»), которое должно выполняться, если совпадение не увенчалось успехом. Операторы switch могут допускать оптимизацию компилятора, например таблицы поиска . В динамических языках регистры могут не ограничиваться константными выражениями и могут распространяться на сопоставление с образцом , как в примере сценария оболочки справа, где *)
реализует регистр по умолчанию как глобус, соответствующий любой строке. Логика Дела также может быть реализована в функциональной форме, как в SQL «s decode
заявлении.
Паскаль : | Ада : | C : | Сценарий оболочки : | Лисп : |
---|---|---|---|---|
case someChar of 'a' : actionOnA ; 'x' : действиеOnX ; 'y' , 'z' : actionOnYandZ ; иначе actionOnNoMatch ; конец ; | case someChar - это когда ' a ' => actionOnA ; когда ' x ' => actionOnX ; когда ' y ' | ' z ' => actionOnYandZ ; когда другие => actionOnNoMatch ; конец ; | переключатель ( someChar ) { case 'a' : actionOnA ; перерыв ; case 'x' : actionOnX ; перерыв ; case 'y' : case 'z' : actionOnYandZ ; перерыв ; по умолчанию : actionOnNoMatch ; } | case $ someChar в a ) actionOnA ;; x ) actionOnX ;; [ yz ]) actionOnYandZ ;; * ) actionOnNoMatch ;; esac | ( case some-char (( # \ a ) action-on-a ) (( # \ x ) action-on-x ) (( # \ y # \ z ) action-on-y-and-z ) ( else действие при отсутствии совпадения )) |
Петли
Цикл - это последовательность операторов, которая указывается один раз, но может выполняться несколько раз подряд. Код «внутри» цикла ( тело цикла, обозначенное ниже как xxx ) выполняется определенное количество раз, или один раз для каждого набора элементов, или до тех пор, пока не будет выполнено какое-либо условие, или бесконечно .
В языках функционального программирования , таких как Haskell и Scheme , циклы могут быть выражены с помощью рекурсии или итерации с фиксированной точкой, а не явных конструкций цикла. Хвостовая рекурсия - это частный случай рекурсии, который можно легко преобразовать в итерацию.
Петли с контролем счета
В большинстве языков программирования есть конструкции для повторения цикла определенное количество раз. В большинстве случаев счет может идти вниз, а не вверх, и можно использовать размер шага, отличный от 1.
ДЛЯ I = 1 К N | для I: = 1 до N действительно начать ххх | ххх СЛЕДУЮЩИЙ I | конец ;-------------------------------------------------- ---------- DO I = 1, N | for (I = 1; I <= N; ++ I) { ххх | ххх КОНЕЦ ДЕЛАТЬ | }
В этих примерах, если N <1, тело цикла может выполняться один раз (при I, имеющем значение 1) или не выполняться вообще, в зависимости от языка программирования.
Во многих языках программирования только целые числа могут надежно использоваться в цикле, управляемом счетчиком. Числа с плавающей запятой представлены неточно из-за аппаратных ограничений, поэтому цикл, такой как
для X: = 0,1 шаг от 0,1 до 1,0 сделать
может повторяться 9 или 10 раз, в зависимости от ошибок округления и / или оборудования и / или версии компилятора. Кроме того, если приращение X происходит путем повторного сложения, накопленные ошибки округления могут означать, что значение X в каждой итерации может довольно значительно отличаться от ожидаемой последовательности 0,1, 0,2, 0,3, ..., 1,0.
Циклы с контролем состояния
В большинстве языков программирования есть конструкции для повторения цикла до тех пор, пока не изменится какое-либо условие. Некоторые варианты проверяют условие в начале цикла; другие тестируют это в конце. Если тест в начале, тело может быть пропущено полностью; если он находится в конце, тело всегда выполняется хотя бы один раз.
ДЕЛАЙТЕ ПОКА (тест) | повторить ххх | ххх ПЕТЛЯ | до теста;---------------------------------------------- пока (тест) {| делать ххх | ххх } | пока (тест);
Перерыв управления представляет собой способ определения изменения значения используется в обычных петлях для обработки триггера для групп значений. Значения отслеживаются в цикле, и изменение перенаправляет поток программы на обработку связанного с ними группового события.
DO UNTIL (конец файла) ЕСЛИ новый-почтовый индекс <> текущий-почтовый индекс display_tally (текущий-почтовый индекс, почтовый индекс) текущий-почтовый индекс = новый-почтовый индекс zipcount = 0 ENDIF zipcount ++ ПЕТЛЯ
Петли, контролируемые сбором
Некоторые языки программирования (например, Ada , D , C ++ 11 , Smalltalk , PHP , Perl , Object Pascal , Java , C # , MATLAB , Visual Basic , Ruby , Python , JavaScript , Fortran 95 и более поздние версии) имеют специальные конструкции, которые позволяют неявно перебор всех элементов массива или всех членов набора или коллекции.
someCollection сделать : [: eachElement | xxx].
для пункта в коллекции действительно начинает ххй конец ; foreach (элемент; myCollection) {xxx} foreach someArray {xxx} foreach ($ someArray as $ k => $ v) {xxx} Коллекцияcoll; for (String s: coll) {} foreach ( строка s в myStringCollection) {xxx} someCollection | ForEach-Object {$ _}
forall (индекс = первый: последний: шаг ...)
В Scala есть выражения for , которые обобщают циклы, управляемые коллекциями, а также поддерживают другие применения, такие как асинхронное программирование . В Haskell есть do-выражения и понимания, которые вместе предоставляют функции, аналогичные функциям for в Scala.
Общая итерация
Общие конструкции итерации, такие как for
оператор C и форма Common Lisp , do
могут использоваться для выражения любого из вышеупомянутых видов циклов и других, таких как параллельный цикл по некоторому количеству коллекций. Там, где может использоваться более конкретная конструкция цикла, она обычно предпочтительнее общей конструкции итерации, поскольку она часто делает цель выражения более ясной.
Бесконечные петли
Бесконечные циклы используются для обеспечения того, чтобы сегмент программы зацикливался вечно или до тех пор, пока не возникнет исключительное условие, такое как ошибка. Например, программа, управляемая событиями (такая как сервер ), должна зацикливаться бесконечно, обрабатывая события по мере их возникновения, останавливаясь только тогда, когда процесс завершается оператором.
Бесконечные циклы могут быть реализованы с использованием других конструкций потока управления. Чаще всего в неструктурированном программировании это возврат назад (goto), тогда как в структурированном программировании это неопределенный цикл (цикл while), для которого задано никогда не заканчиваться, либо путем пропуска условия, либо путем явной установки его в значение true, как while (true) ...
. В некоторых языках есть специальные конструкции для бесконечных циклов, обычно путем исключения условия из неопределенного цикла. Примеры включают Ada ( loop ... end loop
), [4] Fortran ( DO ... END DO
), Go ( for { ... }
) и Ruby ( loop do ... end
).
Часто бесконечный цикл непреднамеренно создается из-за ошибки программирования в цикле, управляемом условием, при этом условие цикла использует переменные, которые никогда не изменяются внутри цикла.
Продолжение со следующей итерацией
Иногда внутри тела цикла возникает желание пропустить оставшуюся часть тела цикла и продолжить следующую итерацию цикла. В некоторых языках заявления , такие как continue
(большинство языков) skip
, [5] или next
(Perl и Ruby), который будет делать это. Результатом является преждевременное завершение самого внутреннего тела цикла, а затем возобновление в обычном режиме со следующей итерацией. Если итерация является последней в цикле, результатом будет досрочное завершение всего цикла.
Повторить текущую итерацию
В некоторых языках, таких как Perl [6] и Ruby, [7], есть redo
оператор, который перезапускает текущую итерацию с самого начала.
Цикл перезапуска
В Ruby есть retry
инструкция, которая перезапускает весь цикл с начальной итерации. [8]
Ранний выход из петель
При использовании цикла, управляемого счетчиком, для поиска в таблице может быть желательно прекратить поиск, как только будет найден требуемый элемент. Некоторые языки программирования предоставляют такие операторы, как break
(большинство языков), Exit
(Visual Basic) или last
(Perl), результатом которых является немедленное завершение текущего цикла и передача управления оператору сразу после этого цикла. Другой термин для циклов раннего выхода - « полторы петли» .
Следующий пример сделан на Ada, который поддерживает как ранний выход из циклов, так и циклы с тестом в середине . Обе функции очень похожи, и сравнение обоих фрагментов кода покажет разницу: ранний выход должен сочетаться с оператором if, в то время как условие в середине является автономной конструкцией.
с Ada.Text IO ; с Ada.Integer Text IO ;процедура Print_Squares - X : Integer ; begin Read_Data : цикл Ada . Целочисленный текст IO . Получить ( X ); выйти из Read_Data, когда X = 0 ; Ада . Текст IO . Положите ( X * X ); Ада . Текст IO . New_Line ; конец цикла Read_Data ; конец Print_Squares ;
Python поддерживает условное выполнение кода в зависимости от того, был ли цикл завершен раньше (с помощью break
оператора) или нет с помощью предложения else с циклом. Например,
for n в set_of_numbers : if isprime ( n ): print ( «Набор содержит простое число» ) break else : print ( «Набор не содержит простых чисел» )
Предложение else
в приведенном выше примере связано с for
оператором, а не с внутренним if
оператором. И Python, for
и while
циклы поддерживают такое предложение else, которое выполняется только в том случае, если не произошло раннего выхода из цикла.
Некоторые языки поддерживают выход из вложенных циклов; в теоретических кругах это называется многоуровневыми перерывами. Один из распространенных примеров использования - поиск в многомерной таблице. Это можно сделать либо с помощью многоуровневых разрывов (выход из N уровней), как в bash [9] и PHP, [10], либо с помощью помеченных разрывов (разрыв и продолжение с заданной меткой), как в Java и Perl. [11] Альтернативы многоуровневым разрывам включают одиночные разрывы вместе с переменной состояния, которая проверяется для выхода на другой уровень; исключения, которые вылавливаются на разбитом уровне; размещение вложенных циклов в функции и использование return для завершения всего вложенного цикла; или используя метку и инструкцию goto. C не включает многоуровневый разрыв, и обычная альтернатива - использовать goto для реализации помеченного разрыва. [12] Python не имеет многоуровневого прерывания или продолжения - это было предложено в PEP 3136 и отклонено на том основании, что дополнительная сложность не стоит редкого законного использования. [13]
Понятие многоуровневых разрывов представляет определенный интерес для теоретической информатики , поскольку оно порождает то, что сегодня называется иерархией Косараджу . [14] В 1973 году С. Рао Косараджу усовершенствовал теорему о структурированной программе , доказав, что можно избежать добавления дополнительных переменных в структурированном программировании, если разрешены многоуровневые разрывы произвольной глубины из циклов. [15] Кроме того, Косараджу доказал, что существует строгая иерархия программ: для каждого целого числа n существует программа, содержащая многоуровневый разрыв глубины n, которую нельзя переписать как программу с многоуровневыми разрывами глубины меньше n. без введения дополнительных переменных. [14]
Также можно return
выйти из подпрограммы, выполнив зацикленные операторы, выйдя как из вложенного цикла, так и из подпрограммы. Существуют и другие предлагаемые управляющие структуры для множественных разрывов, но они, как правило, реализуются как исключения.
В своем учебнике 2004 года Дэвид Ватт использует понятие секвенсора Теннента, чтобы объяснить сходство между многоуровневыми прерываниями и операторами возврата. Уотт отмечает, что класс секвенсоров, известных как управляющие секвенсоры , определяемый как «секвенсор, который завершает выполнение команды или процедуры, заключенной в текст», включает как разрывы из циклов (включая многоуровневые разрывы), так и операторы возврата. Однако, как обычно реализовано, секвенсоры возврата также могут нести (возвращаемое) значение, тогда как секвенсор прерывания, реализованный в современных языках, обычно не может. [16]
Варианты и инварианты петель
Петля варианты и инварианты петли используются , чтобы выразить правильность петель. [17]
На практике вариант цикла - это целочисленное выражение, имеющее начальное неотрицательное значение. Значение варианта должно уменьшаться во время каждой итерации цикла, но никогда не должно становиться отрицательным во время правильного выполнения цикла. Варианты цикла используются, чтобы гарантировать, что цикл завершится.
Инвариант цикла - это утверждение, которое должно быть истинным до первой итерации цикла и оставаться истинным после каждой итерации. Это означает, что при правильном завершении цикла выполняются как условие выхода, так и инвариант цикла. Инварианты цикла используются для отслеживания определенных свойств цикла во время последовательных итераций.
Некоторые языки программирования, такие как Eiffel, содержат встроенную поддержку вариантов и инвариантов цикла. В других случаях поддержка является надстройкой, например спецификацией языка моделирования Java для операторов цикла в Java .
Подъязык цикла
Некоторые диалекты Lisp предоставляют обширный подъязык для описания циклов. Ранний пример можно найти в Конверсионном Лиспе Interlisp . Common Lisp [18] предоставляет макрос Loop, который реализует такой подъязык.
Таблица перекрестных ссылок петлевой системы
Язык программирования | условный | петля | ранний выход | продолжение цикла | повторить | повторить попытку | средства корректности | ||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
начинать | середина | конец | считать | коллекция | Генеральная | бесконечный [1] | вариант | инвариантный | |||||
Ада | да | да | да | да | массивы | Нет | да | глубоко вложенный | Нет | ||||
APL | да | Нет | да | да | да | да | да | глубоко вложенный [3] | да | Нет | Нет | ||
C | да | Нет | да | Нет [2] | Нет | да | Нет | глубоко вложенный [3] | глубоко вложенный [3] | Нет | |||
C ++ | да | Нет | да | Нет [2] | Да [9] | да | Нет | глубоко вложенный [3] | глубоко вложенный [3] | Нет | |||
C # | да | Нет | да | Нет [2] | да | да | Нет | глубоко вложенный [3] | глубоко вложенный [3] | ||||
КОБОЛ | да | Нет | да | да | Нет | да | Нет | глубоко вложенный [15] | глубоко вложенный [14] | Нет | |||
Common Lisp | да | да | да | да | только встроенный [16] | да | да | глубоко вложенный | Нет | ||||
D | да | Нет | да | да | да | да | Да [14] | глубоко вложенный | глубоко вложенный | Нет | |||
Эйфелева | да | Нет | Нет | Да [10] | да | да | Нет | один уровень [10] | Нет | Нет | Нет [11] | только целое число [13] | да |
F # | да | Нет | Нет | да | да | Нет | Нет | Нет [6] | Нет | Нет | |||
FORTRAN 77 | да | Нет | Нет | да | Нет | Нет | Нет | один уровень | да | ||||
Фортран 90 | да | Нет | Нет | да | Нет | Нет | да | глубоко вложенный | да | ||||
Fortran 95 и более поздние версии | да | Нет | Нет | да | массивы | Нет | да | глубоко вложенный | да | ||||
Haskell | Нет | Нет | Нет | Нет | да | Нет | да | Нет [6] | Нет | Нет | |||
Ява | да | Нет | да | Нет [2] | да | да | Нет | глубоко вложенный | глубоко вложенный | Нет | неродной [12] | неродной [12] | |
JavaScript | да | Нет | да | Нет [2] | да | да | Нет | глубоко вложенный | глубоко вложенный | Нет | |||
Естественный | да | да | да | да | Нет | да | да | да | да | да | Нет | ||
OCaml | да | Нет | Нет | да | массивы, списки | Нет | Нет | Нет [6] | Нет | Нет | |||
PHP | да | Нет | да | Нет [2] [5] | Да [4] | да | Нет | глубоко вложенный | глубоко вложенный | Нет | |||
Perl | да | Нет | да | Нет [2] [5] | да | да | Нет | глубоко вложенный | глубоко вложенный | да | |||
Python | да | Нет | Нет | Нет [5] | да | Нет | Нет | глубоко вложенный [6] | глубоко вложенный [6] | Нет | |||
REBOL | Нет [7] | да | да | да | да | Нет [8] | да | один уровень [6] | Нет | Нет | |||
Рубин | да | Нет | да | да | да | Нет | да | глубоко вложенный [6] | глубоко вложенный [6] | да | да | ||
Стандартный ML | да | Нет | Нет | Нет | массивы, списки | Нет | Нет | Нет [6] | Нет | Нет | |||
Visual Basic .NET | да | Нет | да | да | да | Нет | да | один уровень на тип петли | один уровень на тип петли | ||||
PowerShell | да | Нет | да | Нет [2] | да | да | Нет | ? | да |
-
while (true)
Для этой цели a не считается бесконечным циклом, потому что это не специальная языковая структура. - a b c d e f g h ЦиклC- это общая конструкция цикла, а не конкретно счетная, хотя она часто используется для этого.
for (init; test; increment)
- a b c Глубокие разрывы могут быть выполнены в APL, C, C ++ и C # с помощью меток и gotos.
- Итерация над объектами быладобавленав PHP 5.
- a b c Цикл подсчета можно смоделировать, перебирая увеличивающийся список или генератор, например, Python
range()
. - a b c d e Глубокие перерывы могут быть выполнены с помощью обработки исключений.
- a Специальной конструкции нет, так как
while
для этого можно использовать функцию. - a Специальной конструкции нет, но пользователи могут определять общие функции цикла.
- a СтандартC ++ 11представилдля. ВSTLестьфункция-
std::for_each
шаблон,которая может выполнять итерацию поконтейнерамSTLи вызыватьунарную функциюдля каждого элемента. [19]Функциональность также может быть построена какмакросдля этих контейнеров. [20] - Count-контролируемое подключение петли осуществляется путем итераций через целочисленный интервал; досрочный выход с включением дополнительного условия выхода.
- Eiffel поддерживает зарезервированное слово
retry
, однако он используется вобработке исключений,не управление циклом. - a Требуетсяязыкспецификации интерфейса поведенияJava Modeling Language(JML).
- a Требует, чтобы варианты цикла были целыми числами; трансфинитные варианты не поддерживаются. [1]
- a D поддерживает бесконечные коллекции и возможность перебирать эти коллекции. Для этого не требуется особой конструкции.
- Глубокие разрывы могут быть достигнутыиспользовании
GO TO
и процедуры. - Common Lisp предшествует понятие общего типа коллекции.
Структурированный нелокальный поток управления
Многие языки программирования, особенно те, которые отдают предпочтение более динамичным стилям программирования, предлагают конструкции для нелокального потока управления . Это заставляет поток выполнения выскакивать из заданного контекста и возобновляться в некоторой заранее объявленной точке. Условия , исключения и продолжения - это три распространенных вида нелокальных управляющих конструкций; существуют и более экзотические, такие как генераторы , сопрограммы и ключевое слово async .
Условия
PL / I имеет около 22 стандартных условий (например, ZERODIVIDE SUBSCRIPTRANGE ENDFILE), которые могут быть вызваны и которые могут быть перехвачены: действием условия ON ; Программисты также могут определять и использовать свои собственные именованные условия.
Как и в случае неструктурированного if , можно указать только один оператор, поэтому во многих случаях требуется GOTO, чтобы решить, где следует возобновить поток управления.
К сожалению, некоторые реализации имели значительные накладные расходы как в пространстве, так и во времени (особенно SUBSCRIPTRANGE), поэтому многие программисты пытались избегать использования условий.
Общие примеры синтаксиса:
ON условие GOTO label
Исключения
Современные языки имеют специализированную структурированную конструкцию для обработки исключений, которая не полагается на использование GOTO
или (многоуровневых) разрывов или возвратов. Например, в C ++ можно написать:
try { xxx1 // Где-то здесь xxx2 // use: '' 'throw' '' someValue; xxx3 } улова ( SomeClass & someId ) { // задвижка значение SomeClass actionForSomeClass } задвижка ( SomeType & anotherId ) { // задвижка значение SomeType actionForSomeType } поймать (...) { // поймать ничего уже не поймала actionForAnythingElse }
catch
Выше можно использовать любое количество и разнообразие пунктов. Если catch
совпадения нет throw
, управление возвращается через вызовы подпрограмм и / или вложенные блоки, пока не catch
будет найдено совпадение или пока не будет достигнут конец основной программы, после чего программа принудительно останавливается с соответствующим сообщением об ошибке.
Благодаря влиянию C ++, catch
это ключевое слово зарезервировано для объявления обработчика исключений сопоставления с образцом в других популярных сегодня языках, таких как Java или C #. Некоторые другие языки, такие как Ada, используют ключевое слово exception
для введения обработчика исключений, а затем могут даже использовать другое ключевое слово ( when
в Ada) для сопоставления с образцом. Некоторые языки, такие как AppleScript, включают заполнители в синтаксис обработчика исключений для автоматического извлечения нескольких фрагментов информации при возникновении исключения. Этот подход проиллюстрирован ниже on error
конструкцией из AppleScript:
попробуйте установить myNumber на myNumber / 0 при ошибке e число n от f до t частичный результат pr if ( e = «Невозможно делить на ноль» ), затем отобразите диалоговое окно «Вы не должны этого делать» конец попытки
В учебнике Дэвида Ватта 2004 г. также анализируется обработка исключений в рамках секвенсоров (представленных в этой статье в разделе о ранних выходах из циклов). Ватт отмечает, что ненормальная ситуация, обычно иллюстрируемая арифметическим переполнением или ошибками ввода / вывода, например, файл не найден, является своего рода ошибкой, которая «обнаруживается в некотором программном модуле низкого уровня, но [для которой] обработчик более естественно расположен в программном блоке высокого уровня ". Например, программа может содержать несколько вызовов для чтения файлов, но действие, которое нужно выполнить, когда файл не найден, зависит от значения (цели) файла, о котором идет речь в программе, и, следовательно, процедура обработки этой ненормальной ситуации не может быть находится в системном коде нижнего уровня. Уоттс далее отмечает, что введение тестирования флагов состояния в вызывающей стороне, как это повлечет за собой структурированное программирование с одним выходом или даже (с несколькими выходами) секвенсоры возврата, приводит к ситуации, когда «код приложения имеет тенденцию быть загроможденным тестами флагов состояния» и что «программист может по забывчивости или лениво пропустить проверку флага состояния. Фактически, ненормальные ситуации, представленные флагами состояния, по умолчанию игнорируются!» Ватт отмечает, что в отличие от тестирования флагов состояния, исключения имеют противоположное поведение по умолчанию , вызывая завершение программы, если только программист явно не обработает исключение каким-либо образом, возможно, путем добавления явного кода для его игнорирования. Основываясь на этих аргументах, Ватт заключает, что секвенсоры перехода или управляющие секвенсоры не так подходят, как выделенный секвенсор исключений с семантикой, рассмотренной выше. [21]
В Object Pascal, D, Java, C # и Python к конструкции finally
можно добавить предложение try
. Независимо от того, как управление уходит, try
код внутри finally
предложения будет выполняться гарантированно. Это полезно при написании кода, который должен отказаться от дорогостоящего ресурса (такого как открытый файл или соединение с базой данных) после завершения обработки:
FileStream stm = null ; // Пример C # try { stm = new FileStream ( "logfile.txt" , FileMode . Create ); вернуть ProcessStuff ( stm ); // может вызвать исключение } finally { if ( stm ! = null ) stm . Закрыть (); }
Поскольку этот шаблон довольно распространен, C # имеет особый синтаксис:
используя ( var stm = new FileStream ( "logfile.txt" , FileMode . Create )) { return ProcessStuff ( stm ); // может вызвать исключение }
После выхода из using
-block компилятор гарантирует, что stm
объект освобожден, эффективно привязывая переменную к файловому потоку, абстрагируясь от побочных эффектов инициализации и освобождения файла. with
Оператор Python и аргумент блока Ruby File.open
используются с аналогичным эффектом.
Все упомянутые выше языки определяют стандартные исключения и обстоятельства, при которых они возникают. Пользователи могут создавать собственные исключения; на самом деле C ++ позволяет пользователям отбрасывать и ловить практически любой тип, включая базовые типы int
, в то время как другие языки, такие как Java, не столь разрешительны.
Продолжение
Асинхронный
В C # 5.0 появилось ключевое слово async для поддержки асинхронного ввода-вывода в «прямом стиле».
Генераторы
Генераторы , также известные как полупрограммы, позволяют временно передавать управление методу-потребителю, обычно используя yield
ключевое слово ( описание выхода ). Подобно ключевому слову async, он поддерживает программирование в «прямом стиле».
Сопрограммы
Сопрограммы - это функции, которые могут передавать друг другу управление - форма совместной многозадачности без потоков.
Сопрограммы могут быть реализованы в виде библиотеки, если язык программирования предоставляет либо продолжения, либо генераторы, поэтому различие между сопрограммами и генераторами на практике является технической деталью.
Перекрестная ссылка нелокального потока управления
Язык программирования | условия | исключения | генераторы / сопрограммы | асинхронный |
---|---|---|---|---|
Ада | Нет | да | ? | ? |
C | Нет | Нет | Нет | Нет |
C ++ | Нет | да | да, используя BOOST | ? |
C # | Нет | да | да | да |
КОБОЛ | да | да | Нет | Нет |
Common Lisp | да | Нет | ? | ? |
D | Нет | да | да | ? |
Эйфелева | Нет | да | ? | ? |
Erlang | Нет | да | да | ? |
F # | Нет | да | да | да |
Идти | Нет | да | да | ? |
Haskell | Нет | да | да | Нет |
Ява | Нет | да | Нет | Нет |
JavaScript | ? | да | Да, ES6 | Да, этап 3 |
Цель-C | Нет | да | Нет | ? |
PHP | Нет | да | да | ? |
PL / I | да | Нет | Нет | Нет |
Python | Нет | да | да | Да [22] |
REBOL | да | да | Нет | ? |
Рубин | Нет | да | да | ? |
Ржавчина | Нет | да | экспериментальный [23] [24] | Да [25] |
Scala | Нет | да | через экспериментальное расширение [26] | через экспериментальное расширение |
Tcl | по следам | да | да | через цикл событий |
Visual Basic .NET | да | да | Нет | ? |
PowerShell | Нет | да | Нет | ? |
Предлагаемые структуры управления
В подделке статьи Datamation [27] в 1973 году Р. Лоуренс Кларк предложил заменить оператор GOTO оператором COMEFROM и привел несколько интересных примеров. COMEFROM был реализован на одном эзотерическом языке программирования под названием INTERCAL .
В статье Дональда Кнута 1974 г. «Структурированное программирование с использованием операторов перехода» [28] определены две ситуации, которые не были охвачены перечисленными выше управляющими структурами, и приведены примеры управляющих структур, которые могли бы справиться с этими ситуациями. Несмотря на их полезность, эти конструкции еще не нашли своего применения в основных языках программирования.
Петля с тестом посередине
Следующее было предложено Далем в 1972 году: [29]
петля петля xxx1 чтение (симв.); во время теста; пока не atEndOfFile; xxx2 запись (символ); повторить ; повторить ;
Если xxx1 опущен, то мы получим петлю с тестом на вершине (традиционная в то время как петля). Если xxx2 опущено, мы получаем цикл с тестом внизу, что эквивалентно циклу do while на многих языках. Если опустить while , мы получим бесконечный цикл. Конструкцию здесь можно представить как цикл do с проверкой while посередине. Следовательно, эта единственная конструкция может заменить несколько конструкций в большинстве языков программирования.
Языки, в которых отсутствует эта конструкция, обычно эмулируют ее, используя эквивалентную идиому бесконечного цикла с прерыванием:
while (true) { xxx1 если ( не тест) сломаться xxx2}
Возможный вариант - разрешить более одного теста while ; внутри цикла, но использование exitwhen (см. следующий раздел), кажется, лучше охватывает этот случай.
В Ada указанная выше конструкция цикла ( цикл - while - repeat ) может быть представлена с использованием стандартного бесконечного цикла ( цикл - конец цикла ), который имеет предложение exit when посередине (не путать с оператором exitwhen в следующем разделе ).
с Ada.Text_IO ; с Ada.Integer_Text_IO ;процедура Print_Squares - X : Integer ; begin Read_Data : цикл Ada . Целочисленный_текст_IO . Получить ( X ); выйти из Read_Data, когда X = 0 ; Ада . Текст IO . Положите ( X * X ); Ада . Текст IO . New_Line ; конец цикла Read_Data ; конец Print_Squares ;
Присвоение имени циклу (например, Read_Data в этом примере) необязательно, но позволяет выйти из внешнего цикла нескольких вложенных циклов.
Множественный ранний выход / выход из вложенных циклов
Это было предложено Зан в 1974 году. [30] Здесь представлена модифицированная версия.
exitwhen EVENTA или событие В или EventC; ххх выходы СобытиеA: действиеA СобытиеB: действиеB EventC: действиеC эндексит ;
exitwhen используется для указания событий, которые могут произойти в пределах xxx , их появление указывается с помощью имени события в качестве оператора. Когда какое-то событие все же происходит, выполняется соответствующее действие, а затем управление переходит сразу после завершения . Эта конструкция обеспечивает очень четкое разделение между определением того, что определенная ситуация применима, и действиями, которые необходимо предпринять в этой ситуации.
exitwhen концептуально аналогичен обработке исключений , и исключения или аналогичные конструкции используются для этой цели во многих языках.
Следующий простой пример включает поиск определенного элемента в двумерной таблице.
выход при обнаружении или отсутствии; для I: = от 1 до N делать для J: = от 1 до M делать, если таблица [I, J] = target then found; отсутствующий; выходы найдено: print («элемент находится в таблице»); отсутствует: print («товар отсутствует в таблице»); эндексит ;
Безопасность
Один из способов атаковать часть программного обеспечения - перенаправить поток выполнения программы. Для защиты от этих атак используются различные методы обеспечения целостности потока управления , включая канарейки стека , защиту от переполнения буфера , теневые стеки и проверку указателя vtable . [31] [32] [33]
Смотрите также
- Отрасль (информатика)
- Анализ потока управления
- Диаграмма потока управления
- График потока управления
- Контрольный стол
- Сопрограмма
- Цикломатическая сложность
- Дракон-карта
- Схема
- ПЕРЕЙТИ К
- Джеру , помогает изучить управляющие структуры
- Основной цикл
- Рекурсия
- Планирование (вычисления)
- Код спагетти
- Структурированное программирование
- Подпрограмма
- Оператор переключения , условно изменяет поток управления
Рекомендации
- ^ Бём, Якопини. «Блок-схемы, машины Тьюринга и языки только с двумя правилами формирования» Comm. ACM , 9 (5): 366-371, май 1966 г.
- ^ a b Робертс, Э. [1995] « Выходы из цикла и структурированное программирование: возобновление дискуссии », Бюллетень ACM SIGCSE, (27) 1: 268–272.
- ^ Дэвид Энтони Уотт; Уильям Финдли (2004). Концепции проектирования языков программирования . Джон Вили и сыновья. п. 228. ISBN 978-0-470-85320-7.
- ^ Программирование на Аде: Управление: бесконечный цикл
- ^ «Что такое цикл и как мы можем их использовать?» . Проверено 25 мая 2020 .
- ^ "повторить - perldoc.perl.org" . perldoc.perl.org . Проверено 25 сентября 2020 .
- ^ «control_expressions - Документация для Ruby 2.4.0» . docs.ruby-lang.org . Проверено 25 сентября 2020 .
- ^ «control_expressions - Документация для Ruby 2.3.0» . docs.ruby-lang.org . Проверено 25 сентября 2020 .
- ^ Расширенное руководство по сценариям Bash: 11.3. Циклическое управление
- ^ Руководство по PHP: " перерыв "
- ^ perldoc: последний
- ^ comp.lang.c Список часто задаваемых вопросов · " Вопрос 20.20b "
- ^ [Python-3000] Анонс PEP 3136 , Гвидо ван Россум
- ^ а б Козен, Декстер (2008). «Теорема Бёма – Якопини неверна пропозиционально». Математика построения программ (PDF) . Конспект лекций по информатике. 5133 . С. 177–192. CiteSeerX 10.1.1.218.9241 . DOI : 10.1007 / 978-3-540-70594-9_11 . ISBN 978-3-540-70593-2.
- ^ Косараджу, С. Рао. «Анализ структурированных программ», Учеб. Пятый ежегодный сироп ACM. Теория вычислений (май 1973 г.), 240–252; также в J. Computer and System Sciences, 9, 3 (декабрь 1974 г.). цитируется Дональд Кнут (1974). «Структурированное программирование с переходом к операторам». Вычислительные обзоры . 6 (4): 261–301. CiteSeerX 10.1.1.103.6084 . DOI : 10.1145 / 356635.356640 . S2CID 207630080 .
- ^ Дэвид Энтони Уотт; Уильям Финдли (2004). Концепции проектирования языков программирования . Джон Вили и сыновья. С. 215–221. ISBN 978-0-470-85320-7.
- ^ Мейер, Бертран (1991). Эйфель: язык . Прентис Холл. С. 129–131.
- ^ "Макрос Common Lisp LOOP" .
- ^ for_each . Sgi.com. Проверено 9 ноября 2010.
- ^ Глава 1. Boost.Foreach . Boost-sandbox.sourceforge.net (19 декабря 2009 г.). Проверено 9 ноября 2010.
- ^ Дэвид Энтони Уотт; Уильям Финдли (2004). Концепции проектирования языков программирования . Джон Вили и сыновья. С. 221–222. ISBN 978-0-470-85320-7.
- ^ https://docs.python.org/3/library/asyncio.html
- ^ https://doc.rust-lang.org/beta/unstable-book/language-features/generators.html
- ^ https://docs.rs/corona/0.4.3/corona/
- ^ https://rust-lang.github.io/async-book/
- ^ http://storm-enroute.com/coroutines/
- ^ Мы не знаем, куда ПЕРЕЙТИ, если не знаем, ОТКУДА НАХОДИМСЯ. Это лингвистическое нововведение (обман) оправдывает все ожидания. Архивировано 16 июля 2018 г. в Wayback Machine Р. Лоуренсом Кларком * Из Datamation, декабрь 1973 г.
- ^ Кнут, Дональд Э. «Структурированное программирование спереходомк утверждениям» ACM Computing Surveys 6 (4): 261-301, декабрь 1974 г.
- ^ Даль, Дейкстра и Хоар, "Структурированное программирование" Academic Press, 1972.
- ^ Зан, Коннектикут «Управляющий оператор для естественного нисходящего структурированного программирования», представленный на симпозиуме по языкам программирования, Париж, 1974.
- ^ Плательщик, Матиас ; Кузнецов, Владимир. «О различиях свойств CFI, CPS и CPI» . nebelwelt.net . Проверено 1 июня 2016 .
- ^ «Обнаружение ошибок Adobe Flash приводит к новому методу смягчения атак» . Темное чтение . Проверено 1 июня 2016 .
- ^ Финал. «Финал, который будет представлен на Black Hat USA 2016» . www.prnewswire.com . Проверено 1 июня 2016 .
дальнейшее чтение
- Хоар, CAR «Разделение: алгоритм 63», «Быстрая сортировка: алгоритм 64» и «Найти: алгоритм 65». Comm. АСМ 4, 321-322, 1961.
Внешние ссылки
- СМИ, связанные с потоком управления на Викискладе?
- Перейти к заявлению, которое считается вредным
- Лингвистический вклад программирования без GOTO
- «Структурированное программирование с помощью операторов перехода» (PDF) . Архивировано из оригинального (PDF) 24 августа 2009 года. (2,88 МБ)
- «Руководство по IBM 704» (PDF) . (31,4 МБ)