Jump to content

Поток управления

(Перенаправлено из вложенного цикла )

В информатике выполняются поток управления (или управления ) — это порядок, в котором отдельные операторы , инструкции или вызовы функций императивной оцениваются программы или поток . Акцент на явном потоке управления отличает императивный язык программирования от декларативного языка программирования .

В императивном языке программирования оператор потока управления — это оператор, в результате которого делается выбор, какому из двух или более путей следовать. В нестрогих функциональных языках существуют функции и языковые конструкции для достижения одного и того же результата, но их обычно не называют операторами потока управления.

Набор операторов, в свою очередь, обычно структурируется как блок , который помимо группировки также определяет лексическую область действия .

Прерывания и сигналы — это механизмы низкого уровня, которые могут изменять поток управления аналогично подпрограмме , но обычно возникают как ответ на какой-либо внешний стимул или событие (которое может происходить асинхронно ), а не как выполнение встроенной программы . заявление о потоке управления.

На уровне машинного языка или ассемблера инструкции потока управления обычно работают путем изменения счетчика программ . Для некоторых центральных процессоров (ЦП) единственными доступными инструкциями потока управления являются инструкции условного или безусловного перехода , также называемые переходами.

Категории

[ редактировать ]
Диаграмма состояний процесса поиска карт масс пептидных ионов.

Виды операторов потока управления, поддерживаемые разными языками, различаются, но их можно классифицировать по их эффекту:

  • Продолжение с другого оператора (безусловный переход или переход)
  • Выполнение набора операторов только в том случае, если выполнено какое-то условие (выбор — т. е. условное ветвление ).
  • Выполнение набора операторов ноль или более раз, пока не будет выполнено какое-либо условие (т. е. цикл — то же самое, что и условное ветвление )
  • Выполнение набора удаленных операторов, после чего обычно возвращается поток управления ( подпрограммы , сопрограммы и продолжения )
  • Остановка программы с предотвращением дальнейшего выполнения (безусловная остановка)

Примитивы

[ редактировать ]

Этикетки

[ редактировать ]

Метка — это явное имя или номер , присвоенный фиксированной позиции в исходном коде , на которое могут ссылаться операторы потока управления, встречающиеся в других местах исходного кода. Метка отмечает позицию в исходном коде и не имеет никакого другого эффекта.

Номера строк являются альтернативой именованной метке, используемой в некоторых языках (например, 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 . Таким языкам нужен какой-то способ группировки операторов:
  • Последнее ключевое слово: 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] Да Да Нет ? Да
  1. а while (true) для этой цели не считается бесконечным циклом, поскольку это не специальная языковая структура.
  2. а б с д и ж г час С for (init; test; increment) Цикл — это общая конструкция цикла, а не конкретно счетная, хотя она часто используется для этого.
  3. а б с Глубокие разрывы могут быть достигнуты в APL, C, C++ и C# с помощью меток и методов перехода.
  4. а Итерация по объектам была добавлена ​​в PHP 5.
  5. а б с Цикл подсчета можно смоделировать, перебирая увеличивающийся список или генератор, например, Python range().
  6. а б с д и Глубокие разрывы могут быть достигнуты с помощью обработки исключений.
  7. а Специальной конструкции нет, так как while Для этого можно использовать функцию.
  8. а Специальной конструкции нет, но пользователи могут определять общие функции цикла.
  9. а Стандарт C++11 ввел диапазон для . В STL есть std::for_each функция шаблона , которая может перебирать контейнеры STL и вызывать унарную функцию для каждого элемента. [22] Функциональность также может быть реализована в виде макроса в этих контейнерах. [23]
  10. а Цикл, управляемый счетом, осуществляется путем итерации по целочисленному интервалу; досрочный выход путем включения дополнительного условия для выхода.
  11. а Эйфель поддерживает зарезервированное слово retry, однако он используется для обработки исключений , а не для управления циклом.
  12. а Требуется Java Modeling Language (JML). язык спецификации поведенческого интерфейса
  13. а Требует, чтобы варианты цикла были целыми числами; трансфинитные варианты не поддерживаются. [1]
  14. а D поддерживает бесконечные коллекции и возможность перебора этих коллекций. Для этого не требуется какой-либо специальной конструкции.
  15. а Глубокие перерывы могут быть достигнуты с помощью GO TO и процедуры.
  16. а 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]

См. также

[ редактировать ]
  1. ^ Бём, Якопини. «Блок-схемы, машины Тьюринга и языки только с двумя правилами формирования» Комм. ACM , 9(5):366-371, май 1966 г.
  2. Перейти обратно: Перейти обратно: а б Робертс, Э. [1995] « Выходы из цикла и структурированное программирование: возобновление дебатов, заархивировано 25 июля 2014 г. в Wayback Machine », Бюллетень ACM SIGCSE, (27)1: 268–272.
  3. ^ Дэвид Энтони Ватт; Уильям Финдли (2004). Концепции проектирования языков программирования . Джон Уайли и сыновья. п. 228. ИСБН  978-0-470-85320-7 .
  4. ^ «Вложенные циклы в C с примерами» . Гики для Гиков . 25 ноября 2019 г. Проверено 14 марта 2024 г.
  5. ^ «Вложенные циклы Python» . www.w3schools.com . Проверено 14 марта 2024 г.
  6. ^ Дин, Дженна (22 ноября 2019 г.). «Вложенные циклы» . Стартап . Проверено 14 марта 2024 г.
  7. ^ Программирование на языке Ada: Управление: бесконечный цикл
  8. ^ «Что такое циклы и как их использовать?» . Архивировано из оригинала 28 июля 2020 г. Проверено 25 мая 2020 г.
  9. ^ «повторить — perldoc.perl.org» . perldoc.perl.org . Проверено 25 сентября 2020 г.
  10. ^ «control_expressions — Документация для Ruby 2.4.0» . docs.ruby-lang.org . Проверено 25 сентября 2020 г.
  11. ^ «control_expressions — Документация для Ruby 2.3.0» . docs.ruby-lang.org . Проверено 25 сентября 2020 г.
  12. ^ Расширенное руководство по написанию сценариев Bash: 11.3. Контурное управление
  13. ^ Руководство по PHP: « перерыв »
  14. ^ perldoc: последний
  15. ^ comp.lang.c Список часто задаваемых вопросов · « Вопрос 20.20b »
  16. ^ [Python-3000] Анонс PEP 3136 , Гвидо ван Россум
  17. Перейти обратно: Перейти обратно: а б Козен, Декстер (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 .
  18. ^ Косараджу, С. Рао. «Анализ структурированных программ», Учеб. Пятый ежегодный сироп ACM. Теория вычислений (май 1973 г.), 240–252; также в журнале «Компьютерные и системные науки», 9, 3 (декабрь 1974 г.). цитируется Кнут, Дональд (1974). «Структурное программирование с переходом к операторам». Вычислительные опросы . 6 (4): 261–301. CiteSeerX   10.1.1.103.6084 . дои : 10.1145/356635.356640 . S2CID   207630080 .
  19. ^ Дэвид Энтони Ватт; Уильям Финдли (2004). Концепции проектирования языков программирования . Джон Уайли и сыновья. стр. 215–221. ISBN  978-0-470-85320-7 .
  20. ^ Мейер, Бертран (1991). Эйфель: Язык . Прентис Холл. стр. 129–131.
  21. ^ «Общий макрос Lisp LOOP» .
  22. ^ for_each . Sgi.com. Проверено 9 ноября 2010 г.
  23. ^ Глава 1. Boost.Foreach. Архивировано 29 января 2010 г. в Wayback Machine . Boost-sandbox.sourceforge.net (19 декабря 2009 г.). Проверено 9 ноября 2010 г.
  24. ^ Дэвид Энтони Ватт; Уильям Финдли (2004). Концепции проектирования языков программирования . Джон Уайли и сыновья. стр. 221–222. ISBN  978-0-470-85320-7 .
  25. ^ «Asyncio: асинхронный ввод-вывод: документация Python 3.10.2» .
  26. ^ «Сокет/Асинхронный» . Гитхаб . 25 февраля 2022 г.
  27. ^ «Генераторы — книга о нестабильной ржавчине» .
  28. ^ «Корона-Ржавчина» .
  29. ^ «Начало работы — асинхронное программирование на Rust» .
  30. ^ «Джитси Встреча» . Storm-enroute.com . Проверено 7 сентября 2022 г.
  31. ^ Мы не знаем, куда ПЕРЕЙТИ, если не знаем, ОТКУДА мы ПРИШЛИ. Эта (пародия) лингвистическая инновация оправдывает все ожидания. Архивировано 16 июля 2018 г. в Wayback Machine Р. Лоуренсом Кларком * Из Datamation, декабрь 1973 г.
  32. ^ Кнут, Дональд Э. «Структурное программирование с переходом к операторам» ACM Computing Surveys 6 (4): 261-301, декабрь 1974 г.
  33. ^ Даль, Дейкстра и Хоар, «Структурированное программирование», Academic Press, 1972.
  34. ^ Зан, Коннектикут «Управляющий оператор для естественного структурированного нисходящего программирования», представленный на симпозиуме по языкам программирования, Париж, 1974.
  35. ^ Пайер, Матиас ; Кузнецов Владимир. «О различиях свойств CFI, CPS и CPI» . nebelwelt.net . Проверено 1 июня 2016 г.
  36. ^ «Обнаружение ошибок Adobe Flash приводит к созданию нового метода предотвращения атак» . Мрачное чтение . 10 ноября 2015 года . Проверено 1 июня 2016 г.
  37. ^ Финал. «Финал будет представлен на выставке Black Hat USA 2016» . www.prnewswire.com . Проверено 1 июня 2016 г.

Дальнейшее чтение

[ редактировать ]
  • Хоар, CAR «Разбиение: алгоритм 63», «Быстрая сортировка: алгоритм 64» и «Найти: алгоритм 65». Комм. ACM 4, 321–322, 1961.
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: 679bb3dca694caf2a163d87579fe0917__1721779860
URL1:https://arc.ask3.ru/arc/aa/67/17/679bb3dca694caf2a163d87579fe0917.html
Заголовок, (Title) документа по адресу, URL1:
Control flow - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)