Заявление о переключении

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

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

Операторы switch работают ifпримерно так же, как операторы, используемые в таких языках программирования, как C / C ++, C #, Visual Basic.NET, Java, и существуют в большинстве языков императивного программирования высокого уровня, таких как Pascal, Ada, C / C ++, C #, Visual Basic. NET, Java, и во многих других типах языка, используя такие ключевые слова, как switch, case, selectили inspect.

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

Оператор переключения в C
switch (age) { case 1: printf("You're one.");   break; case 2: printf("You're two.");   break; case 3: printf("You're three."); case 4: printf("You're three or four."); break; default: printf("You're not 1,2,3 or 4!"); }
СОДЕРЖАНИЕ
  • 1 История
  • 2 Типичный синтаксис
  • 3 Семантика
    • 3.1 Прохождение
  • 4 Сборник
  • 5 Достоинства и недостатки
  • 6 Переключить выражения
  • 7 Альтернативные варианты использования
    • 7.1 PHP
    • 7.2 Рубин
    • 7.3 Ассемблер
    • 7.4 Обработка исключений
  • 8 альтернатив
  • 9 См. Также
  • 10 Ссылки
  • 11 Дальнейшее чтение
История

В своем тексте « Введение в метаматематику» 1952 года Стивен Клини формально доказал, что функция CASE (функция IF-THEN-ELSE является ее простейшей формой) является примитивной рекурсивной функцией, где он определяет это понятие definition by casesследующим образом:

"#F. Определенная таким образом функция φ
φ (x 1,..., x n) =
  • φ 1 (x 1,..., x n), если Q 1 (x 1,..., x n),
  • ............
  • φ m (x 1,..., x n), если Q m (x 1,..., x n),
  • φ m + 1 (x 1,..., x n) в противном случае,
где Q 1,..., Q m - взаимоисключающие предикаты (или φ (x 1,..., x n) должен иметь значение, заданное первым применимым предложением) является примитивно рекурсивным в φ 1,..., φ m + 1, Q 1,..., Q m + 1.

Клини предоставляет доказательство этого в терминах булевых рекурсивных функций «знак» sg () и «не знак» ~ sg () (Kleene 1952: 222-223); первый возвращает 1, если его вход положительный, и -1, если его вход отрицательный.

Булос-Берджесс-Джеффри делает дополнительное наблюдение, что «определение по случаям» должно быть как взаимоисключающим, так и исчерпывающим в совокупности. Они также предлагают доказательство примитивной рекурсивности этой функции (Boolos-Burgess-Jeffrey 2002: 74-75).

IF-THEN-ELSE является основой формализма Маккарти : его использование заменяет как примитивную рекурсию, так и оператор mu.

Типичный синтаксис

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

  • первое select, за которым следует выражение, которое часто называют управляющим выражением или управляющей переменной оператора switch
  • последующие строки, определяющие фактические случаи (значения), с соответствующими последовательностями операторов для выполнения при обнаружении совпадения
  • В языках с провальным поведением breakоператор обычно следует за caseоператором, чтобы завершить указанное утверждение. [Уэллс]

Каждая альтернатива начинается с конкретного значения или списка значений (см. Ниже), которым может соответствовать управляющая переменная и которые заставят элемент управления перейти к соответствующей последовательности операторов. Значение (или список / диапазон значений) обычно отделяется от соответствующей последовательности операторов двоеточием или стрелкой следования. Во многих языках перед каждым регистром также должно стоять ключевое слово, например caseили when.

Дополнительный корпус по умолчанию, как правило, также допускаются, задается default, otherwiseили elseключевым словом. Это выполняется, когда ни один из других случаев не соответствует контрольному выражению. В некоторых языках, таких как C, если ни один регистр не соответствует и defaultопущен, switchинструкция просто завершается. В других, таких как PL / I, возникает ошибка.

Семантика

Семантически существует две основные формы операторов switch.

Первая форма - это структурированные переключатели, как в Паскале, где берется ровно одна ветвь, а случаи рассматриваются как отдельные исключительные блоки. Это функционирует как обобщенное условие if-then-else, здесь с любым количеством ветвей, а не только с двумя.

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

Провалиться

Во многих языках выполняется только соответствующий блок, а затем выполнение продолжается в конце оператора switch. К ним относятся семейство Паскаля (Object Pascal, Modula, Oberon, Ada и т. Д.), А также PL / I, современные формы диалектов Fortran и BASIC, на которые повлиял Паскаль, большинство функциональных языков и многие другие. Чтобы позволить нескольким значениям выполнять один и тот же код (и избежать необходимости дублировать код ), языки типа Pascal допускают любое количество значений для каждого случая, заданного в виде списка, разделенного запятыми, в виде диапазона или в виде комбинации.

Языки, производные от языка C, и, в более общем плане, те, на которые влияет вычисляемый GOTO Fortran, вместо этого имеют функцию провала, когда управление перемещается в соответствующий регистр, а затем выполнение продолжается ("проваливается") до операторов, связанных со следующим регистром в исходном тексте.. Это также позволяет нескольким значениям соответствовать одной и той же точке без какого-либо специального синтаксиса: они просто перечислены с пустыми телами. Значения могут быть специально обусловлены кодом в теле кейса. На практике провал обычно предотвращается с помощью breakключевого слова в конце совпадающего тела, которое завершает выполнение блока переключения, но это может вызвать ошибки из-за непреднамеренного провала, если программист забывает вставить breakоператор. Таким образом, многие считают это языковой бородавкой и предостерегают от этого в некоторых инструментах lint. Синтаксически варианты интерпретируются как метки, а не блоки, а операторы switch и break явно изменяют поток управления. Некоторые языки, на которые влияет C, например JavaScript, сохраняют провал по умолчанию, в то время как другие удаляют провал или разрешают его только в особых случаях. Известные варианты этого в семействе C включают C #, в котором все блоки должны заканчиваться символом breakили, returnесли блок не пуст (т. Е. Переход используется как способ указания нескольких значений).

В некоторых случаях языки обеспечивают необязательный откат. Например, Perl по умолчанию не работает, но случай может явно сделать это с помощью continueключевого слова. Это предотвращает непреднамеренное падение, но позволяет при желании. Точно так же Bash по умолчанию не проваливается при завершении с помощью ;;, но разрешает провал с помощью ;amp;или ;;amp;вместо этого.

Примером оператора switch, который полагается на провал, является устройство Даффа.

Компиляция

Оптимизирующие компиляторы, такие как GCC или Clang, могут скомпилировать оператор switch либо в таблицу ветвлений, либо в двоичный поиск по значениям в случаях. Таблица переходов позволяет оператору switch определять с помощью небольшого постоянного числа инструкций, какую ветвь выполнять, не просматривая список сравнений, в то время как двоичный поиск требует только логарифмического числа сравнений, измеряемого числом наблюдений в оператор переключения.

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

Преимущества и недостатки

В некоторых языках и средах программирования использование оператора caseor switchсчитается более предпочтительным по сравнению с эквивалентной серией операторов if else if, поскольку оно:

  • Легче отлаживать (например, установка точек останова в коде по сравнению с таблицей вызовов, если отладчик не имеет возможности условной точки останова)
  • Человеку легче читать
  • Легче понять и, следовательно, проще поддерживать
  • Фиксированная глубина: последовательность операторов «if else if» может привести к глубокой вложенности, что затрудняет компиляцию (особенно в автоматически сгенерированном коде)
  • Легче проверить, обрабатываются ли все значения. Компиляторы могут выдавать предупреждение, если некоторые значения перечисления не обрабатываются.

Кроме того, оптимизированная реализация может выполняться намного быстрее, чем альтернатива, поскольку она часто реализуется с использованием индексированной таблицы переходов. Например, принятие решения о выполнении программы на основе значения одного символа, если оно правильно реализовано, намного более эффективно, чем альтернатива, значительно сокращая длину пути команд. При реализации как таковой оператор switch по сути становится идеальным хешем.

В терминах графа потока управления оператор switch состоит из двух узлов (входа и выхода) плюс одно ребро между ними для каждого варианта. Напротив, последовательность операторов «if... else if... else if» имеет дополнительный узел для каждого случая, кроме первого и последнего, вместе с соответствующим ребром. Получающийся в результате граф потока управления для последовательностей «if», таким образом, имеет намного больше узлов и почти вдвое больше ребер, при этом они не добавляют никакой полезной информации. Однако простые ветви в операторах if по отдельности концептуально проще, чем сложная ветвь оператора switch. Что касается цикломатической сложности, оба этих варианта увеличивают ее на k −1, если заданы k случаев.

Переключить выражения

Выражения переключения представлены в Java SE 12, 19 марта 2019 г., в качестве предварительной версии. Здесь для возврата значения можно использовать целое выражение переключателя. Существует также новая форма метки case, case L-gt;где правая часть представляет собой одно выражение. Это также предотвращает падение и требует, чтобы случаи были исчерпывающими. В Java SE 13 представлен yieldоператор, а в Java SE 14 выражения переключения становятся стандартной функцией языка. Например:

int ndays = switch(month) { case JAN, MAR, MAY, JUL, AUG, OCT, DEC -gt; 31; case APR, JUN, SEP, NOV -gt; 30; case FEB -gt; { if(year % 400 ==0) yield 29; else if(year % 100 == 0) yield 28; else if(year % 4 ==0) yield 29; else yield 28; } };
Альтернативные варианты использования

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

PHP

Например, в PHP константа может использоваться в качестве «переменной» для проверки, и будет выполнен первый оператор case, который оценивает эту константу:

switch (true) { case ($x == 'hello'): foo(); break; case ($z == 'howdy'): break; } switch (5) { case $x: break; case $y: break; }

Эта функция также полезна для проверки нескольких переменных по одному значению, а не одной переменной по нескольким значениям. COBOL также поддерживает эту форму (и другие формы) в EVALUATEзаявлении. PL / I имеет альтернативную форму SELECTоператора, в которой контрольное выражение полностью опускается, а выполняется первое, WHENкоторое оценивается как истинное.

Рубин

В Ruby, благодаря обработке ===равенства, оператор может использоваться для проверки класса переменной:

case input when Array then puts 'input is an Array!' when Hash then puts 'input is a Hash!' end

Ruby также возвращает значение, которое может быть присвоено переменной, и на самом деле не требует caseналичия каких-либо параметров (действует как else ifоператор):

catfood = case when cat.age lt;= 1 junior when cat.age gt; 10 senior else normal end

Ассемблер

Оператор switch на языке ассемблера :

switch: cmp ah, 00h je a cmp ah, 01h je b jmp swtend ; No cases match or "default" code here a: push ah mov al, 'a' mov ah, 0Eh mov bh, 00h int 10h pop ah jmp swtend ; Equivalent to "break" b: push ah mov al, 'b' mov ah, 0Eh mov bh, 00h int 10h pop ah jmp swtend ; Equivalent to "break"... swtend:

Обработка исключений

Ряд языков реализуют форму оператора switch при обработке исключений, где, если исключение возникает в блоке, в зависимости от исключения выбирается отдельная ветвь. В некоторых случаях также присутствует ветвь по умолчанию, если исключение не возникает. Ранним примером является Modula-3, в котором используется синтаксис TRY... EXCEPT, каждый из которых EXCEPTопределяет регистр. Это также можно найти в Delphi, Scala и Visual Basic.NET.

Альтернативы

Некоторые альтернативы операторам переключения могут быть:

  • Серия условных выражений if-else, которые проверяют целевое значение по одному за раз. Поведение провала может быть достигнуто с помощью последовательности условных выражений if, каждое без предложения else.
  • Таблицы поиска, которая содержит, в качестве ключей, то caseзначений и, как значения, в части под caseзаявлением.
(На некоторых языках в качестве значений в справочной таблице разрешены только фактические типы данных. На других языках также возможно назначать функции в качестве значений справочной таблицы, что обеспечивает такую ​​же гибкость, что и реальный switchоператор. Подробнее см. В статье о контрольной таблице. на этом).
Lua не поддерживает операторы case / switch: http://lua-users.org/w/SwitchStatement. Этот метод поиска - один из способов реализации switchоператоров на языке Lua, который не имеет встроенных функций switch.
В некоторых случаях таблицы поиска более эффективны, чем неоптимизированные switch операторы, поскольку многие языки могут оптимизировать поиск в таблицах, в то время как операторы switch не оптимизируются, если диапазон значений невелик с небольшими пробелами. Однако неоптимизированный, небинарный поиск почти наверняка будет медленнее, чем неоптимизированный переключатель или эквивалентные множественные операторы if-else.
  • Управления таблица (который может быть реализована в виде простой таблицы перекодировки) также может быть настроена для размещения нескольких условий на нескольких входах, если это необходимо, и, как правило, проявляет большую «» визуальную компактность, чем эквивалентный переключатель (который может занимать много заявлений).
  • Сопоставление с образцом, которое используется для реализации функциональности переключателя во многих функциональных языках.
Смотрите также
использованная литература
дальнейшее чтение
  • Стивен Клини, 1952 г. (10-е переиздание 1991 г.), Введение в метаматематику, издательство North-Holland Publishing Company, Амстердам, Нидерланды, ISBN   0-7204-2103-9
  • Джордж Булос, Джон Берджесс и Ричард Джеффри, 2002, Вычислимость и логика: четвертое издание, Cambridge University Press, Cambridge UK, ISBN   0-521-00758-5 в мягкой обложке. см. стр. 74-75.
Последняя правка сделана 2023-03-27 08:48:28
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте