Jump to content

Оператор переключения

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

Операторы Switch функционируют примерно так же, как и операторы Switch. if оператор, используемый в таких языках программирования, как C / C++ , C# , Visual Basic .NET , Java высокого уровня, , и существует в большинстве императивных языков программирования таких как Pascal , Ada , C / C++ , C# , [1] : 374–375  Visual Basic.NET , Java , [2] : 157–167  и во многих других типах языка, используя такие ключевые слова , как switch, case, select или inspect.

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

Оператор переключения в 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!");
}

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

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

«#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 .

Стивен Клини, [3]

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

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

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

Самые ранние компиляторы Фортрана поддерживали вычисляемый оператор GOTO для многопутевого ветвления. Ранние компиляторы АЛГОЛА поддерживали тип данных SWITCH, который содержит список «выражений обозначения». Оператор GOTO может ссылаться на переменную переключателя и, предоставляя индекс, переходить к желаемому месту назначения. С опытом стало понятно, что необходима более формальная многоходовая конструкция с единой точкой входа и выхода. Такие языки, как BCPL , ALGOL-W и ALGOL-68 , представили формы этой конструкции, которые сохранились в современных языках.

Типичный синтаксис [ править ]

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

  • первый select, за которым следует выражение, которое часто называют управляющим выражением или управляющей переменной оператора переключения.
  • последующие строки, определяющие фактические случаи (значения), с соответствующими последовательностями операторов для выполнения при возникновении совпадения.
  • В языках с провальным поведением break заявление обычно следует за case заявление о завершении указанного заявления. [Уэллс]
  • В некоторых языках, например, PL/I , управляющее выражение является необязательным; если управляющего выражения нет, то каждая альтернатива начинается с WHEN предложение, содержащее логическое выражение, и совпадение происходит для первого случая, для которого это выражение имеет значение true. Такое использование похоже на структуры if/then/elseif/else в некоторых других языках, например Perl .
  • В некоторых языках, например, Rexx , управляющие выражения не допускаются, и каждая альтернатива начинается с символа. WHEN предложение, содержащее логическое выражение, и совпадение происходит для первого случая, для которого это выражение имеет значение true.

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

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

Семантика [ править ]

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

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

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

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

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

Фортрана Языки, производные от языка C, и, в более общем смысле, те, на которые влияет вычисленный GOTO , вместо этого имеют провал, когда управление переходит к соответствующему регистру, а затем выполнение продолжается («проваливается») до операторов, связанных со следующим регистром в исходном тексте. . Это также позволяет нескольким значениям соответствовать одной и той же точке без какого-либо специального синтаксиса: они просто перечисляются с пустыми телами. Значения могут быть специально обусловлены кодом в теле дела. На практике провал обычно предотвращается с помощью break ключевое слово в конце соответствующего тела, которое завершает выполнение блока переключателя, но это может вызвать ошибки из-за непреднамеренного сбоя, если программист забудет вставить ключевое слово break заявление. Так видят многие [4] как языковая бородавка, и некоторые инструменты для ворса предупреждают об этом. Синтаксически случаи интерпретируются как метки, а не блоки, а операторы переключения и прерывания явно изменяют поток управления. Некоторые языки, находящиеся под влиянием C, такие как JavaScript , сохраняют провал по умолчанию, в то время как другие удаляют провал или допускают его только в особых обстоятельствах. Заметные варианты этого в семействе C включают C# , в котором все блоки должны заканчиваться символом break или return если только блок не пуст (т. е. проходной режим используется как способ указания нескольких значений).

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

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

Сборник [ править ]

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

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

Преимущества и недостатки [ править ]

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

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

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

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

Переключение выражений [ править ]

Выражения переключения представлены в Java SE 12 от 19 марта 2019 г. в качестве предварительной функции. Здесь для возврата значения можно использовать целое выражение переключателя. Также имеется новая форма этикетки корпуса, case L-> где правая часть представляет собой одно выражение. Однако это также предотвращает падение и требует, чтобы случаи были исчерпывающими. В Java SE 13 yield введен оператор, и в Java SE 14 выражения переключения становятся стандартной функцией языка. [8] [9] [10] Например:

int ndays = switch (month) {
    case JAN, MAR, MAY, JUL, AUG, OCT, DEC -> 31;
    case APR, JUN, SEP, NOV -> 30;
    case FEB -> {
        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 который имеет значение true, выполняется.

Руби [ править ]

В 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 <= 1
    junior
  when cat.age > 10
    senior
  else
    normal
  end

Собрать [ править ]

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

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:

Питон [ править ]

Для Python 3.10.6 были приняты PEP 634-636, что добавило match и case ключевые слова. [11] [12] [13] [14] В отличие от других языков, Python не демонстрирует провалов.

letter = input("Put in a single letter: ").strip()[0].casefold() # first non-whitespace character of the input, lowercase
match letter:
    case 'a' | 'e' | 'i' | 'o' | 'u': # Unlike conditions in if statements, the `or` keyword cannot be used here to differentiate between cases
        print(f"Letter {letter} is a vowel!")
    case 'y':
        print(f"Letter {letter} may be a vowel.")
    case _: # `case _` is equivalent to `default` from C and others
        print(f"Letter {letter} is not a vowel!")

Обработка исключений [ править ]

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

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

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

  • Ряд if-else условных операторов , которые проверяют целевое значение по одному. Поведение Fallthrough может быть достигнуто с помощью последовательности условных операторов if без предложения else .
  • Таблица поиска , которая в качестве ключей содержит case значения и, как значения, часть под case заявление.
(В некоторых языках в качестве значений в таблице поиска разрешены только фактические типы данных. В других языках также можно назначать функции в качестве значений таблицы поиска, получая ту же гибкость, что и реальные типы данных. switch заявление. см. в статье «Таблица управления» ). Более подробную информацию об этом
Lua не поддерживает операторы case/switch. [15] Этот метод поиска является одним из способов реализации switch операторы на языке Lua, который не имеет встроенных switch. [15]
В некоторых случаях таблицы поиска более эффективны, чем неоптимизированные . switch операторы, поскольку многие языки могут оптимизировать поиск в таблицах, тогда как операторы переключения не оптимизируются, если диапазон значений не мал с небольшим количеством пробелов. Однако неоптимизированный, недвоичный поиск почти наверняка будет медленнее, чем неоптимизированный переключатель или эквивалентные несколько операторов if-else . [ нужна ссылка ]
  • Таблица управления (которая может быть реализована как простая таблица поиска) также может быть настроена для учета нескольких условий на нескольких входах, если это необходимо, и обычно демонстрирует большую «визуальную компактность», чем эквивалентный переключатель (который может занимать много операторов).
  • Сопоставление с образцом , которое используется для реализации функциональности, подобной переключателю, во многих функциональных языках.

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

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

  1. ^ Скит, Джон (23 марта 2019 г.). C# в глубине . Мэннинг. ISBN  978-1617294532 .
  2. ^ Блох, Джошуа (2018). «Эффективная Java: Руководство по языку программирования» (третье изд.). Аддисон-Уэсли. ISBN  978-0134685991 .
  3. ^ «Определение по случаям», Клини 1952: 229.
  4. ^ ван дер Линден, Питер (1994). Экспертное программирование на языке C: глубокие секреты C , с. 38. Прентис Холл, Иглвуд Клиффс. ISBN   0131774298 .
  5. ^ начиная с версии 4.0 , выпущенной в 2009 году.
  6. ^ Влад Лазаренко. От оператора Switch до машинного кода
  7. ^ Гюнтерот, Курт (27 апреля 2016 г.). Оптимизированный С++ . О'Рейли Медиа. п. 182. ИСБН  9781491922033 .
  8. ^ «JEP 325: Переключение выражений (предварительная версия)» . openjdk.java.net . Проверено 28 апреля 2021 г.
  9. ^ «JEP 354: Переключение выражений (вторая предварительная версия)» . openjdk.java.net . Проверено 28 апреля 2021 г.
  10. ^ «JEP 361: Переключение выражений» . openjdk.java.net . Проверено 28 апреля 2021 г.
  11. ^ Галиндо Сальгадо, Пабло. «Что нового в Python 3.10» . Документация Python 3.10.6 . Проверено 19 августа 2022 г.
  12. ^ Бухер, Брандт; ван Россум, Гвидо (12 сентября 2020 г.). «PEP 634 – Соответствие структурному шаблону: Спецификация» . Предложения по улучшению Python . Проверено 19 августа 2022 г.
  13. ^ Кон, Тобиас ; ван Россум, Гвидо (12 сентября 2020 г.). «PEP 635 – Сопоставление структурных шаблонов: мотивация и обоснование» . Предложения по улучшению Python . Проверено 19 августа 2022 г.
  14. ^ Муассе, Дэниел Ф. «PEP 636 – Сопоставление структурных шаблонов: Учебное пособие» . Предложения по улучшению Python . Проверено 19 августа 2022 г.
  15. ^ Jump up to: Перейти обратно: а б Оператор Switch в Lua

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

Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: 4ec9133573ea2c53d908f8d8a1762b92__1718218320
URL1:https://arc.ask3.ru/arc/aa/4e/92/4ec9133573ea2c53d908f8d8a1762b92.html
Заголовок, (Title) документа по адресу, URL1:
Switch statement - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)