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

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

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

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

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

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

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

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

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

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

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

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

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

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

10 ПОЗВОНИТЬ X = 3 20 ПЕЧАТЬ X      

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

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

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

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

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

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

  метка goto

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • Нет последнего ключевого слова: ALGOL 60 , C , C ++ , Haskell , Java , Pascal , Perl , PHP , PL / I , Python , PowerShell . Таким языкам нужен какой-то способ группировки операторов вместе:
    • АЛГОЛ 60 и Паскаль: begin...end
    • C, C ++, Java, Perl, PHP и PowerShell: фигурные скобки { ...}
    • PL / I: DO...END
    • Python: использует уровень отступа (см. Автономное правило )
    • Haskell: можно использовать либо уровень отступа, либо фигурные скобки, и их можно свободно смешивать
    • Lua: использует do...end
  • Последнее ключевое слово: Ада , АЛГОЛ 68 , Модула-2 , Фортран 77 , Мифрил , Visual Basic . Формы последнего ключевого слова различаются:
    • Ada: последнее ключевое слово - это end+ пробел + начальное ключевое слово, например, if... end if, loop...end loop
    • АЛГОЛ 68, мифрил: начальное ключевое слово написано в обратном порядке, например, if... fi, case...esac
    • Fortran 77: последнее ключевое слово END+ начальное ключевое слово, например, IF... ENDIF, DO...ENDDO
    • Модула-2: одно и то же последнее ключевое слово ENDдля всего
    • Visual Basic: каждая структура управления имеет собственное ключевое слово. If... End If; For... Next; Do... Loop; While...Wend

Выбор [ править ]

Операторы 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 дополняет стиль Си 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.

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

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

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

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

 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 . Целочисленный  текст  IO . Получить ( X );  выйти из  Read_Data,  когда  X  =  0 ;  Ада . Текст  IO . Положите  ( X  *  X );  Ада . Текст  IO . New_Line ;  конец  цикла  Read_Data ; конец  Print_Squares;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1. 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; ххх выходы СобытиеA: действиеA СобытиеB: действиеB EventC: действиеC эндексит ;

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

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

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

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

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

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

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

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

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

  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. Теория вычислений (май 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 МБ)