Поток управления
Конструкции цикла |
---|
В информатике выполняются поток управления (или управления ) — это порядок, в котором отдельные операторы , инструкции или вызовы функций императивной оцениваются программы или поток . Акцент на явном потоке управления отличает императивный язык программирования от декларативного языка программирования .
В императивном языке программирования оператор потока управления — это оператор, в результате которого делается выбор, какому из двух или более путей следовать. В нестрогих функциональных языках существуют функции и языковые конструкции для достижения одного и того же результата, но их обычно не называют операторами потока управления.
Набор операторов, в свою очередь, обычно структурируется как блок , который помимо группировки также определяет лексическую область действия .
Прерывания и сигналы — это механизмы низкого уровня, которые могут изменять поток управления аналогично подпрограмме , но обычно возникают как ответ на какой-либо внешний стимул или событие (которое может происходить асинхронно ), а не как выполнение встроенной программы . заявление о потоке управления.
На уровне машинного языка или ассемблера инструкции потока управления обычно работают путем изменения счетчика программ . Для некоторых центральных процессоров (ЦП) единственными доступными инструкциями потока управления являются инструкции условного или безусловного перехода , также называемые переходами.
Категории
[ редактировать ]Виды операторов потока управления, поддерживаемые разными языками, различаются, но их можно классифицировать по их эффекту:
- Продолжение с другого оператора (безусловный переход или переход)
- Выполнение набора операторов только в том случае, если выполнено какое-то условие (выбор — т. е. условное ветвление ).
- Выполнение набора операторов ноль или более раз, пока не будет выполнено какое-либо условие (т. е. цикл — то же самое, что и условное ветвление )
- Выполнение набора удаленных операторов, после чего обычно возвращается поток управления ( подпрограммы , сопрограммы и продолжения )
- Остановка программы с предотвращением дальнейшего выполнения (безусловная остановка)
Примитивы
[ редактировать ]Этикетки
[ редактировать ]Метка — это явное имя или номер , присвоенный фиксированной позиции в исходном коде , на которое могут ссылаться операторы потока управления, встречающиеся в других местах исходного кода. Метка отмечает позицию в исходном коде и не имеет никакого другого эффекта.
Номера строк являются альтернативой именованной метке, используемой в некоторых языках (например, BASIC ). Это целые числа, помещаемые в начале каждой строки текста исходного кода. Языки, в которых они используются, часто накладывают ограничение, согласно которому номера строк должны увеличиваться в каждой следующей строке, но не могут требовать, чтобы они были последовательными. Например, в БЕЙСИКЕ:
10 LET X = 3
20 PRINT X
В других языках, таких как C и Ada , метка — это идентификатор , обычно появляющийся в начале строки, за которым сразу следует двоеточие. Например, в С:
Success: printf("The operation was successful.\n");
Язык АЛГОЛ 60 допускал использование в качестве меток как целых чисел, так и идентификаторов (оба связаны двоеточиями со следующим оператором), но лишь немногие другие АЛГОЛа варианты допускали целые числа. Ранние компиляторы Фортрана допускали использование в качестве меток только целых чисел. Начиная с Фортрана-90, также разрешены буквенно-цифровые метки.
Перейти
[ редактировать ]Оператор goto (сочетание английских слов go и to , произносимых соответственно) — это основная форма безусловной передачи управления.
Хотя ключевое слово может быть написано как прописными, так и строчными буквами в зависимости от языка, обычно оно записывается так:
goto label
Эффект оператора goto заключается в том, что следующим оператором будет выполнен оператор, появляющийся в указанной метке (или сразу после нее).
, считали операторы Goto вредными Многие ученые-компьютерщики, особенно Дейкстра .
Подпрограммы
[ редактировать ]Терминология подпрограмм различается; альтернативно они могут быть известны как подпрограммы, процедуры, функции (особенно, если они возвращают результаты) или методы (особенно, если они принадлежат классам или классам типов ).
подпрограммы использовались в основном В 1950-х годах компьютерная память была очень маленькой по нынешним стандартам, поэтому для уменьшения размера программы. Часть кода была написана один раз, а затем использовалась много раз в разных местах программы.
Сегодня подпрограммы чаще используются, чтобы сделать программу более структурированной, например, изолируя какой-то алгоритм или скрывая какой-то метод доступа к данным. Если над одной программой работают несколько программистов, подпрограммы представляют собой один из видов модульности , которая может помочь разделить работу.
Последовательность
[ редактировать ]В структурном программировании упорядоченная последовательность последовательных команд считается одной из основных структур управления, которая используется в качестве строительного блока программ наряду с итерацией, рекурсией и выбором.
Минимальный структурированный поток управления
[ редактировать ]В мае 1966 года Бём и Якопини опубликовали статью. [1] в сообщениях ACM , которые показали, что любая программа с goto может быть преобразована в форму без goto, включающую только выбор (IF THEN ELSE) и циклы (WHILE условие DO xxx), возможно, с дублированным кодом и/или добавлением логического значения. переменные (флаги true/false). Более поздние авторы показали, что выбор можно заменить циклами (и еще несколькими логическими переменными).
То, что такой минимализм возможен, не означает, что он обязательно желателен; в конце концов, компьютерам теоретически нужна только одна машинная инструкция (вычитание одного числа из другого и разветвление, если результат отрицательный), но практические компьютеры имеют десятки или даже сотни машинных инструкций.
Статья Бема и Якопини показала, что все программы могут быть свободны от операторов goto. Другое исследование показало, что структуры управления с одним входом и одним выходом гораздо легче понять, чем любую другую форму. [ нужна ссылка ] главным образом потому, что их можно использовать где угодно в качестве оператора, не нарушая поток управления. Другими словами, они были составными . (Более поздние разработки, такие как нестрогие языки программирования , а в последнее время и компонуемые программные транзакции , продолжили эту стратегию, сделав компоненты программ еще более свободно компонуемыми.)
Некоторые ученые придерживались пуристского подхода к результату Бема-Якопини и утверждали, что даже такие инструкции, как break
и return
из середины циклов является плохой практикой, поскольку они не нужны в доказательстве Бема – Якопини, и поэтому они выступали за то, чтобы все циклы имели одну точку выхода. Этот пуристский подход воплощен в языке Паскаль (разработанном в 1968–1969 годах), который до середины 1990-х годов был предпочтительным инструментом для обучения вводному программированию в академических кругах. [2] Прямое применение теоремы Бема-Якопини может привести к введению дополнительных локальных переменных в структурированную диаграмму, а также к некоторому дублированию кода . [3] Паскаль подвержен обеим этим проблемам, и согласно эмпирическим исследованиям, на которые ссылается Эрик С. Робертс , студентам-программистам было трудно сформулировать правильные решения в Паскале для нескольких простых задач, включая написание функции для поиска элемента в массиве. Исследование Генри Шапиро 1980 года, цитируемое Робертсом, показало, что при использовании только структур управления, предоставляемых Паскалем, правильное решение было дано только 20% испытуемых, в то время как ни один испытуемый не написал неправильный код для этой задачи, если ему было разрешено написать возврат из середина петли. [2]
Структуры контроля на практике
[ редактировать ]Большинство языков программирования со структурами управления имеют начальное ключевое слово, которое указывает тип используемой структуры управления. [ нужны разъяснения ] Затем языки расходятся во мнениях относительно того, имеют ли структуры управления последнее ключевое слово.
- Нет финального ключевого слова: ALGOL 60 , C , C++ , Go , Haskell , Java , Pascal , Perl , PHP , PL/I , Python , PowerShell . Таким языкам нужен какой-то способ группировки операторов:
- Алгол 60 и Паскаль:
begin
...end
- C, C++, Go, Java, Perl, PHP и PowerShell: фигурные скобки.
{
...}
- ПЛ/Я:
DO
...END
- Python: использует отступа уровень (см. Правило «вне игры» ).
- Haskell: можно использовать либо уровень отступа , либо фигурные скобки, и их можно свободно смешивать.
- Луа: использует
do
...end
- Алгол 60 и Паскаль:
- Последнее ключевое слово: Ada , APL , ALGOL 68 , Modula-2 , Fortran 77 , Mythryl , Visual Basic . Формы конечного ключевого слова различаются:
- Ада: последнее ключевое слово
end
+ пробел + начальное ключевое слово, например,if
...end if
,loop
...end loop
- APL: последнее ключевое слово
:End
опционально + начальное ключевое слово, например,:If
...:End
или:If
...:EndIf
,Select
...:End
или:Select
...:EndSelect
, однако при добавлении конечного условия конечное ключевое слово становится:Until
- АЛГОЛ 68, Мифрил: начальное ключевое слово написано наоборот, например:
if
...fi
,case
...esac
- Фортран 77: последнее ключевое слово
END
+ начальное ключевое слово, например,IF
...ENDIF
,DO
...ENDDO
- Модуль-2: то же последнее ключевое слово
END
для всего - Visual Basic: каждая структура управления имеет свое ключевое слово.
If
...End If
;For
...Next
;Do
...Loop
;While
...Wend
- Ада: последнее ключевое слово
Выбор
[ редактировать ]Операторы if-then-(else)
[ редактировать ]Условные выражения и условные конструкции — это особенности языка программирования заданное программистом логическое условие , которые выполняют различные вычисления или действия в зависимости от того, истинно или ложно .
IF..GOTO
. Форма, встречающаяся в неструктурированных языках, имитирующая типичную инструкцию машинного кода, будет переходить к (GOTO) метке или номеру строки, когда условие будет выполнено.IF..THEN..(ENDIF)
. Вместо того, чтобы ограничиваться переходом, любой простой оператор или вложенный блок может следовать за ключевым словом THEN. Это структурированная форма.IF..THEN..ELSE..(ENDIF)
. То же, что и выше, но со вторым действием, которое необходимо выполнить, если условие ложно. Это одна из самых распространенных форм, имеющая множество вариаций. Некоторым требуется терминалENDIF
, другие нет. C и родственные ему языки не требуют ключевого слова терминала или «то», но требуют круглых скобок вокруг условия.- Условные операторы могут быть и часто вложены в другие условные операторы. Некоторые языки позволяют
ELSE
иIF
быть объединены вELSEIF
, избегая необходимости иметь сериюENDIF
или другие заключительные утверждения в конце составного утверждения.
Паскаль : | Есть : |
---|---|
if a > 0 then
writeln("yes")
else
writeln("no");
|
if a > 0 then
Put_Line("yes");
else
Put_Line("no");
end if;
|
С : | Шелл-скрипт : |
if (a > 0) {
puts("yes");
}
else {
puts("no");
}
|
if [ $a -gt 0 ]; then
echo "yes"
else
echo "no"
fi
|
Питон : | Лисп : |
if a > 0:
print("yes")
else:
print("no")
|
(princ
(if (plusp a)
"yes"
"no"))
|
Менее распространенные варианты включают:
- В некоторых языках, таких как Fortran , есть трехсторонняя арифметика if , проверяющая, является ли числовое значение положительным, отрицательным или нулевым.
- В некоторых языках есть функциональная форма
if
оператор, например Лиспcond
. - В некоторых языках есть операторная форма
if
C. оператор, такой как тернарный оператор - Perl дополняет стиль C
if
сwhen
иunless
. - Смоллток использует
ifTrue
иifFalse
сообщения для реализации условных операторов, а не какой-либо фундаментальной языковой конструкции.
Операторы Case и Switch
[ редактировать ]Операторы переключения (или операторы case , или многосторонние ветвления ) сравнивают заданное значение с указанными константами и выполняют действия в соответствии с первой совпадающей константой. Обычно предусмотрено действие по умолчанию («иначе», «иначе»), которое должно быть выполнено, если совпадение не удалось. Операторы переключения могут обеспечивать оптимизацию компилятора, например таблицы поиска . В динамических языках случаи могут не ограничиваться константными выражениями и могут распространяться на сопоставление с образцом , как в примере сценария оболочки справа, где *)
реализует случай по умолчанию как glob , соответствующий любой строке. Логика регистра также может быть реализована в функциональной форме, как в SQL . decode
заявление.
Паскаль : | Есть : |
---|---|
case someChar of
'a': actionOnA;
'x': actionOnX;
'y','z':actionOnYandZ;
else actionOnNoMatch;
end;
|
case someChar is
when 'a' => actionOnA;
when 'x' => actionOnX;
when 'y' | 'z' => actionOnYandZ;
when others => actionOnNoMatch;
end;
|
С : | Шелл-скрипт : |
switch (someChar) {
case 'a': actionOnA; break;
case 'x': actionOnX; break;
case 'y':
case 'z': actionOnYandZ; break;
default: actionOnNoMatch;
}
|
case $someChar in
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 action-on-no-match))
|
select case (someChar)
case ('a')
actionOnA
case ('x')
actionOnX
case ('y','z')
actionOnYandZ
case default
actionOnNoMatch
end select
|
Петли
[ редактировать ]Цикл — это последовательность операторов, которая указывается один раз, но может выполняться несколько раз подряд. Код «внутри» цикла ( тело цикла, показанное ниже как xxx ) выполняется заданное количество раз, либо один раз для каждого набора элементов, либо до тех пор, пока не будет выполнено какое-либо условие, либо бесконечно . Если один из этих элементов сам по себе является циклом, он называется «вложенным циклом». [4] [5] [6]
В языках функционального программирования , таких как Haskell и Scheme , как рекурсивные , так и итеративные процессы выражаются с помощью процедур хвостовой рекурсии вместо циклических конструкций, которые являются синтаксическими.
Петли, управляемые счетом
[ редактировать ]В большинстве языков программирования есть конструкции для повторения цикла определенное количество раз. В большинстве случаев счет может идти вниз, а не вверх, и можно использовать размер шага, отличный от 1.
FOR I = 1 TO N xxx NEXT I |
for I := 1 to N do begin xxx end; |
DO I = 1,N xxx END DO |
for ( I=1; I<=N; ++I ) { xxx } |
В этих примерах, если N < 1, тело цикла может выполняться один раз (при этом I имеет значение 1) или не выполняться вообще, в зависимости от языка программирования.
Во многих языках программирования в циклах, управляемых счетом, можно надежно использовать только целые числа. Числа с плавающей запятой представляются неточно из-за аппаратных ограничений, поэтому такой цикл, как
for X := 0.1 step 0.1 to 1.0 do
может повторяться 9 или 10 раз, в зависимости от ошибок округления и/или аппаратного обеспечения и/или версии компилятора. Более того, если приращение X происходит путем повторного сложения, накопленные ошибки округления могут означать, что значение X на каждой итерации может весьма существенно отличаться от ожидаемой последовательности 0,1, 0,2, 0,3,..., 1,0.
Контуры, управляемые по состоянию
[ редактировать ]В большинстве языков программирования есть конструкции для повторения цикла до тех пор, пока не изменится какое-либо условие. Некоторые варианты проверяют условие в начале цикла; другие проверяют это в конце. Если тест стоит на старте, то тело можно вообще пропустить; если оно находится в конце, тело всегда выполняется хотя бы один раз.
DO WHILE (test) xxx LOOP |
repeat xxx until test; |
while (test) { xxx } |
do xxx while (test); |
Разрыв управления — это метод обнаружения изменения значения, используемый в обычных циклах для запуска обработки групп значений. Значения отслеживаются внутри цикла, и изменение переключает поток программы на обработку связанного с ними группового события.
DO UNTIL (End-of-File) IF new-zipcode <> current-zipcode display_tally(current-zipcode, zipcount) current-zipcode = new-zipcode zipcount = 0 ENDIF zipcount++ LOOP
Циклы, управляемые коллекцией
[ редактировать ]Некоторые языки программирования (например, Ada , D , C++11 , Smalltalk , PHP , Perl , Object Pascal , Java , C# , MATLAB , Visual Basic , Ruby , Python , JavaScript , Fortran 95 и более поздние версии) имеют специальные конструкции, которые позволяют неявно циклический просмотр всех элементов массива или всех членов набора или коллекции.
someCollection do: [:eachElement |xxx]. for Item in Collection do begin xxx end; foreach (item; myCollection) { xxx } foreach someArray { xxx } foreach ($someArray as $k => $v) { xxx } Collection<String> coll; for (String s : coll) {} foreach (string s in myStringCollection) { xxx } someCollection | ForEach-Object { $_ } forall ( index = first:last:step... )
В Scala есть выражения for , которые обобщают циклы, управляемые коллекциями, а также поддерживают другие применения, например асинхронное программирование . В Haskell есть do-выражения и comprehension, которые вместе выполняют функции, аналогичные for-выражениям в Scala.
Общая итерация
[ редактировать ]Общие конструкции итерации, такие как C for
оператор и Common Lisp do
form можно использовать для выражения любого из вышеперечисленных типов циклов, а также других, таких как параллельный цикл по некоторому количеству коллекций. Там, где можно использовать более конкретную конструкцию цикла, она обычно предпочтительнее общей конструкции итерации, поскольку она часто проясняет цель выражения.
Бесконечные циклы
[ редактировать ]Бесконечные циклы используются для обеспечения зацикливания сегмента программы навсегда или до тех пор, пока не возникнет исключительная ситуация, например ошибка. Например, программа, управляемая событиями (такая как сервер ), должна работать вечно, обрабатывая события по мере их возникновения и останавливаясь только тогда, когда процесс завершается оператором.
Бесконечные циклы могут быть реализованы с использованием других конструкций потока управления. Чаще всего в неструктурированном программировании это переход назад (goto), тогда как в структурированном программировании это бесконечный цикл (цикл while), который никогда не заканчивается, либо путем пропуска условия, либо явно устанавливая для него значение true, как while (true) ...
. В некоторых языках есть специальные конструкции для бесконечных циклов, обычно путем исключения условия из неопределенного цикла. Примеры включают Аду ( loop ... end loop
), [7] Фортран ( DO ... END DO
), Идти ( for { ... }
) и Руби ( loop do ... end
).
Часто бесконечный цикл непреднамеренно создается из-за ошибки программирования в цикле, управляемом по условию, где условие цикла использует переменные, которые никогда не изменяются внутри цикла.
Продолжение следующей итерации
[ редактировать ]Иногда внутри тела цикла возникает желание пропустить оставшуюся часть тела цикла и перейти к следующей итерации цикла. Некоторые языки предоставляют такое утверждение, как continue
(большинство языков), skip
, [8] cycle
(Фортран) или next
(Perl и Ruby), который это сделает. В результате выполнение самого внутреннего тела цикла преждевременно завершается, а затем возобновляется в обычном режиме со следующей итерации. Если итерация является последней в цикле, результатом будет досрочное завершение всего цикла.
Повторить текущую итерацию
[ редактировать ]Некоторые языки, например Perl [9] и Руби, [10] иметь redo
оператор, который перезапускает текущую итерацию с самого начала.
Перезапустить цикл
[ редактировать ]У Руби есть retry
оператор, который перезапускает весь цикл с начальной итерации. [11]
Ранний выход из циклов
[ редактировать ]При использовании цикла, управляемого счетчиком, для поиска по таблице может оказаться желательным остановить поиск, как только нужный элемент будет найден. Некоторые языки программирования предоставляют такое утверждение, как break
(большинство языков), Exit
(Visual Basic) или last
(Perl), результатом чего является немедленное завершение текущего цикла и передача управления оператору сразу после этого цикла. Другой термин для циклов с ранним выходом — «полтора цикла» .
Следующий пример выполнен на языке Ada , который поддерживает как ранний выход из циклов , так и циклы с тестом в середине . Обе функции очень похожи, и сравнение обоих фрагментов кода покажет разницу: ранний выход должен сочетаться с оператором if , а условие в середине является автономной конструкцией.
with Ada.Text IO;
with Ada.Integer Text IO;
procedure Print_Squares is
X : Integer;
begin
Read_Data : loop
Ada.Integer Text IO.Get(X);
exit Read_Data when X = 0;
Ada.Text IO.Put (X * X);
Ada.Text IO.New_Line;
end loop Read_Data;
end Print_Squares;
Python поддерживает условное выполнение кода в зависимости от того, был ли завершен цикл раньше (с break
оператор) или нет, используя предложение else в цикле. Например,
for n in set_of_numbers:
if isprime(n):
print("Set contains a prime number")
break
else:
print("Set did not contain any prime numbers")
The else
пункт в приведенном выше примере связан с for
утверждение, а не внутреннее if
заявление. Оба Python for
и while
циклы поддерживают такое предложение else, которое выполняется только в том случае, если не произошел досрочный выход из цикла.
Некоторые языки поддерживают выход из вложенных циклов; в теоретических кругах это называется многоуровневыми разрывами. Одним из распространенных примеров использования является поиск в многомерной таблице. Это можно сделать либо через многоуровневые разрывы (выход из N уровней), как в bash [12] и PHP, [13] или через помеченные разрывы (вырваться и продолжить с заданной метки), как в Go, Java и Perl. [14] Альтернативы многоуровневым разрывам включают одиночные разрывы вместе с переменной состояния, которая проверяется на выход на другой уровень; исключения, которые перехватываются на уровне, на который осуществляется прорыв; помещение вложенных циклов в функцию и использование возврата для завершения всего вложенного цикла; или используя метку и оператор перехода. C не включает многоуровневый разрыв, и обычной альтернативой является использование перехода для реализации помеченного разрыва. [15] В Python нет многоуровневого разрыва или продолжения — это было предложено в PEP 3136 и отклонено на том основании, что добавленная сложность не стоит редкого законного использования. [16]
Идея многоуровневых разрывов представляет определенный интерес в теоретической информатике , поскольку она порождает то, что сегодня называется иерархией Косараджу . [17] В 1973 году С. Рао Косараджу уточнил теорему о структурированной программе , доказав, что можно избежать добавления дополнительных переменных в структурном программировании, если разрешены многоуровневые выходы из циклов произвольной глубины. [18] Кроме того, Косараджу доказал, что существует строгая иерархия программ: для каждого целого числа n существует программа, содержащая многоуровневый разрыв глубины n , которую нельзя переписать как программу с многоуровневыми разрывами глубины меньше n без введения дополнительных переменные. [17]
Можно также return
из подпрограммы, выполняющей зацикленные операторы, выходя как из вложенного цикла, так и из подпрограммы. Существуют и другие предлагаемые структуры управления для множественных перерывов, но они обычно реализуются в виде исключений.
В своем учебнике 2004 года Дэвид Уотт Теннента использует понятие секвенсора , чтобы объяснить сходство между многоуровневыми разрывами и операторами возврата. Ватт отмечает, что класс секвенсоров, известный как escape-секвенсоры , определяемый как «секвенсор, который завершает выполнение текстовой команды или процедуры», включает в себя как выходы из циклов (включая многоуровневые разрывы), так и операторы возврата. Однако в обычном исполнении секвенсоры возврата также могут нести (возвратное) значение, тогда как секвенсор прерывания, реализованный в современных языках, обычно не может. [19]
Варианты цикла и инварианты
[ редактировать ]Варианты цикла и инварианты цикла используются для выражения правильности циклов. [20]
На практике вариант цикла представляет собой целочисленное выражение, имеющее начальное неотрицательное значение. Значение варианта должно уменьшаться во время каждой итерации цикла, но никогда не должно становиться отрицательным во время правильного выполнения цикла. Варианты цикла используются, чтобы гарантировать завершение цикла.
Инвариант цикла — это утверждение, которое должно быть истинным до первой итерации цикла и оставаться истинным после каждой итерации. Это означает, что при корректном завершении цикла выполняются как условие выхода, так и инвариант цикла. Инварианты цикла используются для мониторинга определенных свойств цикла во время последовательных итераций.
Некоторые языки программирования, такие как Eiffel, содержат встроенную поддержку вариантов и инвариантов циклов. В других случаях поддержка является дополнением, например, спецификацией языка моделирования Java для операторов цикла в Java .
Циклический подъязык
[ редактировать ]Некоторые диалекты Лиспа предоставляют расширенный подъязык для описания циклов. Ранний пример можно найти в Conversional Lisp of Interlisp . Общий Лисп [21] предоставляет макрос Loop, который реализует такой подъязык.
Таблица перекрестных ссылок системы контуров
[ редактировать ]Язык программирования | условный | петля | ранний выход | продолжение цикла | переделывать | повторить попытку | средства корректности | ||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
начинать | середина | конец | считать | коллекция | общий | бесконечный [1] | вариант | инвариант | |||||
Есть | Да | Да | Да | Да | массивы | Нет | Да | глубоко вложенный | Нет | ||||
АПЛ | Да | Нет | Да | Да | Да | Да | Да | глубоко вложенный [3] | Да | Нет | Нет | ||
С | Да | Нет | Да | Нет [2] | Нет | Да | Нет | глубоко вложенный [3] | глубоко вложенный [3] | Нет | |||
С++ | Да | Нет | Да | Нет [2] | Да [9] | Да | Нет | глубоко вложенный [3] | глубоко вложенный [3] | Нет | |||
С# | Да | Нет | Да | Нет [2] | Да | Да | Нет | глубоко вложенный [3] | глубоко вложенный [3] | ||||
КОБОЛ | Да | Нет | Да | Да | Нет | Да | Нет | глубоко вложенный [15] | глубоко вложенный [14] | Нет | |||
Общий Лисп | Да | Да | Да | Да | только встроенный [16] | Да | Да | глубоко вложенный | Нет | ||||
Д | Да | Нет | Да | Да | Да | Да | Да [14] | глубоко вложенный | глубоко вложенный | Нет | |||
Эйфелева | Да | Нет | Нет | Да [10] | Да | Да | Нет | один уровень [10] | Нет | Нет | Нет [11] | только целое число [13] | Да |
Ф# | Да | Нет | Нет | Да | Да | Нет | Нет | Нет [6] | Нет | Нет | |||
ФОРТРАН 77 | Да | Нет | Нет | Да | Нет | Нет | Нет | один уровень | Да | ||||
Фортран 90 | Да | Нет | Нет | Да | Нет | Нет | Да | глубоко вложенный | Да | ||||
Фортран 95 и более поздние версии | Да | Нет | Нет | Да | массивы | Нет | Да | глубоко вложенный | Да | ||||
Идти | Да | Нет | Нет | Да | только встроенный | Да | Да | глубоко вложенный | глубоко вложенный | Нет | |||
Хаскелл | Нет | Нет | Нет | Нет | Да | Нет | Да | Нет [6] | Нет | Нет | |||
Ява | Да | Нет | Да | Нет [2] | Да | Да | Нет | глубоко вложенный | глубоко вложенный | Нет | неродной [12] | неродной [12] | |
JavaScript | Да | Нет | Да | Нет [2] | Да | Да | Нет | глубоко вложенный | глубоко вложенный | Нет | |||
Естественный | Да | Да | Да | Да | Нет | Да | Да | Да | Да | Да | Нет | ||
OCaml | Да | Нет | Нет | Да | массивы, списки | Нет | Нет | Нет [6] | Нет | Нет | |||
PHP | Да | Нет | Да | Нет [2] [5] | Да [4] | Да | Нет | глубоко вложенный | глубоко вложенный | Нет | |||
Перл | Да | Нет | Да | Нет [2] [5] | Да | Да | Нет | глубоко вложенный | глубоко вложенный | Да | |||
Питон | Да | Нет | Нет | Нет [5] | Да | Нет | Нет | глубоко вложенный [6] | глубоко вложенный [6] | Нет | |||
Ребол | Нет [7] | Да | Да | Да | Да | Нет [8] | Да | один уровень [6] | Нет | Нет | |||
Руби | Да | Нет | Да | Да | Да | Нет | Да | глубоко вложенный [6] | глубоко вложенный [6] | Да | Да | ||
Стандартный ML | Да | Нет | Нет | Нет | массивы, списки | Нет | Нет | Нет [6] | Нет | Нет | |||
Визуальный Бейсик .NET | Да | Нет | Да | Да | Да | Нет | Да | один уровень на каждый тип цикла | один уровень на каждый тип цикла | ||||
PowerShell | Да | Нет | Да | Нет [2] | Да | Да | Нет | ? | Да |
- а
while (true)
для этой цели не считается бесконечным циклом, поскольку это не специальная языковая структура. - а б с д и ж г час С
for (init; test; increment)
Цикл — это общая конструкция цикла, а не конкретно счетная, хотя она часто используется для этого. - а б с Глубокие разрывы могут быть достигнуты в APL, C, C++ и C# с помощью меток и методов перехода.
- а Итерация по объектам была добавлена в PHP 5.
- а б с Цикл подсчета можно смоделировать, перебирая увеличивающийся список или генератор, например, Python
range()
. - а б с д и Глубокие разрывы могут быть достигнуты с помощью обработки исключений.
- а Специальной конструкции нет, так как
while
Для этого можно использовать функцию. - а Специальной конструкции нет, но пользователи могут определять общие функции цикла.
- а Стандарт C++11 ввел диапазон для . В STL есть
std::for_each
функция шаблона , которая может перебирать контейнеры STL и вызывать унарную функцию для каждого элемента. [22] Функциональность также может быть реализована в виде макроса в этих контейнерах. [23] - а Цикл, управляемый счетом, осуществляется путем итерации по целочисленному интервалу; досрочный выход путем включения дополнительного условия для выхода.
- а Эйфель поддерживает зарезервированное слово
retry
, однако он используется для обработки исключений , а не для управления циклом. - а Требуется Java Modeling Language (JML). язык спецификации поведенческого интерфейса
- а Требует, чтобы варианты цикла были целыми числами; трансфинитные варианты не поддерживаются. [1]
- а D поддерживает бесконечные коллекции и возможность перебора этих коллекций. Для этого не требуется какой-либо специальной конструкции.
- а Глубокие перерывы могут быть достигнуты с помощью
GO TO
и процедуры. - а Common Lisp появился раньше концепции универсального типа коллекции.
Структурированный нелокальный поток управления
[ редактировать ]Многие языки программирования, особенно те, которые предпочитают более динамичные стили программирования, предлагают конструкции для нелокального потока управления . Это приводит к тому, что поток выполнения выскакивает из заданного контекста и возобновляется в какой-то заранее объявленной точке. Условия , исключения и продолжения — это три распространенных типа нелокальных управляющих конструкций; существуют и более экзотические, такие как генераторы , сопрограммы и ключевое слово async .
Условия
[ редактировать ]PL/I имеет около 22 стандартных условий (например, ZERODIVIDE SUBSCRIPTRANGE ENDFILE), которые могут быть вызваны и перехвачены с помощью: условия действия ON; Программисты также могут определять и использовать свои собственные именованные условия.
Как и в случае с неструктурированным if , можно указать только один оператор, поэтому во многих случаях требуется GOTO, чтобы решить, где следует возобновить поток управления.
К сожалению, некоторые реализации имели значительные накладные расходы как по пространству, так и по времени (особенно SUBSCRIPTRANGE), поэтому многие программисты старались избегать использования условий.
Общие примеры синтаксиса:
ON condition GOTO label
Исключения
[ редактировать ]Современные языки имеют специальную структурированную конструкцию для обработки исключений, которая не требует использования GOTO
или (многоуровневый) разрыв или возврат. Например, в C++ можно написать:
try {
xxx1 // Somewhere in here
xxx2 // use: '''throw''' someValue;
xxx3
} catch (someClass& someId) { // catch value of someClass
actionForSomeClass
} catch (someType& anotherId) { // catch value of someType
actionForSomeType
} catch (...) { // catch anything not already caught
actionForAnythingElse
}
Любое количество и разнообразие catch
положения можно использовать выше. Если нет catch
соответствие конкретному throw
управление передается обратно через вызовы подпрограмм и/или вложенные блоки до тех пор, пока не будет найдено соответствующее catch
не найден или пока не будет достигнут конец основной программы, после чего программа принудительно останавливается с соответствующим сообщением об ошибке.
Благодаря влиянию C++, catch
— это ключевое слово, зарезервированное для объявления обработчика исключений сопоставления с образцом в других популярных сегодня языках, таких как Java или C#. Некоторые другие языки, такие как Ада, используют ключевое слово exception
чтобы ввести обработчик исключений, а затем даже использовать другое ключевое слово ( when
в Ada) для сопоставления с образцом. Некоторые языки, такие как AppleScript, включают заполнители в синтаксис обработчика исключений для автоматического извлечения нескольких фрагментов информации при возникновении исключения. Примером такого подхода ниже является on error
построить из AppleScript:
try
set myNumber to myNumber / 0
on error e number n from f to t partial result pr
if ( e = "Can't divide by zero" ) then display dialog "You must not do that"
end try
В учебнике Дэвида Уотта 2004 года также анализируется обработка исключений в рамках секвенсоров (представленная в этой статье в разделе о ранних выходах из циклов). Уотт отмечает, что ненормальная ситуация, обычно иллюстрируемая арифметическими переполнениями или ошибками ввода/вывода , например, файл не найден, является своего рода ошибкой, которая «обнаруживается в каком-то низкоуровневом программном модуле, но [для которого] более естественным образом расположен обработчик. в программном модуле высокого уровня». Например, программа может содержать несколько вызовов чтения файлов, но действие, которое необходимо выполнить, когда файл не найден, зависит от значения (назначения) рассматриваемого файла для программы, и, следовательно, процедура обработки этой ненормальной ситуации не может быть выполнена. находится в низкоуровневом системном коде. Уоттс далее отмечает, что введение тестирования флагов состояния в вызывающем объекте, как это может повлечь за собой структурное программирование с одним выходом или даже секвенсор возврата (с несколькими выходами), приводит к ситуации, когда «код приложения имеет тенденцию загромождаться проверками флагов состояния» и что «программист может по забывчивости или лениво не проверять флаг состояния. Фактически, нештатные ситуации, представленные флагами состояния, по умолчанию игнорируются!» Уотт отмечает, что в отличие от тестирования флагов состояния, исключения имеют противоположный эффект. поведение по умолчанию , вызывающее завершение работы программы, если только программа не обработает исключение каким-либо явным образом, возможно, добавив явный код для его игнорирования. Основываясь на этих аргументах, Уотт заключает, что секвенсоры перехода или escape-секвенсоры менее подходят в качестве специализированного секвенсора исключений с семантикой, описанной выше. [24]
В Object Pascal, D, Java, C# и Python finally
пункт можно добавить в try
построить. Независимо от того, как контроль покидает try
код внутри finally
условие гарантированно будет выполнено. Это полезно при написании кода, который должен освободить дорогостоящий ресурс (например, открытый файл или соединение с базой данных) после завершения обработки:
FileStream stm = null; // C# example
try
{
stm = new FileStream("logfile.txt", FileMode.Create);
return ProcessStuff(stm); // may throw an exception
}
finally
{
if (stm != null)
stm.Close();
}
Поскольку этот шаблон довольно распространен, C# имеет специальный синтаксис:
using (var stm = new FileStream("logfile.txt", FileMode.Create))
{
return ProcessStuff(stm); // may throw an exception
}
Покидая using
-block, компилятор гарантирует, что stm
объект освобождается, эффективно привязывая переменную к файловому потоку и абстрагируясь от побочных эффектов инициализации и освобождения файла. Python with
оператор и аргумент блока Ruby для File.open
используются для аналогичного эффекта.
Все упомянутые выше языки определяют стандартные исключения и обстоятельства, при которых они выдаются. Пользователи могут создавать собственные исключения; C++ позволяет пользователям генерировать и перехватывать практически любые типы, включая такие базовые типы, как int
, тогда как другие языки, такие как Java, менее либеральны.
Продолжение
[ редактировать ]Асинхронный
[ редактировать ]В C# 5.0 появилось ключевое слово async для поддержки асинхронного ввода-вывода в «прямом стиле».
Генераторы
[ редактировать ]Генераторы , также известные как полусопрограммы, позволяют временно передать управление потребительскому методу, обычно с помощью yield
ключевое слово ( описание доходности ). Как и ключевое слово async, оно поддерживает программирование в «прямом стиле».
Сопрограммы
[ редактировать ]Сопрограммы — это функции, которые могут передавать управление друг другу — форма совместной многозадачности без потоков.
Сопрограммы могут быть реализованы в виде библиотеки, если язык программирования предоставляет либо продолжения, либо генераторы, поэтому на практике различие между сопрограммами и генераторами является технической деталью.
Перекрестная ссылка на нелокальный поток управления
[ редактировать ]Язык программирования | условия | исключения | генераторы/сопрограммы | асинхронный |
---|---|---|---|---|
Есть | Нет | Да | ? | ? |
С | Нет | Нет | Нет | Нет |
С++ | Нет | Да | Да | ? |
С# | Нет | Да | Да | Да |
КОБОЛ | Да | Да | Нет | Нет |
Общий Лисп | Да | Нет | ? | ? |
Д | Нет | Да | Да | ? |
Эйфелева | Нет | Да | ? | ? |
Эрланг | Нет | Да | Да | ? |
Ф# | Нет | Да | Да | Да |
Идти | Нет | Да | Да | ? |
Хаскелл | Нет | Да | Да | Нет |
Ява | Нет | Да | Нет | Нет |
JavaScript | ? | Да | Да | Да |
Цель-C | Нет | Да | Нет | ? |
PHP | Нет | Да | Да | ? |
ПЛ/Я | Да | Нет | Нет | Нет |
Питон | Нет | Да | Да | Да [25] |
Ребол | Да | Да | Нет | ? |
Руби | Нет | Да | Да | через расширение [26] |
Ржавчина | Нет | Да | экспериментальный [27] [28] | Да [29] |
Скала | Нет | Да | через экспериментальное расширение [30] | через экспериментальное расширение |
Ткл | по следам | Да | Да | через цикл событий |
Визуальный Бейсик .NET | Да | Да | Нет | ? |
PowerShell | Нет | Да | Нет | ? |
Предлагаемые структуры контроля
[ редактировать ]В ложной Datamation статье [31] В 1973 году Р. Лоуренс Кларк предположил, что оператор GOTO можно заменить оператором COMEFROM , и привел несколько занимательных примеров. COMEFROM был реализован на одном эзотерическом языке программирования под названием INTERCAL .
Статья Дональда Кнута 1974 года «Структурное программирование с операторами перехода» [32] идентифицирует две ситуации, которые не были охвачены структурами управления, перечисленными выше, и приводит примеры структур управления, которые могут справиться с этими ситуациями. Несмотря на свою полезность, эти конструкции еще не нашли своего применения в основных языках программирования.
Петля с тестом посередине
[ редактировать ]Следующее было предложено Далем в 1972 году: [33]
loop loop xxx1 read(char); while test; while not atEndOfFile; xxx2 write(char); repeat; repeat;
Если xxx1 опущен, мы получим цикл с проверкой вверху (традиционный цикл while ). Если xxx2 опущен, мы получим цикл с проверкой внизу, что эквивалентно циклу do while во многих языках. Если while опущено, мы получим бесконечный цикл. Конструкцию здесь можно рассматривать как цикл do с проверкой while посередине. Следовательно, эта единственная конструкция может заменить несколько конструкций в большинстве языков программирования.
Языки, в которых отсутствует эта конструкция, обычно эмулируют ее, используя эквивалентную идиому бесконечного цикла с разрывом:
while (true) { xxx1 if (not test) break xxx2 }
Возможный вариант — разрешить более одного теста во время теста; внутри цикла, но использование exitwhen (см. следующий раздел), по-видимому, лучше описывает этот случай.
В Ada приведенная выше конструкция цикла ( loop- . while - repeat ) может быть представлена с помощью стандартного бесконечного цикла ( loop - end Loop ), который имеет предложение выхода When в середине (не путать с оператором выхода When в следующем разделе) ).
with Ada.Text_IO;
with Ada.Integer_Text_IO;
procedure Print_Squares is
X : Integer;
begin
Read_Data : loop
Ada.Integer_Text_IO.Get(X);
exit Read_Data when X = 0;
Ada.Text IO.Put (X * X);
Ada.Text IO.New_Line;
end loop Read_Data;
end Print_Squares;
Именование цикла (например, Read_Data в этом примере) не является обязательным, но позволяет оставить внешний цикл из нескольких вложенных циклов.
Множественный ранний выход/выход из вложенных циклов
[ редактировать ]Эта конструкция была предложена Заном в 1974 году. [34] Модифицированная версия представлена здесь.
exitwhen EventA or EventB or EventC; xxx exits EventA: actionA EventB: actionB EventC: actionC endexit;
exitwhen используется для указания событий, которые могут произойти в пределах xxx , их появление обозначается использованием названия события в качестве утверждения. Когда какое-то событие все-таки происходит, выполняется соответствующее действие, а затем управление передается сразу после endexit . Эта конструкция обеспечивает очень четкое разделение между определением того, что какая-то ситуация применима, и действием, которое необходимо предпринять в этой ситуации.
exitwhen концептуально похож на обработку исключений , и во многих языках для этой цели используются исключения или подобные конструкции.
Следующий простой пример включает поиск определенного элемента в двумерной таблице.
exitwhen found or missing; for I := 1 to N do for J := 1 to M do if table[I,J] = target then found; missing; exits found: print ("item is in table"); missing: print ("item is not in table"); endexit;
Безопасность
[ редактировать ]Одним из способов атаки на часть программного обеспечения является перенаправление потока выполнения программы. различные обеспечения целостности потока управления методы , в том числе канарейки стека , защита от переполнения буфера , теневые стеки и проверка указателя виртуальной таблицы . Для защиты от этих атак используются [35] [36] [37]
См. также
[ редактировать ]- Отрасль (информатика)
- Анализ потока управления
- Диаграмма потока управления
- Граф потока управления
- Таблица управления
- Сопрограмма
- Цикломатическая сложность
- Drakon-chart
- Блок-схема
- Перейти
- Джеру , помогает изучить структуры управления
- Основной цикл
- Рекурсия
- Планирование (вычисления)
- Хвосты спагетти
- Структурированное программирование
- Подпрограмма
- Оператор Switch условно изменяет поток управления.
- Конструкция Зана
Ссылки
[ редактировать ]- ^ Бём, Якопини. «Блок-схемы, машины Тьюринга и языки только с двумя правилами формирования» Комм. ACM , 9(5):366-371, май 1966 г.
- ^ Jump up to: а б Робертс, Э. [1995] « Выходы из цикла и структурированное программирование: возобновление дебатов, заархивировано 25 июля 2014 г. в Wayback Machine », Бюллетень ACM SIGCSE, (27)1: 268–272.
- ^ Дэвид Энтони Ватт; Уильям Финдли (2004). Концепции проектирования языков программирования . Джон Уайли и сыновья. п. 228. ИСБН 978-0-470-85320-7 .
- ^ «Вложенные циклы в C с примерами» . Гики для Гиков . 25 ноября 2019 г. Проверено 14 марта 2024 г.
- ^ «Вложенные циклы Python» . www.w3schools.com . Проверено 14 марта 2024 г.
- ^ Дин, Дженна (22 ноября 2019 г.). «Вложенные циклы» . Стартап . Проверено 14 марта 2024 г.
- ^ Программирование на языке Ada: Управление: бесконечный цикл
- ^ «Что такое циклы и как их использовать?» . Архивировано из оригинала 28 июля 2020 г. Проверено 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 , Гвидо ван Россум
- ^ Jump up to: а б Козен, Декстер (2008). «Теорема Бема – Якопини ложна с теоретической точки зрения». Математика построения программ (PDF) . Конспекты лекций по информатике. Том. 5133. стр. 177–192. CiteSeerX 10.1.1.218.9241 . дои : 10.1007/978-3-540-70594-9_11 . ISBN 978-3-540-70593-2 .
- ^ Косараджу, С. Рао. «Анализ структурированных программ», Учеб. Пятый ежегодный сироп ACM. Теория вычислений (май 1973 г.), 240–252; также в журнале «Компьютерные и системные науки», 9, 3 (декабрь 1974 г.). цитируется Кнут, Дональд (1974). «Структурное программирование с переходом к операторам». Вычислительные опросы . 6 (4): 261–301. CiteSeerX 10.1.1.103.6084 . дои : 10.1145/356635.356640 . S2CID 207630080 .
- ^ Дэвид Энтони Ватт; Уильям Финдли (2004). Концепции проектирования языков программирования . Джон Уайли и сыновья. стр. 215–221. ISBN 978-0-470-85320-7 .
- ^ Мейер, Бертран (1991). Эйфель: Язык . Прентис Холл. стр. 129–131.
- ^ «Общий макрос Lisp LOOP» .
- ^ for_each . Sgi.com. Проверено 9 ноября 2010 г.
- ^ Глава 1. Boost.Foreach. Архивировано 29 января 2010 г. в Wayback Machine . Boost-sandbox.sourceforge.net (19 декабря 2009 г.). Проверено 9 ноября 2010 г.
- ^ Дэвид Энтони Ватт; Уильям Финдли (2004). Концепции проектирования языков программирования . Джон Уайли и сыновья. стр. 221–222. ISBN 978-0-470-85320-7 .
- ^ «Asyncio: асинхронный ввод-вывод: документация Python 3.10.2» .
- ^ «Сокет/Асинхронный» . Гитхаб . 25 февраля 2022 г.
- ^ «Генераторы — книга о нестабильной ржавчине» .
- ^ «Корона-Ржавчина» .
- ^ «Начало работы — асинхронное программирование на Rust» .
- ^ «Джитси Встреча» . Storm-enroute.com . Проверено 7 сентября 2022 г.
- ^ Мы не знаем, куда ПЕРЕЙТИ, если не знаем, ОТКУДА мы ПРИШЛИ. Эта (пародия) лингвистическая инновация оправдывает все ожидания. Архивировано 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 приводит к созданию нового метода предотвращения атак» . Мрачное чтение . 10 ноября 2015 года . Проверено 1 июня 2016 г.
- ^ Финал. «Финал будет представлен на выставке Black Hat USA 2016» . www.prnewswire.com . Проверено 1 июня 2016 г.
Дальнейшее чтение
[ редактировать ]- Хоар, CAR «Разбиение: алгоритм 63», «Быстрая сортировка: алгоритм 64» и «Найти: алгоритм 65». Комм. ACM 4, 321–322, 1961.
Внешние ссылки
[ редактировать ]- СМИ, связанные с потоком управления, на Викискладе?
- Перейти к заявлению, которое считается вредным
- Лингвистический вклад программирования без GOTO
- «Структурное программирование с операторами перехода» (PDF) . Архивировано из оригинала (PDF) 24 августа 2009 г. (2,88 МБ)
- «Руководство по IBM 704» (PDF) . (31,4 МБ)