Тернарна умовна операція

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до навігації Перейти до пошуку

Тернарна умовна операція (від лат. ternarius — «потрійний») (зазвичай записується як ?:) — в багатьох мовах програмування операція, яка повертає свій другий або третій операнд в залежності від значення логічного виразу, заданого першим операндом. Як можна судити із назви, тернарна операція приймає всього три вказаних операнда. Аналогом тернарної умовної операції в математичній логіці і булевій алгебрі є умовна диз'юнкція, яка записується у вигляді [p, q, r] і реалізує алгоритм: «Якщо p, то q, інакше r», що можна переписати як «q або r, в залежності від p або не p».

Зазвичай тернарна умовна операція асоціюється з операцією ?:, яка використовується в сі-подібних мовах програмування. Насправді, подібні операції з іншим синтаксисом є і в багатьох далеких по синтаксису від Сі мовах програмування. До найбільш популярних мов, що містять тернарну умовну операцію, можна віднести C, C++, JavaScript, Swift, Objective-C, C#, D, Java, ECMAScript, Perl, PHP, Python,Tcl, Ruby, Verilog, Turbo Basic та інші. Своєю появою безпосередньо в тернарній формі ця операція зобов'язана мові Алгол-60, в якому вона мала синтаксис if o1 then o2 else o3 і потім мови BCPL (o1 -> o2, o3)[1] замість звичного тепер o1 ? o2 : o3. Прототипом цієї операції, в свою чергу, є умовна функція cond мови Лісп, яка записується за правилами Ліспа в префіксній формі і має довільну кількість аргументів.

Визначення[ред. | ред. код]

Безвідносно до певної мови програмування тернарну операцію можна визначити так:

логічний вираз ? вираз 1 : вираз 2

Алгоритм роботи операції наступний:

  1. Обчислюється логічний вираз.
  2. Якщо логічний вираз істинний, то обчислюється значення виразу вираз 1, в іншому випадку — значення виразу вираз 2.
  3. Обчислене значення повертається.

Потрібно звернути увагу, що обчислюється тільки одне з виразів: вираз 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++[ред. | ред. код]

В 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.

Python[ред. | ред. код]

a = 42
b = 41
result = a if a > b else b
assert result == 42

Також можна реалізувати через список:

[<вираз 1>, <вираз 2>][<умова>]

Повернеться результат виразу 1, якщо умова хибна; і виразу 2, якщо умова істинна. Якщо умова не буде булевими виразом, можливий вихід за межі списку із виключенням.

PHP[ред. | ред. код]

 $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';

JavaScript[ред. | ред. код]

var a = 1==0 ? "first value" : 
        2==0 ? "second value" :
        3==3 ? "result value" : "default value"

Ruby[ред. | ред. код]

Загальний синтаксис аналогічний C-подібним мовам.

print true ? "true" : "false" # Виведе true в стандартний вивід

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)
    
    'Змінна iCount буде містити "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]

Turbo Basic[ред. | ред. код]

Синтаксис[6]: IF logic_expression [<> 0] [,] THEN statement(s) [ELSE statement(s)]

Будь-який результат logic_expression не рівний 0 вважається %FALSE, але не %TRUE, рівній тільки -1. logic_expression може бути числовим (numeric), так і символьним (string). У разі символьного виразу обчислення проводяться з ASCII-кодами символів.

%TRUE = -1
%FALSE = 0
A$ = "M"
B$ = "N"
C! = 43
D# = 44
IF A$>B$ <> %FALSE, THEN RESULT# = C! ELSE RESULT# = D#
PRINT RESULT#

За допомогою функції FN IfThenElse(X1,X2,X3), замість інфіксного виду тернарного оператора If Then X1 X2 Else X3 можна користуватися префіксним видом тернарного оператора IfThenElse:

A$ = "M"
B$ = "N"
C! = 43
D# = 44

COND = A$<B$   'COND = любое logic_expression
PRINT "FN IfThenElse(X1,X2,X3) =";FN IfThenElse(COND,C!,D#) 
END

DEF FN IfThenElse(X1,X2,X3) 
  IF X1 <> 0 THEN FN IfThenElse = X2 ELSE FN IfThenElse = X3
END DEF

Примітки[ред. | ред. код]

  1. BCPL Ternary operator (page 15). BCPL Reference Manual. Архів оригіналу за 31 березня 2012. Процитовано 28 вересня 2017. 
  2. Ю. Ю. Громов, С. И. Татаренко. {{{Заголовок}}}.
  3. Б. Страуструп. {{{Заголовок}}}.
  4. Оператор ?: (C#) // https://msdn.microsoft.com/ru-ru/library/ty67wk28.aspx
  5. Оператор If (Visual Basic) // https://msdn.microsoft.com/ru-ru/library/bb513985.aspx
  6. Borland Turbo BASIC Owners Handbook 1987