В информатике условные выражения ( то есть условные операторы , условные выражения и условные конструкции ) — это конструкции языка программирования , которые выполняют различные вычисления или действия или возвращают разные значения в зависимости от значения логического выражения, называемого условием .
Условные выражения обычно реализуются путем выборочного выполнения инструкций. Хотя динамическая диспетчеризация обычно не классифицируется как условная конструкция, это еще один способ выбора между альтернативами во время выполнения .
Условные операторы — это императивные конструкции, выполняемые для побочного эффекта, тогда как условные выражения возвращают значения. Многие языки программирования (например, C) имеют отдельные условные операторы и условные выражения. Хотя в чистом функциональном программировании условные выражения не имеют побочных эффектов , многие языки с условными выражениями (например, Lisp) поддерживают условные побочные эффекты.
Конструкция if–then
or if–then–else
используется во многих языках программирования. Хотя синтаксис варьируется от языка к языку, базовая структура (в форме псевдокода ) выглядит следующим образом:
Если (логическое условие) Тогда (последующий)Еще (альтернатива)Конец, если
Например:
Если запас=0 Тогда сообщение = заказать новый товарЕще сообщение= есть в наличииКонец, если
В приведенном выше примере кода часть, представленная (логическим условием), представляет собой условное выражение , имеющее внутреннее значение (например, оно может быть заменено любым из значений True
или False
), но не имеющее внутреннего значения. Напротив, комбинация этого выражения, If
и, Then
окружающего его, и последующего, следующего за ним, составляют условное высказывание , имеющее внутреннее значение (например, выражающее связное логическое правило), но не имеющее внутренней ценности.
Когда интерпретатор находит If
, он ожидает логическое условие – например, x > 0
, что означает, что «переменная x содержит число, большее нуля» – и оценивает это условие. Если условие равно , выполняются true
инструкции, следующие за . then
В противном случае выполнение продолжается в следующей ветке — либо в else
блоке (что обычно необязательно), либо, если ветки нет else
, то после end If
.
После выполнения любой из ветвей управление возвращается в точку после end If
.
В ранних языках программирования, особенно в некоторых диалектах BASIC на домашних компьютерах 1980-х годов , if–then
оператор мог содержать только GOTO
операторы (эквивалентные команде перехода ). Это привело к появлению трудночитаемого стиля программирования, известного как программирование спагетти , а программы в этом стиле называются кодом спагетти . В результате структурное программирование , которое позволяет (практически) помещать произвольные операторы в блоки операторов внутри if
оператора, приобрело популярность, пока не стало нормой даже в большинстве кругов программирования на языке BASIC. Такие механизмы и принципы были основаны на более старом, но более совершенном семействе языков ALGOL , а языки, подобные ALGOL, такие как Pascal и Modula-2, в течение многих лет влияли на современные варианты BASIC. Хотя при использовании только GOTO
операторов в операторах можно if–then
писать программы, которые не являются спагетти-кодом и которые так же хорошо структурированы и читабельны, как и программы, написанные на структурированном языке программирования, структурное программирование упрощает эту задачу и обеспечивает ее соблюдение. Структурированные if–then–else
операторы, подобные приведенному выше примеру, являются одним из ключевых элементов структурного программирования и присутствуют в большинстве популярных языков программирования высокого уровня, таких как C , Java , JavaScript и Visual Basic .
Ключевое else
слово предназначено для конкретного if–then
оператора, предшествующего ему, но для вложенных if–then
операторов классические языки программирования, такие как ALGOL 60, изо всех сил пытались определить, какой конкретный оператор следует использовать. Без четких границ, для которых является оператором, else
ключевое слово может быть нацелено на любой предыдущий if–then
оператор в вложении после анализа.
если а , то если б , то s еще s2
может быть проанализировано как
если а , то ( если б , то s) иначе s2
или
если а , то ( если б , то s , иначе s2)
в зависимости от того, else
связан ли он с первым if
или вторым if
. Это известно как проблема висячего else и решается различными способами, в зависимости от языка (обычно с помощью end if
оператора или {...}
скобок).
Используя else if
, можно объединить несколько условий. Будут выполняться только операторы, следующие за первым условием, которое окажется истинным. Все остальные операторы будут пропущены.
if условие then -- операторы elseif условие then -- дополнительные операторы elseif условие then -- дополнительные операторы;...else -- другие утверждения; конец, если ;
Например, для магазина, предлагающего скидку до 30% на товар:
если скидка < 11% , то распечатать (нужно заплатить 30$)иначе если скидка<21% , то распечатать (нужно заплатить 20 долларов)иначе, если скидка<31% , тогда распечатать (нужно заплатить 10 долларов)конец, если ;
В приведенном выше примере, если скидка составляет 10%, то первое утверждение if будет оценено как истинное и будет распечатано «вы должны заплатить 30 долларов». Все остальные операторы ниже первого оператора if будут пропущены.
Например elseif
, в языке Ada этот оператор представляет собой просто синтаксический сахар , за else
которым следует if
. В Ada разница в том, что end if
требуется только один, если elseif
вместо else
него следует if
. PHP использует elseif
ключевое слово [1] как для фигурных скобок, так и для синтаксиса двоеточия. Perl предоставляет ключевое слово elsif
, чтобы избежать большого количества фигурных скобок, которые потребовались бы для нескольких операторов if
and else
. Python использует специальное ключевое слово, elif
поскольку структура обозначается отступами, а не фигурными скобками, поэтому повторное использование else
и if
потребует увеличения отступов после каждого условия. Некоторые реализации BASIC , такие как Visual Basic , [2]ElseIf
тоже используют . Аналогично, более ранние оболочки UNIX (позже объединенные в синтаксис оболочки POSIX [3] ) также используют elif, но дают возможность выбора разделения пробелами, разрывами строк или тем и другим.
Однако во многих языках, более непосредственно произошедших от Algol, таких как Simula , Pascal , BCPL и C , этот специальный синтаксис для else if
конструкции отсутствует, а также он не присутствует во многих синтаксических производных C, таких как Java , ECMAScript и скоро. Это работает, потому что в этих языках любой отдельный оператор (в данном случае ...) может следовать за условным выражением, не заключаясь в блок.if cond
Этот выбор дизайна имеет небольшую «стоимость». Каждая else if
ветвь фактически добавляет дополнительный уровень вложенности. Это усложняет работу компилятора (или людей, которые его пишут), поскольку компилятор должен else if
рекурсивно анализировать и реализовывать цепочки произвольной длины.
Если все термины в последовательности условных выражений проверяют значение одного выражения (например, if x=0
... else if x=1
... else if x=2
...), альтернативой является оператор переключения , также называемый оператором случая или оператором выбора. И наоборот, в языках, в которых нет оператора переключения, они могут быть созданы с помощью последовательности else if
операторов.
Многие языки поддерживают выражения if , которые похожи на операторы if, но в результате возвращают значение. Таким образом, это истинные выражения (которые оцениваются как значения), а не операторы (которые могут быть недопустимы в контексте значения).
АЛГОЛ 60 и некоторые другие члены семейства АЛГОЛ допускают if–then–else
выражение:
моя переменная:= если x > 20, то 1, иначе 2
В диалектах Лиспа — Scheme , Racket и Common Lisp — первый из которых в значительной степени был вдохновлен АЛГОЛом:
;; Схема ( определить мою переменную ( если ( > x 12 ) 1 2 )) ; Присваивает «myvariable» значение 1 или 2, в зависимости от значения «x».
;; Common Lisp ( let (( x 10 )) ( setq myvariable ( if ( > x 12 ) 2 4 ))) ; Присваивает 'myvariable' значение 2
В Haskell 98 есть только выражение if , нет оператора if , и эта else
часть является обязательной, поскольку каждое выражение должно иметь какое-то значение. [4] Логика, которая на других языках выражалась бы с помощью условий, обычно выражается с помощью сопоставления с образцом в рекурсивных функциях.
Поскольку Haskell ленив , можно писать управляющие структуры, например if , как обычные выражения; ленивая оценка означает, что функция if может оценивать только условие и правильную ветвь (тогда как строгий язык оценивал бы все три). Это можно записать так: [5]
if' :: Bool -> a -> a -> a if ' True x _ = x if' False _ y = y
В языках C и C-подобных есть специальный тернарный оператор ( ?: ) для условных выражений с функцией, которая может быть описана таким шаблоном:
condition ? evaluated-when-true : evaluated-when-false
Это означает, что его можно встраивать в выражения, в отличие от операторов if, в C-подобных языках:
моя_переменная = х > 10 ? "фу" : "бар" ; // В C-подобных языках
которые можно сравнить с выражениями if-then-else семейства Алголя (в отличие от оператора ) (и похожими, среди прочего, в Ruby и Scala).
Чтобы добиться того же с помощью оператора if, потребуется более одной строки кода (в соответствии с типичными соглашениями о макете) и дважды упомянуть «my_variable»:
если ( x > 10 ) my_variable = "foo" ; иначе my_variable = "бар" ;
Некоторые утверждают, что явный оператор if/then легче читать и что он может скомпилироваться в более эффективный код, чем тернарный оператор, [6] в то время как другие утверждают, что краткие выражения легче читать, чем операторы, разбитые на несколько строк, содержащие повторения.
х = ТекстовоеОкно . ReadNumber () Если ( x > 10 ) Тогда TextWindow . WriteLine ( "Моя переменная называется 'foo'." ) Else TextWindow . WriteLine ( "Моя переменная называется "bar". ) EndIf
Во-первых, когда пользователь запускает программу, появляется курсор, ожидающий, пока читатель наберет число. Если это число больше 10, появится текст «Моя переменная называется «foo». отображается на экране. Если число меньше 10, появится сообщение «Моя переменная называется «бар». печатается на экране.
В Visual BasicIIf
и некоторых других языках предусмотрена вызываемая функция , которую можно использовать как условное выражение. Однако оно не ведет себя как истинное условное выражение, поскольку всегда оцениваются как истинная, так и ложная ветви; просто результат одного из них выбрасывается, а результат другого возвращается функцией IIf.
В Tcl if
это не ключевое слово, а функция (в Tcl известная как команда или proc
). Например
if { $x > 10 } { ставит "Фу!" }
вызывает функцию с именем, if
передающую 2 аргумента: первый — это условие, а второй — истинная ветвь. Оба аргумента передаются в виде строк (в Tcl все, что находится в фигурных скобках, является строкой).
В приведенном выше примере условие не оценивается перед вызовом функции. Вместо этого реализация функции if
получает условие как строковое значение и отвечает за оценку этой строки как выражения в области вызывающего объекта. [7]
Такое поведение возможно с помощью команд uplevel
и expr
:
Поскольку if
на самом деле это функция, она также возвращает значение:
В Rust это if
всегда выражение. Он оценивается по значению в зависимости от того, какая ветвь выполняется, или по типу модуля, ()
если ни одна ветвь не выполняется. Если ветвь не предоставляет возвращаемое значение, оно оценивается ()
по умолчанию. Чтобы гарантировать, if
что тип выражения известен во время компиляции, каждая ветвь должна оценивать значение одного и того же типа. По этой причине else
ветвь фактически обязательна, если только другие ветви не оцениваются как ()
, потому что if
без else
всегда может оцениваться ()
по умолчанию. [10]
// Присваиваем my_variable какое-то значение, в зависимости от значения x let my_variable = if x > 20 { 1 } else { 2 }; // Этот вариант не скомпилируется, поскольку 1 и () имеют разные типы let my_variable = if x > 20 { 1 }; // Значения можно опускать, если они не нужны, если x > 20 { println! ( «x больше 20» ); }
Язык защищенных команд (GCL) Эдсгера Дейкстры поддерживает условное выполнение в виде списка команд, состоящего из логического ограничителя ( соответствующего условию ) и соответствующего ему оператора. В GCL оценивается ровно одно из утверждений, защита которых истинна, а какая является произвольной. В этом коде
если G0 → S0 □ G1 → S1... □ Gn → Snфи
Gi — это охранники, а Si — утверждения. Если ни одно из предупреждений не истинно, поведение программы не определено.
GCL предназначен в первую очередь для рассуждений о программах, но аналогичные обозначения были реализованы в Concurrent Pascal и occam .
До Fortran 77 в языке Fortran был арифметический оператор if , который переходит к одной из трех меток в зависимости от того, равен ли его аргумент e e < 0, e = 0, e > 0. Это был самый ранний условный оператор в Fortran. [11]
ЕСЛИ ( e ) метка1 , метка2 , метка3
Где e — любое числовое выражение (не обязательно целое число).
Это эквивалентно этой последовательности, где e вычисляется только один раз.
ЕСЛИ ( e.LT.0 ) ПЕРЕЙТИ к метке1 ЕСЛИ ( e.EQ.0 ) ПЕРЕЙТИ к метке2 ЕСЛИ ( e.GT.0 ) ПЕРЕЙТИ к метке3
Арифметика if является неструктурированным управляющим оператором и не используется в структурном программировании .
На практике было замечено, что большинство арифметических IF
операторов ссылаются на следующий оператор с одной или двумя метками.
Это был единственный оператор условного управления в исходной реализации Фортрана на компьютере IBM 704 . На этом компьютере код операции тестирования и перехода имел три адреса для этих трех состояний. Другие компьютеры будут иметь регистры «флагов», такие как положительный, нулевой, отрицательный, четный, переполнение, перенос, связанные с последними арифметическими операциями, и будут использовать такие инструкции, как «Переход, если аккумулятор отрицательный», затем «Переход, если аккумулятор ноль» или аналогичные. Обратите внимание, что выражение вычисляется только один раз , и в таких случаях, как целочисленная арифметика, когда может произойти переполнение, также будут учитываться флаги переполнения или переноса.
В отличие от других языков, в Smalltalk условный оператор не является языковой конструкцией , а определяется в классе Boolean
как абстрактный метод, который принимает два параметра, оба замыкания . Boolean
имеет два подкласса True
и False
, оба из которых определяют метод, True
выполняя только первое замыкание и False
выполняя только второе замыкание. [12]
var = условие ifTrue: [ 'foo' ] ifFalse: [ 'bar' ]
В JavaScript используются операторы if-else, аналогичные тем, которые используются в языках C. Логическое значение принимается в круглых скобках между зарезервированным ключевым словом if и левой фигурной скобкой.
если ( Math.random ( ) < 0,5 ) { console . log ( "У вас есть головы!" ); } Еще { консоль . log ( "У тебя Тейлз!" ); }
В приведенном выше примере используется условие, Math.random() < 0.5
которое выводит, true
если случайное значение с плавающей запятой от 0 до 1 больше 0,5. Оператор использует его для случайного выбора между выводом You got Heads!
или You got Tails!
консолью. Операторы else и else-if также могут быть соединены после фигурной скобки предыдущего оператора столько раз, сколько необходимо, как показано ниже:
вар х = Математика . случайный (); если ( х < 1/3 ) { console . log ( "Выиграл один человек!" ); } Еще если ( х < 2/3 ) { console . log ( "Выиграли два человека!" ); } Еще { консоль . log ( "Это тройная ничья!" ); }
В лямбда-исчислении концепция условия if-then-else может быть выражена с помощью следующих выражений:
истина = λx. λy. Иксложь = λx. λy. йifThenElse = (λc. λx. λy. (cxy))
примечание : если ifThenElse переданы две функции в виде левого и правого условных операторов; необходимо также передать пустой кортеж () в результат ifThenElse , чтобы фактически вызвать выбранную функцию, в противном случае ifThenElse просто вернет объект функции без вызова.
В системе, где числа могут использоваться без определения (например, Lisp, традиционная бумажная математика и т. д.), вышеизложенное можно выразить как одно замыкание ниже:
(( λtrue. λfalse. λifThenElse. ( ifThenElse true 2 3 ) )( λx. λy. x )( λx. λy. y )( λc. λl. λr. c l r ))
Здесь true, false и ifThenElse привязаны к соответствующим определениям, которые передаются в их область действия в конце их блока.
Рабочая аналогия JavaScript (для строгости с использованием только функций с одной переменной) выглядит следующим образом:
var ComputingResult = (( _true => _false => _ifThenElse => _ifThenElse ( _true )( 2 )( 3 ) )( x => y => x )( x => y => y )( c => x => y => c ( x )( y )));
Приведенный выше код с функциями с несколькими переменными выглядит следующим образом:
var вычислениеResult = (( _true , _false , _ifThenElse ) => _ifThenElse ( _true , 2 , 3 ) )(( x , y ) => x , ( x , y ) => y , ( c , x , y ) => с ( х , у ));
Другая версия предыдущего примера без системы, в которой предполагаются числа, приведена ниже.
В первом примере показано использование первой ветви, а во втором примере показано использование второй ветви.
(( λtrue. λfalse. λifThenElse. ( ifThenElse true ( λFirstBranch. FirstBranch ) ( λSecondBranch. SecondBranch )) )( λx. λy. x )( λx. λy. y )( λc. λl. λr. c l r )) (( λtrue. λfalse. λifThenElse. ( ifThenElse false ( λFirstBranch. FirstBranch ) ( λSecondBranch. SecondBranch )) )( λx. λy. x )( λx. λy. y )( λc. λl. λr. c l r ))
Smalltalk использует аналогичную идею для своих представлений true и false, при этом True и False являются одноэлементными объектами, которые по-разному реагируют на сообщения ifTrue/ifFalse.
Раньше Haskell использовал именно эту модель для своего логического типа, но на момент написания большинство программ Haskell использовали синтаксическую сахарную конструкцию «if a then b else c», которая, в отличие от ifThenElse, не компонуется, если она не обернута в другую функцию или не реализована повторно. как показано в разделе Haskell на этой странице.
Операторы переключения (в некоторых языках операторы case или многосторонние ветки) сравнивают заданное значение с указанными константами и выполняют действия в соответствии с первой совпадающей константой. Обычно предусмотрено действие по умолчанию («иначе», «иначе»), которое должно быть выполнено, если совпадение не удалось. Операторы переключения могут обеспечивать оптимизацию компилятора , например таблицы поиска . В динамических языках регистры могут не ограничиваться постоянными выражениями и могут распространяться на сопоставление с образцом , как в примере сценария оболочки справа, где '*)' реализует регистр по умолчанию как регулярное выражение, соответствующее любой строке.
Сопоставление с образцом можно рассматривать как альтернативу операторам if-then-else и case . Он доступен на многих языках программирования с функциями функционального программирования, таких как Wolfram Language , ML и многих других. Вот простой пример, написанный на языке OCaml :
сочетать фрукты с | "яблоко" -> приготовить пирог | "кокос" -> приготовить данго_моти | "банан" -> микс ;;
Сила сопоставления шаблонов заключается в способности кратко сопоставлять не только действия, но и значения с шаблонами данных. Вот пример, написанный на Haskell , который иллюстрирует обе эти функции:
карта _ [] = [] карта ж ( час : т ) знак равно ж час : карта ж т
Этот код определяет карту функции , которая применяет первый аргумент (функцию) к каждому из элементов второго аргумента (списка) и возвращает результирующий список. Две строки — это два определения функции для двух типов аргументов, возможных в этом случае: один, когда список пуст (просто возвращает пустой список), и другой случай, когда список не пуст.
Сопоставление с образцом, строго говоря, не всегда является конструкцией выбора, поскольку в Haskell можно написать только одну альтернативу, которая гарантированно всегда будет сопоставляться – в этой ситуации она используется не как конструкция выбора, а просто как способ для привязки имен к значениям. Однако он часто используется в качестве конструкции выбора на языках, на которых он доступен.
В языках программирования, которые имеют ассоциативные массивы или сопоставимые структуры данных, таких как Python , Perl , PHP или Objective-C , идиоматично использовать их для реализации условного присваивания. [13]
pet = input ( «Введите тип питомца, которого вы хотите назвать:» ) known_pets = { «Собака» : «Фидо» , «Кошка» : «Мяуслз» , «Птица» : «Твити» , } my_name = известные_питомцы [ домашнее животное ]
В языках, которые имеют анонимные функции или которые позволяют программисту присваивать именованную функцию ссылке на переменную, условный поток может быть реализован с использованием хеша в качестве таблицы диспетчеризации .
Альтернативой инструкциям условного перехода является предикация . Предикация — это архитектурная особенность, которая позволяет выполнять инструкции в определенных условиях вместо изменения потока управления .
Эта таблица относится к самой последней языковой спецификации каждого языка. Для языков, не имеющих спецификации, используется последняя официально выпущенная реализация.
else if
case
В конструкции Ruby сопоставление регулярных выражений входит в число доступных альтернатив условного управления потоком. Пример см. в этом вопросе о переполнении стека.CASE
CASE WHEN cond1 THEN expr1 WHEN cond2 THEN expr2 [...] ELSE exprDflt END
if ... else if ... else
CASE
CASE expr WHEN val1 THEN expr1 [...] ELSE exprDflt END
if
в Фортране 90 устарела.