stringtranslate.com

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

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

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

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

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

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

Категории

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

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

Примитивы

Этикетки

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

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

10 ДАВАЙТЕ X = 3 20 ПЕЧАТЬ X      

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

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

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

Перейти

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

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

 перейти  к метке

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

Многие специалисты по информатике, в частности Дейкстра , считали операторы goto вредными .

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

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

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

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

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

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

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

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

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

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

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

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

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

Выбор

Если-то-(иначе) утверждения

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

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

Операторы case и switch

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

Петли

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

В функциональных языках программирования, таких как 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(текущий-почтовый-индекс, zipcount)  текущий-почтовый-индекс = новый-почтовый-индекс почтовый индекс = 0 КОНЕСЛИ  zipcount++ ПЕТЛЯ

Петли, контролируемые сбором

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

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

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

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

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

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

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

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

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

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

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

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

В некоторых языках, таких как Perl [9] и Ruby [10], есть redoоператор, который перезапускает текущую итерацию с самого начала.

Перезапустить цикл

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

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

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

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

с  Ada.Text  IO ; с  Ada.Integer  Text  IO ;procedure  Print_Squares  is  X  :  Integer ; begin  Read_Data  :  loop  Ada . Integer  Text  IO . Get ( X );  exit  Read_Data  when  X  =  0 ;  Ada . Text  IO . Put  ( X  *  X );  Ada . Text  IO . New_Line ;  end  loop  Read_Data ; end  Print_Squares ;

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

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

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

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

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

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

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

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

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

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

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

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

Подъязык цикла

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

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

  1. a while (true) не считается бесконечным циклом для этой цели, поскольку не является специализированной языковой структурой.
  2. Цикл C представляет собой общую конструкцию цикла , а не специально счетную, хотя она часто используется для этого . for (init; test; increment)
  3. a b c Глубокие разрывы могут быть выполнены в APL, C, C++ и C# с помощью меток и goto.
  4. Итерация по объектам была добавлена ​​в PHP 5.
  5. a b c Цикл подсчета можно смоделировать путем итерации по увеличивающемуся списку или генератору, например, Python . range()
  6. a b c d e Глубокие разрывы могут быть достигнуты с помощью обработки исключений.
  7. a Специальной конструкции не существует, поскольку whileдля этого можно использовать функцию.
  8. a Специальной конструкции нет, но пользователи могут определять общие функции цикла.
  9. a Стандарт C++11 ввел основанный на диапазоне for . В STL есть std::for_each шаблонная функция, которая может выполнять итерации по контейнерам STL и вызывать унарную функцию для каждого элемента. [22] Функциональность также может быть создана как макрос для этих контейнеров. [23]
  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), которые могут быть вызваны и которые могут быть прерваны с помощью: действия по условию ; Программисты также могут определять и использовать свои собственные именованные условия.

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

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

Примеры общего синтаксиса:

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

Исключения

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

try { xxx1 // Где-то здесь xxx2 // use: '''throw''' someValue; xxx3 } catch ( someClass & someId ) { // catch value of someClass actionForSomeClass } catch ( someType & anotherId ) { // catch value of someType actionForSomeType } catch (...) { // 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  если  (  e  =  "Невозможно разделить на ноль"  )  затем  отобразить диалоговое окно  "Вы не должны этого делать" конец  попытки

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

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

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

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

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

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

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

Продолжения

Асинхронный

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

Генераторы

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

Корутины

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

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

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

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

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

Статья Дональда Кнута 1974 года "Structured Programming with go to Statements" [32] определяет две ситуации, которые не были охвачены перечисленными выше управляющими структурами, и приводит примеры управляющих структур, которые могли бы справиться с этими ситуациями. Несмотря на свою полезность, эти конструкции пока не нашли своего пути в основные языки программирования.

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

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

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

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

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

в то время как (истина) { xxx1 если ( не тест) перерыв xxx2}

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

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

с  Ada.Text_IO ; с  Ada.Integer_Text_IO ;procedure  Print_Squares  is  X  :  Integer ; begin  Read_Data  :  loop  Ada . Integer_Text_IO . Get ( X );  exit  Read_Data  when  X  =  0 ;  Ada . Text  IO . Put  ( X  *  X );  Ada . Text  IO . New_Line ;  end  loop  Read_Data ; end  Print_Squares ;

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

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

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

 выход при Событии A или Событии B или Событии C; ххх выходы СобытиеA: действиеA СобытиеB: действиеB СобытиеC: действиеC endexit ;

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

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

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

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

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

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

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

Примечания

  1. ^ В Fortran это утверждение было признано устаревшим в Fortran-90 и удалено в Fortran 2018.

Ссылки

  1. ^ Бём, Якопини. «Диаграммы потоков, машины Тьюринга и языки только с двумя правилами формирования» Comm. ACM , 9(5):366-371, май 1966 г.
  2. ^ Робертс, Э. [1995] «Выходы из циклов и структурное программирование: возобновление дебатов». Архивировано 25 июля 2014 г. в Wayback Machine , ACM SIGCSE Bulletin, (27)1: 268–272.
  3. ^ Дэвид Энтони Уотт; Уильям Финдли (2004). Концепции дизайна языка программирования . John Wiley & Sons. стр. 228. ISBN 978-0-470-85320-7.
  4. ^ "Вложенные циклы в C с примерами". GeeksforGeeks . 2019-11-25 . Получено 2024-03-14 .
  5. ^ "Вложенные циклы Python". www.w3schools.com . Получено 2024-03-14 .
  6. ^ Дин, Дженна (2019-11-22). "Вложенные циклы". Стартап . Получено 2024-03-14 .
  7. ^ Программирование на языке Ada: Управление: Бесконечный цикл
  8. ^ "Что такое цикл и как мы можем его использовать?". Архивировано из оригинала 2020-07-28 . Получено 2020-05-25 .
  9. ^ "redo - perldoc.perl.org". perldoc.perl.org . Получено 2020-09-25 .
  10. ^ "control_expressions - Документация по Ruby 2.4.0". docs.ruby-lang.org . Получено 2020-09-25 .
  11. ^ "control_expressions - Документация по Ruby 2.3.0". docs.ruby-lang.org . Получено 2020-09-25 .
  12. ^ Расширенное руководство по написанию сценариев Bash: 11.3. Управление циклами
  13. ^ Руководство по PHP: "break"
  14. ^ perldoc: последний
  15. ^ Список часто задаваемых вопросов comp.lang.c · "Вопрос 20.20b"
  16. ^ [Python-3000] Анонс PEP 3136, Гвидо ван Россум
  17. ^ ab Kozen, Dexter (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.
  18. ^ Kosaraju, S. Rao. "Analysis of structured programs," Proc. Fifth Annual ACM Syrup. Theory of Computing, (май 1973 г.), 240-252; также в J. Computer and System Sciences, 9, 3 (декабрь 1974 г.). цитируется Knuth, Donald (1974). "Structured Programming with go to Statements". Computing Surveys . 6 (4): 261–301. CiteSeerX 10.1.1.103.6084 . doi :10.1145/356635.356640. S2CID  207630080. 
  19. ^ Дэвид Энтони Уотт; Уильям Финдли (2004). Концепции проектирования языков программирования . John Wiley & Sons. стр. 215–221. ISBN 978-0-470-85320-7.
  20. ^ Мейер, Бертран (1991). Эйфель: Язык . Prentice Hall. С. 129–131.
  21. ^ "Макрос Common Lisp LOOP".
  22. ^ for_each. Sgi.com. Получено 09.11.2010.
  23. Глава 1. Boost.Foreach Архивировано 29.01.2010 на Wayback Machine . Boost-sandbox.sourceforge.net (19.12.2009). Получено 09.11.2010.
  24. ^ Дэвид Энтони Уотт; Уильям Финдли (2004). Концепции проектирования языков программирования . John Wiley & Sons. стр. 221–222. ISBN 978-0-470-85320-7.
  25. ^ «Asyncio: Асинхронный ввод-вывод: Документация Python 3.10.2».
  26. ^ "Socketry/Async". GitHub . 25 февраля 2022 г.
  27. ^ «Генераторы — Rust Unstable Book».
  28. ^ "Корона - Ржавчина".
  29. ^ «Начало работы — Асинхронное программирование в Rust».
  30. ^ "Jitsi Meet". Storm-enroute.com . Получено 2022-09-07 .
  31. ^ Мы не знаем, куда ИДТИ, если не знаем, ОТКУДА ПРИШЛИ. Это (пародийное) лингвистическое новшество оправдывает все ожидания. Архивировано 16 июля 2018 г. в Wayback Machine Р. Лоуренсом Кларком* Из Datamation, декабрь 1973 г.
  32. ^ Кнут, Дональд Э. «Структурное программирование с операторами перехода» ACM Computing Surveys 6(4):261-301, декабрь 1974 г.
  33. ^ Даль, Дейкстра и Хоар, «Структурное программирование», Academic Press, 1972.
  34. ^ Зан, CT «Управляющее утверждение для естественного структурного программирования сверху вниз», представленное на Симпозиуме по языкам программирования, Париж, 1974.
  35. ^ Пайер, Матиас ; Кузнецов, Владимир. «О различиях между свойствами CFI, CPS и CPI». nebelwelt.net . Получено 01.06.2016 .
  36. ^ "Adobe Flash Bug Discovery Leads To New Attack Mitigation Method". Dark Reading . 10 ноября 2015 г. Получено 01.06.2016 .
  37. ^ Endgame. "Endgame to Present at Black Hat USA 2016". www.prnewswire.com (Пресс-релиз) . Получено 01.06.2016 .

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

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