Тернарная условная операция: различия между версиями
[непроверенная версия] | [непроверенная версия] |
Нет описания правки |
|||
Строка 51: | Строка 51: | ||
</source> |
</source> |
||
Как известно, в Си нет [[Логический тип|логического типа данных]] (в [[ |
Как известно, в Си нет [[Логический тип|логического типа данных]] (в [[C99]] появился логический тип _Bool). Поэтому операнд <code>o1</code> должен быть числом ([[Целый тип|целым]] или [[Числа с плавающей запятой|вещественным]]) или [[Указатель (тип данных)|указателем]]. Сначала вычисляется именно его значение. Оно сравнивается с [[0 (число)|нулём]] и, если оно не равно нулю, вычисляется и возвращается <code>o2</code>, в случае равенства — <code>o3</code>. Операнды <code>o2</code> и <code>o3</code> могут быть различных, вообще говоря, несовпадающих типов, включая [[void]]. |
||
В следующем примере вычисляется минимальное из чисел a и b: |
В следующем примере вычисляется минимальное из чисел a и b: |
Версия от 19:23, 21 августа 2015
Терна́рная усло́вная опера́ция (от лат. ternarius — «тройной») (обычно записывается как ?:
) — во многих языках программирования операция, возвращающая свой второй или третий операнд в зависимости от значения логического выражения, заданного первым операндом. Как можно судить из названия, тернарная операция принимает всего три указанных операнда. Аналогом тернарной условной операции в математической логике и булевой алгебре является условная дизъюнкция, которая записывается в виде [p, q, r] и реализует алгоритм: «Если q, то p, иначе r», что можно переписать как «p или r, в зависимости от q или не q».
Обычно тернарная условная операция ассоциируется с операцией ?:
, используемой в си-подобных языках программирования. На самом деле, подобные операции с другим синтаксисом имеются и во многих далёких по синтаксису от Си языках программирования. К наиболее популярным языкам, содержащим тернарную условную операцию, можно отнести C, C++, JavaScript, Objective-C, C#, D, Java, ECMAScript, Perl, PHP, Python,Tcl, Ruby, Verilog и другие. Своим появлением непосредственно в тернарной инфиксной форме эта операция обязана языку Алгол-60, в котором она имела синтаксис if o1 then o2 else o3
и затем языку BCPL (o1 -> o2, o3
)[1] вместо привычного теперь o1 ? o2 : o3
. Прототипом же этой операции, в свою очередь, является условная функция cond
языка Лисп, записываемая по правилам Лиспа в префиксной форме и имеющая произвольное количество аргументов.
Определение
Безотносительно к определённому языку программирования тернарную операцию можно определить так:
логическое выражение ? выражение 1 : выражение 2
Алгоритм работы операции следующий:
- Вычисляется
логическое выражение
. - Если
логическое выражение
истинно, то вычисляется значение выражениявыражение 1
, в противном случае — значение выражениявыражение 2
. - Вычисленное значение возвращается.
Нужно обратить внимание, что вычисляется только одно из выражений: выражение 1
или выражение 2
. Это соответствует принципу ленивых вычислений, и сделано не столько для оптимизации, сколько для расширения возможностей: так, выражение x < 0 ? 0 : sqrt(x)
абсолютно корректно, несмотря на то, что из отрицательных чисел корень не берётся.
Использование и реализации
Тернарная условная операция используется в выражениях для получения одного из двух вариантов в зависимости от условия.
alarm_time = today in [SUNDAY, MONDAY] ? 12.00 : 8.00
В этом примере условному программируемому электронному будильнику проставляется время, в которое он должен звонить, в зависимости от текущего дня недели. Нужно заметить, что пример снова приведён для некоторого абстрактного алгоритмического языка программирования.
В следующем примере вычисляется значение простейшего дельта-символа.
y = x == 0 ? 1 : 0
В следующем примере данная операция использована в ситуации, не связанной с присваиванием:
sprintf(
Title,
"%s %s",
tv_system == TV_PAL ?
"PAL" :
"SECAM",
tv_input ?
Tv_Name[ tv_input - 1 ]:
"TEST"
);
В данном случае эквивалентная конструкция с использованием if-then-else потребовала бы записи вызова функции sprintf четыре раза. Либо, в качестве альтернативы, потребовалось бы написать аналогичный по назначению (но формально не эквивалентный) код с использованием двух дополнительных временных переменных либо нескольких последовательных вызовов sprintf.
В Си тернарная операция имеет следующий синтаксис:[2]
o1 ? o2 : o3
Как известно, в Си нет логического типа данных (в C99 появился логический тип _Bool). Поэтому операнд o1
должен быть числом (целым или вещественным) или указателем. Сначала вычисляется именно его значение. Оно сравнивается с нулём и, если оно не равно нулю, вычисляется и возвращается o2
, в случае равенства — o3
. Операнды o2
и o3
могут быть различных, вообще говоря, несовпадающих типов, включая void.
В следующем примере вычисляется минимальное из чисел a и b:
min = (a < b) ? a : b;
В C++ тернарная условная операция имеет тот же синтаксис, что и в Си.[3] Однако за счёт наличия разницы между инициализацией и присваиванием, бывают ситуации, когда операцию ?:
нельзя заменить конструкцией if-then-else
, как, например, в следующем случае:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(int argc, char** argv)
{
string name;
ofstream fout;
if (argc > 1 && argv[1])
{
name = argv[1];
fout.open(name.c_str(), ios::out | ios::app);
}
ostream& sout = name.empty() ? cout : fout;
return 0;
}
Здесь переменная sout
инициализируется в момент объявления результатом работы тернарной операции. Подобного эффекта не удалось бы достичь простым присваиванием в том или ином случае.
Кроме того, тернарная условная операция может быть применена в левой части оператора присвоения:
0. #include <iostream>
1. int main ()
2. {
3. int a=0, b=0;
4.
5. const bool cond = ...;
6. (cond ? a : b) = 1;
7. std::cout << "a=" << a << ','
8. << "b=" << b << '\n';
9. }
В этом примере, если логическая переменная cond в строке 5 будет содержать значение true, то значение 1 будет присвоено переменной a, иначе, оно будет присвоено переменной b.
b = 3
a = 1 if b==1 else \
2 if b==2 else \
3
assert a==3
Также можно реализовать через список:
[<выражение 1>, <выражение 2>][<условие>]
Будет возвращен результат выражения 1, если условие ложно; и выражения 2, если условие истинно. Если условие будет не булевым выражением, возможен выход за границы списка с исключением.
$a = 1==0 ? "first value" :
(2==0 ? "second value" :
(3==3 ? "result value" : "default value"));
Тернарный оператор в PHP эквивалентен более длинной конструкции if - else. Следующие два примера эквивалентны:
//Первый пример
$result = isset($a) ? $a : 'DefaultValue';
//Второй пример
if (isset($a)) {
$result = $a;
} else {
$result = 'DefaultValue';
}
Такие конструкции часто применяются, чтобы в любом случае проинициализировать переменную для последующих вычислений (иначе PHP выдаст ошибку уровня E_NOTICE).
Начиная с версии 5.3 появилась возможность не указывать второй параметр операции. Например, две следующих записи эквивалентны:
$Variable = $_GET['Parameter'] ? $_GET['Parameter'] : 'DefaultValue';
$Variable = $_GET['Parameter'] ?: 'DefaultValue';
var a = 1==0 ? "first value" :
2==0 ? "second value" :
3==3 ? "result value" : "default value"
Ruby
print true ? "true" : "false"
C#
На тернарную операцию накладываются дополнительные ограничения, связанные с типобезопасностью. Выражения 1 и 2 должны быть одного типа. Это приводит к следующему:
int a = 1;
double b = 0.0;
int nMax = (a>b) ? a : b;
Такой исходный код не будет компилироваться несмотря на то, что в конечном итоге значение nMax будет равно а. Поскольку a и b должны быть одного и того же типа, a повысится до double, чтобы соответствовать b. Тип результирующего значения тернарной операции оказывается double, и этот тип должен быть понижен до int при присваивании:[4]
int a = 1;
double b = 0.0;
int nMax;
// Можно поступить так:
nMax = (int) ((a>b) ? a : b) ;
// ...или так
nMax = (a>b) ? a : (int)b;
Visual Basic
В классической версии языка существует тернарный оператор в виде функции IIf(Expr, TruePart, FalsePart)
. Данная функция имеет определенную особенность, которая заключается в том, что при оценке выражения Expr
, также будут вычисляться TruePart
и FalsePart
, вне зависимости от результата выражения: истинно оно или ложно. Это может привести к неожиданным результатам, а иногда и к замедлению выполнения кода, если в качестве возвращаемых значений будет вызов функций с длительными операциями.
Dim iCount As Long
Public Sub Main()
iCount = 1
MsgBox IIf(1 = 1, FuncYes, FuncNo)
'Переменная N будет содержать "3", т.к. обе функции будут выполнены
MsgBox iCount
End Sub
Public Function FuncYes() As String
iCount = iCount + 1
FuncYes = "Да"
End Function
Public Function FuncNo() As String
iCount = iCount + 1
FuncNo = "Нет"
End Function
Для замены функции IIf
можно переписать выражение в одну строку, но это не будет являться аналогом функции, а будет всего лишь краткая форма записи оператора ветвления
If Expr Then TruePart Else FalsePart
С появлением VB.NET, в синтаксис языка был включен привычный тернарный оператор и записывается как If(Expr, TruePart, FalsePart)
. Данный оператор использует сокращенные вычисления, в отличии от функции IIf
, которая также для совместимости с прошлыми версиями доступна разработчику.[5]
Примечания
- ↑ BCPL Ternary operator (page 15) . BCPL Reference Manual. Архивировано 31 марта 2012 года.
- ↑ Ю. Ю. Громов, С. И. Татаренко. 1.3.12. Условная операция // Программирование на языке си / Рецензент: профессор А. П. Афанасьев.
- ↑ Б. Страуструп. 7.13. Условная операция // Справочное руководство по C++.
- ↑ Оператор ?: (C#) // https://msdn.microsoft.com/ru-ru/library/ty67wk28.aspx
- ↑ Оператор If (Visual Basic) // https://msdn.microsoft.com/ru-ru/library/bb513985.aspx
Литература
Стефан Рэнди Дэвис, Чак Сфер. Глава 4. Операторы // C# 2005 для "чайников" = C# 2005 for dummies / под редакцией Т. Г. Сковородниковой. — М.-Спб.: Wiley, Диалектика, 2006. — С. 83. — ISBN 5-8459-1068-4.