stringtranslate.com

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

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

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

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

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

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

Категории

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

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

Примитивы

Этикетки

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

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

10 БУКУС X = 3 20 ПЕЧАТЬ X      

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

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

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

Идти к

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

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

 перейти к  ярлыку

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

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

Подпрограммы

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

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

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

Последовательность

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

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

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

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

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

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

Структуры контроля на практике

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

Выбор

Операторы if-then-(else)

Условные выражения и условные конструкции — это функции языка программирования , которые выполняют различные вычисления или действия в зависимости от того, имеет ли заданное программистом логическое условие значение true или false.

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

Операторы Case и Switch

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

Петли

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

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

Петли, управляемые счетом

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

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

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

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

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

Контуры, управляемые по состоянию

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

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

 ДЕЛАТЬ ДО (конца файла) ЕСЛИ новый почтовый индекс <> текущий почтовый индекс display_tally (текущий почтовый индекс, почтовый индекс)  текущий почтовый индекс = новый почтовый индекс почтовый индекс = 0 КОНДИФ  zipcount++ ПЕТЛЯ

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

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

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

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

Общая итерация

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

Бесконечные циклы

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

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

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

Продолжение следующей итерации

Иногда внутри тела цикла возникает желание пропустить оставшуюся часть тела цикла и перейти к следующей итерации цикла. В некоторых языках предусмотрен оператор, например continue(большинство языков), skip, [5] cycle (Фортран) или 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 ; начать  Read_Data  :  цикл  Ada . Целочисленный  текстовый  ввод-вывод . Получить ( Х );  выйти из  Read_Data,  когда  X  =  0 ;  Ада . Текст  ИО . Поместите  ( Х  *  Х );  Ада . Текст  ИО . Новая линия ;  завершить  цикл  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] Альтернативы многоуровневым разрывам включают одиночные разрывы вместе с переменной состояния, которая проверяется на прорыв другого уровня; исключения, которые перехватываются на уровне, на который осуществляется прорыв; помещение вложенных циклов в функцию и использование возврата для завершения всего вложенного цикла; или используя метку и оператор перехода. C не включает многоуровневый разрыв, и обычной альтернативой является использование перехода для реализации помеченного разрыва. [12] В Python нет многоуровневого разрыва или продолжения — это было предложено в PEP 3136 и отклонено на том основании, что добавленная сложность не стоит редкого законного использования. [13]

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

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

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

Варианты цикла и инварианты

Варианты цикла и инварианты цикла используются для выражения правильности циклов. [17]

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

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

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

Циклический подъязык

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

Таблица перекрестных ссылок системы контуров

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

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

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

Условия

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

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

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

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

  Состояние  ВКЛ Метка GOTO 

Исключения

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

try { xxx1 // Где-то здесь xxx2 // используйте: '''throw''' someValue; xxx3 } catch ( someClass & someId ) { // перехватываем значение someClass actionForSomeClass } catch ( someType & otherId ) { // перехватываем значение someType actionForSomeType } catch (...) { // перехватываем все, что еще не перехвачено 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 года также анализируется обработка исключений в рамках секвенсоров (представленная в этой статье в разделе о ранних выходах из циклов). Уотт отмечает, что ненормальная ситуация, обычно иллюстрируемая арифметическими переполнениями или ошибками ввода/вывода, например, файл не найден, является своего рода ошибкой, которая «обнаруживается в каком-то низкоуровневом программном модуле, но [для которого] более естественным образом расположен обработчик. в программном модуле высокого уровня». Например, программа может содержать несколько вызовов чтения файлов, но действие, которое необходимо выполнить, когда файл не найден, зависит от значения (назначения) рассматриваемого файла для программы, и поэтому процедура обработки этой ненормальной ситуации не может быть создана. находится в низкоуровневом системном коде. Уоттс далее отмечает, что введение тестирования флагов состояния в вызывающем объекте, как это повлечет за собой структурное программирование с одним выходом или даже секвенсоры возврата (с несколькими выходами), приводит к ситуации, когда «код приложения имеет тенденцию загромождаться проверками флагов состояния» и что «программист может по забывчивости или лениво не проверять флаг состояния. Фактически, нештатные ситуации, представленные флагами состояния, по умолчанию игнорируются!» Ватт отмечает, что в отличие от тестирования флагов состояния, исключения имеют противоположное поведение по умолчанию , вызывая завершение работы программы, если только программа не обрабатывает исключение каким-либо явным образом, возможно, путем добавления явного кода для его игнорирования. Основываясь на этих аргументах, Уотт заключает, что секвенсоры перехода или escape-секвенсоры менее подходят в качестве специализированного секвенсора исключений с семантикой, описанной выше. [21]

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

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

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

использование ( вар stm = новый FileStream ( «logfile.txt» , FileMode . Create )) { return ProcessStuff ( stm ); // может вызвать исключение }         

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

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

Продолжение

Асинхронный

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

Генераторы

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

Сопрограммы

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

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

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

Предлагаемые структуры контроля

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

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

Петля с тестом посередине

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

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

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

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

в то время как (истина) { ххх1 если ( не тест) сломается ххх2}

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

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

с  Ada.Text_IO ; с  Ada.Integer_Text_IO ;процедура  Print_Squares  равна  X  :  Integer ; начать  Read_Data  :  цикл  Ada . Целое_Текст_IO . Получить ( Х );  выйти из  Read_Data,  когда  X  =  0 ;  Ада . Текст  ИО . Поместите  ( Х  *  Х );  Ада . Текст  ИО . Новая линия ;  завершить  цикл  Read_Data ; конец  Print_Squares ;

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

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

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

 выйти, когда EventA или EventB или EventC; ххх выходит Событие А: действие А Событие Б: действие Б СобытиеC: действиеC конечный выход ;

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

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

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

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

Безопасность

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

Смотрите также

Рекомендации

  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. ^ Программирование на языке Ada: Управление: бесконечный цикл
  5. ^ «Что такое циклы и как их использовать?». Архивировано из оригинала 28 июля 2020 г. Проверено 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. ^ Аб Козен, Декстер (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.
  15. ^ Косараджу, С. Рао. «Анализ структурированных программ», Учеб. Пятый ежегодный сироп ACM. Теория вычислений (май 1973 г.), 240–252; также в журнале J. Computer and System Sciences, 9, 3 (декабрь 1974 г.). цитируется Кнутом, Дональдом (1974). «Структурное программирование с переходом к операторам». Вычислительные опросы . 6 (4): 261–301. CiteSeerX 10.1.1.103.6084 . дои : 10.1145/356635.356640. S2CID  207630080. 
  16. ^ Дэвид Энтони Ватт; Уильям Финдли (2004). Концепции проектирования языков программирования . Джон Уайли и сыновья. стр. 215–221. ISBN 978-0-470-85320-7.
  17. ^ Мейер, Бертран (1991). Эйфель: Язык . Прентис Холл. стр. 129–131.
  18. ^ «Общий макрос Lisp LOOP» .
  19. ^ for_each. Sgi.com. Проверено 9 ноября 2010 г.
  20. ^ Глава 1. Boost.Foreach. Архивировано 29 января 2010 г. в Wayback Machine . Boost-sandbox.sourceforge.net (19 декабря 2009 г.). Проверено 9 ноября 2010 г.
  21. ^ Дэвид Энтони Ватт; Уильям Финдли (2004). Концепции проектирования языков программирования . Джон Уайли и сыновья. стр. 221–222. ISBN 978-0-470-85320-7.
  22. ^ «Asyncio: асинхронный ввод-вывод: документация Python 3.10.2» .
  23. ^ "Сокет/Асинхронный". Гитхаб . 25 февраля 2022 г.
  24. ^ «Генераторы - книга о нестабильной ржавчине» .
  25. ^ "Корона - Ржавчина".
  26. ^ «Начало работы — асинхронное программирование на Rust» .
  27. ^ "Встреча с Джитси". Storm-enroute.com . Проверено 7 сентября 2022 г.
  28. ^ Мы не знаем, куда ПЕРЕЙТИ, если не знаем, ОТКУДА мы ПРИШЛИ. Эта (пародия) лингвистическая инновация оправдывает все ожидания. Архивировано 16 июля 2018 г. в Wayback Machine Р. Лоуренсом Кларком * Из Datamation, декабрь 1973 г.
  29. ^ Кнут, Дональд Э. «Структурное программирование с переходом к операторам» ACM Computing Surveys 6 (4): 261-301, декабрь 1974 г.
  30. ^ Даль, Дейкстра и Хоар, «Структурированное программирование», Academic Press, 1972.
  31. ^ Зан, Коннектикут «Управляющий оператор для естественного структурированного нисходящего программирования», представленный на симпозиуме по языкам программирования, Париж, 1974.
  32. ^ Пайер, Матиас ; Кузнецов Владимир. «О различиях свойств CFI, CPS и CPI». nebelwelt.net . Проверено 1 июня 2016 г.
  33. ^ «Обнаружение ошибок Adobe Flash приводит к новому методу предотвращения атак» . Мрачное чтение . 10 ноября 2015 года . Проверено 1 июня 2016 г.
  34. ^ Финал. «Финал будет представлен на Black Hat USA 2016». www.prnewswire.com . Проверено 1 июня 2016 г.

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

Внешние ссылки