Условный тернарный оператор
Условный тернарный оператор позволяет описать в едином выражении два варианта вычислений в зависимости от некоторого условия. Синтаксис оператора такой:
condition ? expression_true : expression_false |
Логические условие должно быть указано в первом операнде condition. Это может быть произвольное сочетание операций сравнения и логических операций. Обе ветви обязательно должны присутствовать.
Если условие истинно, будет вычисляться выражение expression_true, а если ложно — expression_false.
Данный оператор гарантирует, что только одно из выражений expression_true и expression_false выполнится.
Типы двух выражений должны быть одинаковыми, в противном случае будет предпринята попытка их неявного приведения к общему типу.
Обратите внимание, что результат обработки выражений в MQL5 всегда представляет собой RValue (в C++, если в выражениях стоят только LValue, то и результатом оператора также будет LValue). Так, следующий код компилируется в C++, но выдает ошибку в MQL5:
int x1, y1; ++(x1 > y1 ? x1 : y1); // '++' - l-value required |
Условные операторы могут быть вложенными, то есть в качестве условия или любой из веток (expression_true и expression_false) разрешено использовать другой условный оператор. При этом визуально может быть не всегда понятно, к чему относятся условия (если не используются круглые скобки для явного обозначения группировки). Рассмотрим примеры из ExprConditional.mq5.
int x = 1, y = 2, z = 3, p = 4, q = 5, f = 6, h = 7;
|
В данном случае первое логическое условие представляет собой сравнение x > y. При его истинности выполняется ветвь с переменной z. При его ложности проверяется дополнительное логическое условие p != 0 && q != 0, также с двумя вариантами выражений.
Ниже приведено еще несколько операторов, в которых заглавными буквами обозначены логические условия, а строчными — варианты расчета. Здесь для простоты сделано, что все они — переменные (из примера выше). В реальности каждая из трех компонент бывает более витиеватым выражением.
Вы можете проследить для каждой строки, как получается результат, показанный в комментарии.
bool A = false, B = false, C = true;
|
Поскольку оператор имеет свойство правой ассоциативности, составное выражение разбирается справа налево, то есть самая правая конструкция с тремя операндами, объединенными символами '?' и ':', становится операндом внешнего условия, которое записано левее. Далее с учетом этой "подмены", выражение снова разбирается справа налево и так далее, пока не получится последняя полная конструкция '?:' верхнего уровня.
Поэтому вышеприведенные выражения разбиваются на группы следующим образом (скобками обозначена неявная интерпретация компилятора, но такие скобки могли бы быть добавлены в выражения для наглядности исходного кода, и такой подход рекомендуется).
int r0 = x > y ? z : ((p != 0 && q != 0) ? f / (p + q) : h);
|
Для переменной r5 первое условие A ? f : h вычисляет логическое условие для последующего выражения, поэтому преобразуется в bool. Поскольку A равно false, значение берется из переменной h. Она не равна 0, и потому первое условие считается истинным. В результате срабатывает ветка (B ? x : y), из которой возвращается значение переменной y, так как B равно false.
В операторе обязательно должны присутствовать все 3 компонента (условие и 2 варианта), в противном случае компилятор сгенерирует ошибку "неожиданный токен" ("unexpected token"):
// ';' - unexpected token
|
"Токен" на языке компилятора — это неделимый фрагмент исходного кода с самостоятельным смыслом или назначением (название типа, идентификатор, символ пунктуации и т.д.). Весь исходный код разбивается компилятором на последовательность токенов. Знаки рассматриваемых операторов тоже токены. В вышеприведенном коде имеется два символа '?' и должно быть два парных им символа ':', однако он только один. Поэтому компилятор "говорит", что символ окончания инструкции ';' преждевременный, и "уточняет", чего именно не хватает: "ожидалось двоеточие" ("colon sign expected").
Из-за того, что условный оператор имеет очень низкий приоритет (13 в полной таблице, см. Приоритеты операций), рекомендуется заключать его в скобки. Так проще избежать ситуаций, когда операнды условного оператора могли бы быть "захвачены" соседними операциями с более высоким приоритетом. Например, если требуется рассчитать значение некоторой переменной w через сумму двух тернарных операторов, прямолинейный подход мог бы выглядеть так:
int w = A ? f : h + B ? x : y; // 1 |
Это будет работать иначе, чем задумывалось. Сумма h + B из-за более высокого приоритета рассматривается как единое выражение. Учитывая разбор справа налево, эта сумма оказывается в роли условия и приводится к типу bool, о чем компилятор даже выдает предупреждение "выражение не является логическим" ("expression not boolean"). Трактовку компилятора можно для наглядности выделить скобками:
int w = A ? f : ((h + B) ? x : y); // 1 |
Для решения проблемы необходимо расставить скобки по-своему.
int v = (A ? f : h) + (B ? x : y); // 9 |
Глубокая вложенность условных операторов негативно сказывается на понятности кода. Следует избегать уровней вложенности больше двух-трех.