Из Википедии, бесплатной энциклопедии
  (Перенаправлено из потока управления )
Перейти к навигации Перейти к поиску

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

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

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

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

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

Категории [ править ]

Блок- схема, показывающая поток управления.

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

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

Примитивы [ править ]

Ярлыки [ править ]

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

Номера строк являются альтернативой именованной метке, используемой в некоторых языках (например, BASIC ). Это целые числа, помещенные в начале каждой строки текста в исходном коде. Языки, в которых они используются, часто накладывают ограничение, согласно которому номера строк должны увеличиваться в значении в каждой следующей строке, но могут не требовать, чтобы они были последовательными. Например, в BASIC:

10 LET X = 3 20 ПЕЧАТЬ X      

В других языках, таких как C и Ada , метка - это идентификатор , обычно появляющийся в начале строки и сразу за которым следует двоеточие. Например, в C:

Успех :  printf ( «Операция прошла успешно. \ N » );

Язык АЛГОЛ 60 допускает как целые числа, так и идентификаторы в качестве меток (оба связаны двоеточием со следующим утверждением), но немногие, если вообще какие-либо другие варианты АЛГОЛА допускают целые числа. В начале Fortran компиляторы разрешается только целые числа в качестве меток. Начиная с Fortran-90, также разрешены буквенно-цифровые метки.

Перейти [ править ]

Оператор goto (комбинация английских слов go и to , произносимых соответствующим образом) является самой простой формой безусловной передачи управления.

Хотя ключевое слово может быть в верхнем или нижнем регистре в зависимости от языка, обычно оно записывается как:

  метка goto

Эффект оператора goto заключается в том, что следующий оператор, который будет выполнен, будет оператором, появляющимся на (или сразу после) указанной метке.

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

Подпрограммы [ править ]

Терминология для подпрограмм различается; в качестве альтернативы они могут называться подпрограммами, процедурами, функциями (особенно если они возвращают результаты) или методами (особенно если они принадлежат классам или классам типов ).

В 1950-х годах память компьютера была очень маленькой по нынешним стандартам, поэтому подпрограммы использовались в основном [ ссылка ] для уменьшения размера программы. Кусок кода был написан один раз, а затем многократно использовался в различных других местах программы.

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

Последовательность [ править ]

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

Минимальный структурированный поток управления [ править ]

В мае 1966 года Бём и Якопини опубликовали статью [1] в « Коммуникациях ACM», в которой было показано, что любую программу с goto s можно преобразовать в форму без goto, включающую только выбор (IF THEN ELSE) и циклы (WHILE condition DO xxx ), возможно, с дублированным кодом и / или добавлением логических переменных (флаги true / false). Позднее авторы показали, что выбор можно заменить циклами (и тем более логическими переменными).

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

Статья Бема и Якопини показала, что все программы могут быть свободными от goto. Другое исследование показало, что управляющие структуры с одним входом и одним выходом намного легче понять, чем любую другую форму, [ цитата необходима ] в основном потому, что их можно было использовать где угодно как утверждение, не нарушая потока управления. Другими словами, их можно было компоновать . (Более поздние разработки, такие как нестрогие языки программирования - и в последнее время составные программные транзакции - продолжили эту стратегию, сделав компоненты программ еще более свободно компонуемыми.)

Некоторые ученые придерживались пуристического подхода к результату Бема-Якопини и утверждали, что даже инструкции типа breakи returnиз середины циклов являются плохой практикой, поскольку они не нужны в доказательстве Бема-Якопини, и поэтому они выступали за то, чтобы все циклы имели единый точка выхода. Этот пуристический подход воплощен в языке Pascal (разработанный в 1968–1969), который до середины 1990-х годов был предпочтительным инструментом для обучения вводному программированию в академических кругах. [2] Прямое применение теоремы Бема-Якопини может привести к введению дополнительных локальных переменных в структурированную диаграмму, а также может привести к некоторому дублированию кода . [3]На Паскаль влияют обе эти проблемы, и, согласно эмпирическим исследованиям, процитированным Эриком С. Робертсом , студентам-программистам было трудно сформулировать правильные решения на Паскале для нескольких простых задач, включая написание функции для поиска элемента в массиве. Исследование 1980 года Генри Шапиро, цитируемое Робертсом, показало, что при использовании только управляющих структур, предоставленных Паскалем, правильное решение было дано только 20% испытуемых, в то время как ни один из испытуемых не написал неправильный код для этой проблемы, если бы разрешили написать ответ из середина петли. [2]

Структуры управления на практике [ править ]

Большинство языков программирования со структурами управления имеют начальное ключевое слово, которое указывает тип задействованной структуры управления. [ требуется пояснение ] Затем языки разделяются на предмет наличия или отсутствия у управляющих структур последнего ключевого слова.

  • Нет последнего ключевого слова: ALGOL 60 , C , C ++ , Haskell , Java , Pascal , Perl , PHP , PL / I , Python , PowerShell . Таким языкам нужен способ группировки операторов вместе:
    • АЛГОЛ 60 и Паскаль: begin...end
    • C, C ++, Java, Perl, PHP и PowerShell: фигурные скобки { ...}
    • PL / I: DO...END
    • Python: использует уровень отступа (см. Автономное правило )
    • Haskell: можно использовать либо уровень отступа, либо фигурные скобки, и их можно свободно смешивать
    • Lua: использует do...end
  • Последнее ключевое слово: Ада , АЛГОЛ 68 , Модула-2 , Фортран 77 , Мифрил , Visual Basic . Формы последнего ключевого слова различаются:
    • Ada: последнее ключевое слово - это end+ пробел + начальное ключевое слово, например if... end if, loop...end loop
    • АЛГОЛ 68, Мифрил: начальное ключевое слово написано в обратном порядке, например, if... fi, case...esac
    • Fortran 77: последнее ключевое слово END+ начальное ключевое слово, например, IF... ENDIF, DO...ENDDO
    • Modula-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 и родственные языки не требуют ключевого слова терминала или 'then', но требуют скобок вокруг условия.
  • Условные операторы могут быть и часто вложены в другие условные операторы. Некоторые языки допускают ELSEи IFмогут быть объединены ELSEIF, избегая необходимости иметь ряд ENDIFили других заключительных операторов в конце составного оператора.

Менее распространенные варианты включают:

  • Некоторые языки, такие как Фортран , имеют трехстороннее или арифметическое значение if , проверяющее, является ли числовое значение положительным, отрицательным или нулевым.
  • Некоторые языки имеют функциональную форму ifвыражения, например, Лисп cond .
  • Некоторые языки имеют оператор форму ifзаявления, такие как C в тройном операторе .
  • Perl дополняет стиль C ifс помощью whenи unless.
  • Smalltalk использует ifTrueи ifFalseсообщения для реализации условных выражений, а не какие-либо фундаментальные языковые конструкции.

Операторы case и switch [ править ]

Операторы переключения (или операторы case , или многосторонние ветки ) сравнивают заданное значение с указанными константами и предпринимают действия в соответствии с первой совпадающей константой. Обычно существует положение о том, что действие по умолчанию («иначе», «иначе») должно выполняться, если совпадение не было успешным. Операторы Switch могут допускать оптимизацию компилятора, например таблицы поиска . В динамических языках регистры могут не ограничиваться константными выражениями и могут расширяться до сопоставления с образцом , как в примере сценария оболочки справа, где *)реализует регистр по умолчанию как глобус, соответствующий любой строке. Логика случая также может быть реализована в функциональной форме, как вSQL «s decodeзаявление.

Петли [ править ]

Цикл - это последовательность операторов, которая указывается один раз, но может выполняться несколько раз подряд. Код «внутри» цикла ( тело цикла, показанное ниже как xxx ) выполняется определенное количество раз, или один раз для каждого из набора элементов, или до тех пор, пока не будет выполнено какое-либо условие, или бесконечно .

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

Петли с контролем счета [ править ]

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

 ДЛЯ I = 1 К N | для I: = 1 до N действительно  начать ххх | ххх СЛЕДУЮЩИЙ I | конец ;-------------------------------------------------- ---------- DO I = 1, N | for (I = 1; I <= N; ++ I) { ххх | ххх КОНЕЦ ДЕЛАТЬ | }

В этих примерах, если N <1, тело цикла может выполняться один раз (при этом I имеет значение 1) или не выполняться вообще, в зависимости от языка программирования.

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

 для X: = 0,1 шаг от 0,1 до 1,0 сделать

может повторяться 9 или 10 раз, в зависимости от ошибок округления и / или оборудования и / или версии компилятора. Кроме того, если приращение X происходит путем повторного сложения, накопленные ошибки округления могут означать, что значение X в каждой итерации может значительно отличаться от ожидаемой последовательности 0,1, 0,2, 0,3, ..., 1,0.

Циклы с контролем состояния [ править ]

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

 ДЕЛАЙТЕ ПОКА (тест) | повторение  ххх | ххх LOOP | до теста;---------------------------------------------- пока (тест) {| делать ххх | ххх } | пока (тест);

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

 DO UNTIL (конец файла) ЕСЛИ новый-почтовый индекс <> текущий-почтовый индекс display_tally (текущий-почтовый индекс, почтовый индекс)  текущий почтовый индекс = новый почтовый индекс zipcount = 0 ENDIF  zipcount ++ ПЕТЛЯ

Циклы, управляемые коллекцией [ править ]

Некоторые языки программирования (например, Ada , D , C ++ 11 , Smalltalk , PHP , Perl , Object Pascal , Java , C # , MATLAB , Visual Basic , Ruby , Python , JavaScript , Fortran 95 и более поздние версии) имеют специальные конструкции, которые позволяют неявно цикл по всем элементам массива или всем членам набора или коллекции.

 someCollection сделать : [: eachElement | xxx].
 для пункта в коллекции действительно  начинает ххй конец ; foreach (элемент; myCollection) {xxx} foreach someArray {xxx} foreach ($ someArray as $ k => $ v) {xxx} Коллекция <String> coll; for (String s: coll) {} foreach ( строка s в myStringCollection) {xxx} someCollection | ForEach-Object {$ _}
 forall (индекс = первый: последний: шаг ...)

В Scala есть выражения for , которые обобщают циклы, управляемые коллекциями, а также поддерживают другие применения, такие как асинхронное программирование . В Haskell есть do-выражения и понимания, которые вместе предоставляют функции, аналогичные функциям for в Scala.

Общая итерация [ править ]

Общие конструкции итерации, такие как forоператор C и форма Common Lisp , doмогут использоваться для выражения любого из вышеупомянутых видов циклов и других, таких как параллельный цикл по некоторому количеству коллекций. Там, где может использоваться более конкретная конструкция цикла, она обычно предпочтительнее общей конструкции итерации, поскольку она часто делает цель выражения более ясной.

Бесконечные циклы [ править ]

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

Бесконечные циклы могут быть реализованы с использованием других конструкций потока управления. Чаще всего в неструктурированном программировании это возврат назад (goto), тогда как в структурированном программировании это неопределенный цикл (цикл while), для которого задано никогда не заканчиваться, либо путем исключения условия, либо путем явной установки его в значение true, as while (true) .... В некоторых языках есть специальные конструкции для бесконечных циклов, обычно путем исключения условия из неопределенного цикла. Примеры включают Ada ( loop ... end loop), [4] Fortran ( DO ... END DO), Go ( for { ... }) и Ruby ( loop do ... end).

Часто бесконечный цикл непреднамеренно создается из-за ошибки программирования в цикле, управляемом условием, при этом условие цикла использует переменные, которые никогда не изменяются в цикле.

Продолжение со следующей итерацией [ править ]

Иногда внутри тела цикла возникает желание пропустить оставшуюся часть тела цикла и продолжить следующую итерацию цикла. В некоторых языках заявления , такие как continue(большинство языков) skip, [5] или next(Perl и Ruby), который будет делать это. Результатом является преждевременное завершение самого внутреннего тела цикла, а затем возобновление в обычном режиме со следующей итерацией. Если итерация является последней в цикле, результатом будет досрочное завершение всего цикла.

Повторить текущую итерацию [ править ]

Некоторые языки, такие как Perl [6] и Ruby, [7], имеют redoоператор, который перезапускает текущую итерацию с самого начала.

Цикл перезапуска [ править ]

В Ruby есть retryинструкция, которая перезапускает весь цикл с начальной итерации. [8]

Ранний выход из петель [ править ]

При использовании цикла, управляемого счетчиком, для поиска в таблице может быть желательно прекратить поиск, как только будет найден требуемый элемент. Некоторые языки программирования предоставляют такие операторы, как break(большинство языков), Exit(Visual Basic) или last(Perl), результатом которых является немедленное завершение текущего цикла и передача управления оператору сразу после этого цикла. Еще один термин для циклов раннего выхода - « полторы петли» .

Следующий пример выполнен на Ada, который поддерживает как ранний выход из циклов, так и циклы с тестом в середине . Обе функции очень похожи, и сравнение обоих фрагментов кода покажет разницу: ранний выход должен сочетаться с оператором if, в то время как условие в середине является автономной конструкцией.

с  Ada.Text  IO ; с  Ada.Integer  Text  IO ;процедура  Print_Squares  -  X  :  Integer ; begin  Read_Data  :  цикл  Ada . Целочисленный  текстовый  ввод-вывод . Получить ( X );  выйти из  Read_Data,  когда  X  =  0 ;  Ада . Текст  IO . Положите  ( X  *  X );  Ада . Текст  IO . New_Line ;  конец  цикла  Read_Data ; конец  Print_Squares;

Python поддерживает условное выполнение кода в зависимости от того, был ли цикл завершен раньше (с помощью breakоператора) или нет с помощью предложения else с циклом. Например,

for  n  в  set_of_numbers :  if  isprime ( n ):  print ( «Набор содержит простое число» )  break else :  print ( «Набор не содержит простых чисел» )

Предложение elseв приведенном выше примере связано с forоператором, а не с внутренним ifоператором. И Python, forи whileциклы поддерживают такое предложение else, которое выполняется только в том случае, если не произошло раннего выхода из цикла.

Некоторые языки поддерживают выход из вложенных циклов; в теоретических кругах это называется многоуровневыми перерывами. Один из распространенных примеров использования - поиск в многомерной таблице. Это можно сделать либо с помощью многоуровневых разрывов (выход из N уровней), как в bash [9] и PHP, [10], либо с помощью помеченных разрывов (разрыв и продолжение с заданной меткой), как в Java и Perl. [11]Альтернативы многоуровневым разрывам включают одиночные разрывы вместе с переменной состояния, которая проверяется для выхода на другой уровень; исключения, которые вылавливаются на разбитом уровне; размещение вложенных циклов в функции и использование return для завершения всего вложенного цикла; или используя метку и инструкцию goto. C не включает многоуровневый разрыв, и обычная альтернатива - использовать goto для реализации помеченного разрыва. [12] Python не имеет многоуровневого прерывания или продолжения - это было предложено в PEP 3136 и отклонено на том основании, что дополнительная сложность не стоит редкого законного использования. [13]

Понятие многоуровневых разрывов представляет определенный интерес для теоретической информатики , поскольку оно порождает то, что сегодня называется иерархией Косараджу . [14] В 1973 году С. Рао Косараджу усовершенствовал теорему о структурированной программе , доказав, что можно избежать добавления дополнительных переменных в структурированном программировании, если разрешены многоуровневые разрывы произвольной глубины из циклов. [15] Кроме того, Косараджу доказал, что существует строгая иерархия программ: для каждого целого числа n существует программа, содержащая многоуровневый разрыв глубины n, которую нельзя переписать как программу с многоуровневыми разрывами глубины меньше, чемn без добавления дополнительных переменных. [14]

Также можно returnвыйти из подпрограммы, выполнив зацикленные операторы, выйдя как из вложенного цикла, так и из подпрограммы. Существуют и другие предлагаемые управляющие структуры для множественных перерывов, но они обычно реализуются как исключения.

В своем учебнике 2004 года Дэвид Ватт использует понятие секвенсора Теннента, чтобы объяснить сходство между многоуровневыми прерываниями и операторами возврата. Уотт отмечает, что класс секвенсоров, известных как управляющие секвенсоры , определяемый как «секвенсор, который завершает выполнение команды или процедуры, содержащей текст», включает как разрывы из циклов (включая многоуровневые разрывы), так и операторы возврата. Однако, как обычно реализовано, секвенсоры возврата также могут нести (возвращаемое) значение, тогда как секвенсор прерывания, реализованный в современных языках, обычно не может. [16]

Варианты и инварианты петель [ править ]

Петля варианты и инварианты петли используются , чтобы выразить правильность петель. [17]

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

Инвариант цикла - это утверждение, которое должно быть истинным до первой итерации цикла и оставаться истинным после каждой итерации. Это означает, что при правильном завершении цикла выполняются как условие выхода, так и инвариант цикла. Инварианты цикла используются для отслеживания определенных свойств цикла во время последовательных итераций.

Некоторые языки программирования, такие как Eiffel, содержат встроенную поддержку вариантов и инвариантов цикла. В других случаях поддержка является надстройкой, например спецификацией языка моделирования Java для операторов цикла в Java .

Подъязык цикла [ править ]

Некоторые диалекты Lisp предоставляют обширный подъязык для описания циклов. Ранний пример можно найти в Conversional Lisp или Interlisp . Common Lisp [18] предоставляет макрос Loop, который реализует такой подъязык.

Таблица перекрестных ссылок петлевой системы [ править ]

  1. while (true)Для этой цели a не считается бесконечным циклом, потому что это не специальная языковая структура.
  2. a b c d e f g h Цикл C- это общая конструкция цикла, а не подсчетная, хотя она часто используется для этого.for (init; test; increment)
  3. a b c Глубокие разрывы могут быть выполнены в APL, C, C ++ и C # с помощью меток и gotos.
  4. Итерация над объектами быладобавленав PHP 5.
  5. a b c Цикл подсчета можно смоделировать, перебирая увеличивающийся список или генератор, например, Pythonrange().
  6. a b c d e Глубокие перерывы могут быть выполнены с помощью обработки исключений.
  7. a Специальной конструкции нет, так какwhileдля этого можно использовать функцию.
  8. a Специальной конструкции нет, но пользователи могут определять общие функции цикла.
  9. a СтандартC ++ 11представилдля. ВSTLестьstd::for_each шаблоннаяфункция, которая может выполнять итерацию поконтейнерамSTLи вызыватьунарную функциюдля каждого элемента. [19]Функциональность также может быть построена какмакросдля этих контейнеров. [20]
  10. Count-контролируемое подключение петли осуществляется путем итераций через целочисленный интервал; досрочный выход с включением дополнительного условия выхода.
  11. Eiffel поддерживает зарезервированное словоretry, однако он используется вобработке исключений,не управление циклом.
  12. a Требуетсяязыкспецификации интерфейса поведенияJava Modeling Language(JML).
  13. a Требует, чтобы варианты цикла были целыми числами; трансфинитные варианты не поддерживаются. [1]
  14. a D поддерживает бесконечные коллекции и возможность перебирать эти коллекции. Для этого не требуется особой конструкции.
  15. Глубокие разрывы могут быть достигнутыиспользованииGO TOи процедуры.
  16. Common Lisp предшествует понятие общего типа коллекции.

Структурированный нелокальный поток управления [ править ]

Многие языки программирования, особенно те, которые предпочитают более динамичные стили программирования, предлагают конструкции для нелокального потока управления . Это заставляет поток выполнения выскакивать из данного контекста и возобновляться в некоторой заранее объявленной точке. Условия , исключения и продолжения - три распространенных вида нелокальных управляющих конструкций; существуют и более экзотические, такие как генераторы , сопрограммы и ключевое слово async .

Условия [ править ]

PL / I имеет около 22 стандартных условий (например, ZERODIVIDE SUBSCRIPTRANGE ENDFILE), которые могут быть вызваны и которые могут быть перехвачены: действием условия ON ; Программисты также могут определять и использовать свои собственные именованные условия.

Подобно неструктурированному if , можно указать только один оператор, поэтому во многих случаях GOTO необходим, чтобы решить, где следует возобновить поток управления.

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

Общие примеры синтаксиса:

 ON  условие  GOTO  label

Исключения [ править ]

Современные языки имеют специализированную структурированную конструкцию для обработки исключений, которая не полагается на использование GOTOили (многоуровневых) разрывов или возвратов. Например, в C ++ можно написать:

try  {  xxx1  // Где-то здесь  xxx2  // use: '' 'throw' '' someValue;  xxx3 }  улова  ( SomeClass &  someId )  {  // задвижка значение SomeClass  actionForSomeClass  }  задвижка  ( SomeType &  anotherId )  {  // задвижка значение SomeType  actionForSomeType }  поймать  (...)  {  // поймать ничего уже не поймала  actionForAnythingElse }

catchВыше можно использовать любое количество и разнообразие пунктов. Если catchсовпадения нет throw, управление возвращается через вызовы подпрограмм и / или вложенные блоки, пока не catchбудет найдено совпадение или пока не будет достигнут конец основной программы, после чего программа принудительно останавливается с соответствующим сообщением об ошибке.

Благодаря влиянию C ++, catchэто ключевое слово зарезервировано для объявления обработчика исключений сопоставления с образцом в других популярных сегодня языках, таких как Java или C #. Некоторые другие языки, такие как Ada, используют ключевое слово exceptionдля введения обработчика исключений, а затем могут даже использовать другое ключевое слово ( whenв Ada) для сопоставления с образцом. Некоторые языки, такие как AppleScript, включают заполнители в синтаксис обработчика исключений, чтобы автоматически извлекать несколько фрагментов информации при возникновении исключения. Этот подход проиллюстрирован ниже on errorконструкцией из AppleScript:

попробуйте  установить  myNumber  на  myNumber  /  0 при  ошибке  e  число  n  от  f  до  t  частичный  результат  pr  if  (  e  =  «Невозможно делить на ноль»  ),  затем  отобразите диалоговое окно  «Вы не должны этого делать» конец  попытки

В учебнике Дэвида Ватта 2004 г. также анализируется обработка исключений в рамках секвенсоров (представленных в этой статье в разделе о ранних выходах из циклов). Ватт отмечает, что ненормальная ситуация, обычно представленная арифметическим переполнением или вводом / выводомсбои типа «файл не найден» - это своего рода ошибка, которая «обнаруживается в некотором программном модуле нижнего уровня, но [для которых] обработчик более естественно расположен в программном модуле высокого уровня». Например, программа может содержать несколько вызовов для чтения файлов, но действие, которое нужно выполнить, когда файл не найден, зависит от значения (цели) файла, о котором идет речь, для программы, и, следовательно, процедура обработки этой ненормальной ситуации не может быть находится в системном коде нижнего уровня. Уоттс далее отмечает, что введение тестирования флагов состояния в вызывающей программе, как это повлечет за собой структурированное программирование с одним выходом или даже секвенсоры возврата (с несколькими выходами), приводит к ситуации, когда «код приложения имеет тенденцию быть загроможденным тестами флагов состояния» и что "программист может забыть или лениво пропустить проверку флага состояния. Фактически,ненормальные ситуации, представленные флагами состояния, по умолчанию игнорируются! »Ватт отмечает, что в отличие от тестирования флагов состояния, исключения имеют противоположноеповедение по умолчанию , в результате чего программа завершается, если программист явно не обрабатывает исключение каким-либо образом, возможно, путем добавления явного кода для его игнорирования. Основываясь на этих аргументах, Ватт приходит к выводу, что секвенсоры перехода или управляющие секвенсоры не так подходят, как выделенный секвенсор исключений с семантикой, описанной выше. [21]

В Object Pascal, D, Java, C # и Python к конструкции finallyможно добавить предложение try. Независимо от того, как контроль оставляет tryкод внутри finallyпредложения, он гарантированно выполняется. Это полезно при написании кода, который должен отказаться от дорогостоящего ресурса (такого как открытый файл или соединение с базой данных) по завершении обработки:

FileStream  stm  =  null ;  // Пример C # try  {  stm  =  new  FileStream  ( "logfile.txt" ,  FileMode . Create );  вернуть  ProcessStuff ( stm );  // может вызвать исключение }  finally  {  if  ( stm  ! =  null )  stm . Закрыть (); }

Поскольку этот шаблон довольно распространен, C # имеет особый синтаксис:

используя  ( var  stm  =  new  FileStream ( "logfile.txt" ,  FileMode . Create )) {  return  ProcessStuff ( stm  );  // может вызвать исключение }

После выхода из using-block компилятор гарантирует, что stmобъект освобожден, эффективно привязывая переменную к файловому потоку, абстрагируясь от побочных эффектов инициализации и освобождения файла. withОператор Python и аргумент блока Ruby File.openиспользуются с аналогичным эффектом.

Все упомянутые выше языки определяют стандартные исключения и обстоятельства, при которых они возникают. Пользователи могут создавать собственные исключения; на самом деле C ++ позволяет пользователям отбрасывать и ловить практически любой тип, включая базовые типы int, в то время как другие языки, такие как Java, не столь разрешительны.

Продолжение [ править ]

Асинхронный [ править ]

В C # 5.0 введено ключевое слово async для поддержки асинхронного ввода-вывода в «прямом стиле».

Генераторы [ править ]

Генераторы , также известные как полупрограммы, позволяют временно передавать управление методу потребителя, обычно используя yieldключевое слово ( описание выхода ). Подобно ключевому слову async, он поддерживает программирование в «прямом стиле».

Сопрограммы [ править ]

Сопрограммы - это функции, которые могут передавать друг другу управление - форма совместной многозадачности без потоков.

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

Перекрестная ссылка нелокального потока управления [ править ]

Предлагаемые структуры управления [ править ]

В подделке статьи Datamation [27] в 1973 году Р. Лоуренс Кларк предложил заменить оператор GOTO оператором COMEFROM и привел несколько занимательных примеров. COMEFROM был реализован на одном эзотерическом языке программирования под названием INTERCAL .

В статье Дональда Кнута 1974 г. «Структурированное программирование с использованием операторов перехода» [28] определены две ситуации, которые не были охвачены перечисленными выше управляющими структурами, и приведены примеры управляющих структур, которые могли бы справиться с этими ситуациями. Несмотря на их полезность, эти конструкции еще не нашли своего применения в основных языках программирования.

Цикл с тестом посередине [ править ]

Следующее было предложено Далем в 1972 году: [29]

 петля  петля xxx1 чтение (симв.); во время теста; пока  не atEndOfFile; xxx2 запись (символ); повторить ; повторить ;

Если xxx1 опущен, то мы получим петлю с тестом на вершине (традиционная в то время как петля). Если xxx2 опущено, мы получаем цикл с тестом внизу, эквивалентный циклу do while на многих языках. Если опустить while , мы получим бесконечный цикл. Конструкцию здесь можно рассматривать как цикл do с проверкой while в середине. Следовательно, эта единственная конструкция может заменить несколько конструкций в большинстве языков программирования.

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

while (true) { xxx1 если ( не тест) сломаться xxx2}

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

В Ada указанная выше конструкция цикла ( цикл - while - repeat ) может быть представлена ​​с использованием стандартного бесконечного цикла ( цикл - конец цикла ), который имеет в середине предложение exit when (не путать с оператором exitwhen в следующем разделе ).

с  Ada.Text_IO ; с  Ada.Integer_Text_IO ;процедура  Print_Squares  -  X  :  Integer ; begin  Read_Data  :  цикл  Ada . Целочисленный_текст_IO . Получить ( X );  выйти из  Read_Data,  когда  X  =  0 ;  Ада . Текст  IO . Положите  ( X  *  X );  Ада . Текст  IO . New_Line ;  конец  цикла  Read_Data ; конец  Print_Squares ;

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

Множественный ранний выход / выход из вложенных циклов [ править ]

Это было предложено Зан в 1974 году. [30] Здесь представлена ​​модифицированная версия.

 exitwhen EVENTA или событие В или EventC; ххх выходы EventA: действиеA СобытиеB: действиеB EventC: действиеC эндексит ;

exitwhen используется для указания событий, которые могут произойти в пределах xxx , их появление указывается с помощью имени события в качестве оператора. Когда какое-то событие все же происходит, выполняется соответствующее действие, а затем управление переходит сразу после завершения . Эта конструкция обеспечивает очень четкое разделение между определением применимости некоторой ситуации и действиями, которые необходимо предпринять для этой ситуации.

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

Следующий простой пример включает поиск определенного элемента в двумерной таблице.

 выйти при обнаружении или отсутствии; для I: = от 1 до N делать  для J: = от 1 до M делать,  если таблица [I, J] = target then found; отсутствующий; выходы найдено: print («элемент в таблице»); отсутствует: print («товар отсутствует в таблице»); эндексит ;

Безопасность [ править ]

Один из способов атаковать часть программного обеспечения - перенаправить поток выполнения программы. Для защиты от этих атак используются различные методы обеспечения целостности потока управления , включая канарейки стека , защиту от переполнения буфера , теневые стеки и проверку указателя vtable . [31] [32] [33]

См. Также [ править ]

  • Отрасль (информатика)
  • Анализ потока управления
  • Схема потока управления
  • График потока управления
  • Контрольный стол
  • Coroutine
  • Цикломатическая сложность
  • Дракон-карта
  • Схема
  • ИДТИ К
  • Джеру , помогает изучить управляющие структуры
  • Основной цикл
  • Рекурсия
  • Планирование (вычисление)
  • Код спагетти
  • Структурированное программирование
  • Подпрограмма
  • Оператор переключения , условно изменяет поток управления

Ссылки [ править ]

  1. ^ Бём, Якопини. «Блок-схемы, машины Тьюринга и языки только с двумя правилами формирования» Comm. ACM , 9 (5): 366-371, май 1966 г.
  2. ^ a b Робертс Э. [1995] « Выходы из цикла и структурированное программирование: возобновление дискуссии », Бюллетень ACM SIGCSE, (27) 1: 268–272.
  3. ^ Дэвид Энтони Уотт; Уильям Финдли (2004). Концепции проектирования языков программирования . Джон Вили и сыновья. п. 228. ISBN 978-0-470-85320-7.
  4. ^ Программирование на Аде: Управление: бесконечный цикл
  5. ^ "Что такое цикл и как мы можем их использовать?" . Проверено 25 мая 2020 .
  6. ^ "повторить - perldoc.perl.org" . perldoc.perl.org . Проверено 25 сентября 2020 .
  7. ^ "control_expressions - Документация для Ruby 2.4.0" . docs.ruby-lang.org . Проверено 25 сентября 2020 .
  8. ^ "control_expressions - Документация для Ruby 2.3.0" . docs.ruby-lang.org . Проверено 25 сентября 2020 .
  9. ^ Расширенное руководство по сценариям Bash: 11.3. Контроль цикла
  10. ^ Руководство по PHP: " перерыв "
  11. ^ perldoc: последний
  12. ^ comp.lang.c Список часто задаваемых вопросов · " Вопрос 20.20b "
  13. ^ [Python-3000] Анонс PEP 3136 , Гвидо ван Россум
  14. ^ a b Козен, Декстер (2008). «Теорема Бёма – Якопини неверна пропозиционально». Математика построения программ (PDF) . Конспект лекций по информатике. 5133 . С. 177–192. CiteSeerX 10.1.1.218.9241 . DOI : 10.1007 / 978-3-540-70594-9_11 . ISBN   978-3-540-70593-2.
  15. ^ Косараджу, С. Рао. «Анализ структурированных программ», Тр. Пятый ежегодный сироп ACM. Theory of Computing, (май 1973), 240-252; также в J. Computer and System Sciences, 9, 3 (декабрь 1974 г.). цитируется Дональдом Кнутом (1974). «Структурированное программирование с переходом к операторам». Вычислительные обзоры . 6 (4): 261–301. CiteSeerX 10.1.1.103.6084 . DOI : 10.1145 / 356635.356640 . S2CID 207630080 .  
  16. ^ Дэвид Энтони Уотт; Уильям Финдли (2004). Концепции проектирования языков программирования . Джон Вили и сыновья. С. 215–221. ISBN 978-0-470-85320-7.
  17. ^ Мейер, Бертран (1991). Эйфель: язык . Прентис Холл. С. 129–131.
  18. ^ "Макрос Common Lisp LOOP" .
  19. ^ for_each . Sgi.com. Проверено 9 ноября 2010.
  20. ^ Глава 1. Boost.Foreach . Boost-sandbox.sourceforge.net (19 декабря 2009 г.). Проверено 9 ноября 2010.
  21. ^ Дэвид Энтони Уотт; Уильям Финдли (2004). Концепции проектирования языков программирования . Джон Вили и сыновья. С. 221–222. ISBN 978-0-470-85320-7.
  22. ^ https://docs.python.org/3/library/asyncio.html
  23. ^ https://doc.rust-lang.org/beta/unstable-book/language-features/generators.html
  24. ^ https://docs.rs/corona/0.4.3/corona/
  25. ^ https://rust-lang.github.io/async-book/
  26. ^ http://storm-enroute.com/coroutines/
  27. ^ Мы не знаем, куда ПЕРЕЙТИ, если мы не знаем, ОТКУДА НАХОДИМСЯ. Это лингвистическое нововведение (обман) оправдывает все ожидания. Архивировано 16 июля 2018 г. в Wayback Machine Р. Лоуренсом Кларком * Из Datamation, декабрь 1973 г.
  28. ^ Кнут, Дональд Э. «Структурированное программирование спереходомк утверждениям» ACM Computing Surveys 6 (4): 261-301, декабрь 1974 г.
  29. ^ Даль, Дейкстра и Хоар, "Структурированное программирование" Academic Press, 1972.
  30. ^ Зан, Коннектикут «Управляющий оператор для естественного нисходящего структурированного программирования», представленный на симпозиуме по языкам программирования, Париж, 1974.
  31. ^ Плательщик, Матиас ; Кузнецов, Владимир. «О различиях свойств CFI, CPS и CPI» . nebelwelt.net . Проверено 1 июня 2016 .
  32. ^ «Обнаружение ошибок Adobe Flash приводит к новому методу смягчения атак» . Темное чтение . Проверено 1 июня 2016 .
  33. ^ Финал. «Финал, который будет представлен на Black Hat USA 2016» . www.prnewswire.com . Проверено 1 июня 2016 .

Дальнейшее чтение [ править ]

  • Хоар, CAR «Разделение: алгоритм 63», «Быстрая сортировка: алгоритм 64» и «Найти: алгоритм 65». Comm. АСМ 4, 321-322, 1961.

Внешние ссылки [ править ]

  • СМИ, связанные с потоком управления на Викискладе?
  • Перейти к заявлению, которое считается вредным
  • Лингвистический вклад программирования без GOTO
  • «Структурированное программирование с помощью операторов перехода» (PDF) . Архивировано из оригинального (PDF) 24 августа 2009 года. (2,88 МБ)
  • «Руководство по IBM 704» (PDF) . (31,4 МБ)