В тройной или нет в тройной? [закрыто]

186

Я лично сторонник троичного оператора: ()? :; Я понимаю, что оно имеет свое место, но я сталкивался со многими программистами, которые категорически против того, чтобы когда-либо использовать его, и некоторыми, которые используют его слишком часто.

Каковы ваши чувства по этому поводу? Какой интересный код вы видели, используя его?

пикселей
источник
22
Сторонник чего-либо - это человек, который поддерживает это.
Даг МакКлин
8
Используйте это, когда это ясно, избегайте этого, когда это смущает. Это решение суда. Это может сделать код более читабельным, но только для простых выражений. Попытка всегда использовать это - такая же угроза, как и неуклонное ее избегание.
Авель
3
На самом деле, это условный оператор. Почти повторяющийся вопрос - stackoverflow.com/questions/725973/… .
Даниэль Даранас
Я иногда использовал, x = x if x else yно потом спросил об этом и понял, с помощью других, что это действительно просто уменьшает до x = x или y ( stackoverflow.com/questions/18199381/self-referencing-ternary/… )
Scruffy
4
Подобные вопросы не следует считать неконструктивными.
Ungeheuer

Ответы:

243

Используйте его только для простых выражений :

int a = (b > 10) ? c : d;

Не цепляйте и не вкладывайте троичные операторы, так как их трудно читать и сбивать с толку:

int a = b > 10 ? c < 20 ? 50 : 80 : e == 2 ? 4 : 8;

Кроме того, при использовании троичного оператора рассмотрите возможность форматирования кода таким образом, чтобы улучшить читаемость:

int a = (b > 10) ? some_value                 
                 : another_value;
marcospereira
источник
81
Полностью согласен с первыми несколькими утверждениями, но полностью не согласен с вашим примером «улучшенной читаемости». Если вы собираетесь использовать несколько строк, почему бы просто не использовать оператор if?
Джо Филлипс
3
просто потому, что если иное достаточно много для простых решений: int a = 0; if (b> 10) a = some_value; иначе a = another_value; Что Вы предпочитаете?
Marcospereira
44
@ d03boy: Потому что if-Statement - это просто утверждение, и не будет делать, когда все, что вы хотите, это выражение.
Фальстро
2
@roe в некоторых языках if является выражением (например, в Scala), поэтому val x = if(true) 0 else 1вполне законно
om-nom-nom
5
@ om-nom-nom в данном случае это сделало бы его выражением if, а не выражением if, и по сути то же самое, что и оператор?: -.
Фальстро
141

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

оборота Дэвид Сегондс
источник
44
Это лучший аргумент против троичного оператора, которого я когда-либо слышал. Я не покупаю аргумент «не читаемый» (мне кажется, что люди слишком ленивы, чтобы привыкнуть к нему), но на самом деле это имеет смысл.
EpsilonVector
80

Я люблю их, особенно на типобезопасных языках.

Я не вижу, как это:

int count = (condition) ? 1 : 0;

это сложнее, чем это:

int count;

if (condition)
{
  count = 1;
} 
else
{
  count = 0;
}

редактировать -

Я бы сказал, что троичные операторы делают все менее сложным и аккуратным, чем альтернативные.

Ян П
источник
5
Тройная инициализация еще более полезна в D или C ++, когда переменная постоянна. например, const int count = ...;
deft_code
Ну, ты вроде как искажаешь if/elseненужные скобки там.
Бобобобо
1
Кроме того , в этом случае , если conditionэто bool можно просто сделать int count = condition;
bobobobo
1
@bobobobo это if/elseс фигурными скобками, как большинство программистов переписать троичный ..
Andre Figueiredo
1
@bobobobo, если / иначе без скобок просто напрашивается на неприятности. Для кого-то слишком легко добавить строку, но забудьте, что тогда ему понадобятся фигурные скобки, чтобы сделать то, что они ожидают (выполнить дополнительную строку как часть блока): stackoverflow.com/a/381274/377225
Джордж Мариан
43

Я прикован цепью - вложенный, не так уж и много.

Я склонен использовать их больше в C, просто b / c, это оператор if, который имеет значение, поэтому он сокращает ненужные повторения или переменные:

x = (y < 100) ? "dog" :
    (y < 150) ? "cat" :
    (y < 300) ? "bar" : "baz";

скорее, чем

     if (y < 100) { x = "dog"; } 
else if (y < 150) { x = "cat"; }
else if (y < 300) { x = "bar"; } 
else              { x = "baz"; }

В подобных заданиях я считаю, что это меньше для рефакторинга и яснее.

С другой стороны, когда я работаю в ruby, я чаще использую if...else...endего, потому что это тоже выражение.

x =   if (y < 100) then "dog"
    elif (y < 150) then "cat"
    elif (y < 300) then "bar"
    else                "baz"
    end

(хотя, по общему признанию, для чего-то такого простого, я мог бы в любом случае просто использовать троичный оператор).

колокольчик-рапунцель
источник
2
Мне нравится твой первый пример - я не думал о том, чтобы связать их так раньше. Спасибо, что поделился. =)
Эрик Форбс
5
+1 Я на самом деле недавно начал это делать. Я даже делаю это, чтобы заменить операторы switch.
Davy8
1
Отличный пример @rampion.
Иллюминат
39

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

Но есть много ситуаций, когда консервативное использование ?:оператора может привести к коду, который на самом деле легче читать, чем иначе. Например:

int compareTo(Object object) {
    if((isLessThan(object) && reverseOrder) || (isGreaterThan(object) && !reverseOrder)) {
       return 1;
    if((isLessThan(object) && !reverseOrder) || (isGreaterThan(object) && reverseOrder)) {
       return -1;
    else
      return 0;              
}

Теперь сравните это с этим:

int compareTo(Object object) {
    if(isLessThan(object))
        return reverseOrder ? 1 : -1;         
    else(isGreaterThan(object))
        return reverseOrder ? -1 : 1;
    else        
       return 0;              
}

Поскольку код более компактен, в нем меньше синтаксического шума, и при разумном использовании троичного оператора (то есть только в связи со свойством reverseOrder ) конечный результат не особенно лаконичен.

Ryan Delucchi
источник
я бы все еще выступал за использование похвал на каждой конструкции if / then / else, которая не является троичной, поэтому в вашем втором примере не хватает нескольких imho.
Крис
Да, это функционально. Это как крошечная функция, которая имеет один логический аргумент и возвращает любой тип, который вы хотите! На самом деле аккуратный оператор.
Бобобобо
24

На самом деле это вопрос стиля; подсознательные правила, которым я склонен следовать:

  • Оцените только 1 выражение - так foo = (bar > baz) ? true : false, но НЕfoo = (bar > baz && lotto && someArray.Contains(someValue)) ? true : false
  • Если я использую его для отображения логики, например, <%= (foo) ? "Yes" : "No" %>
  • Только реально используйте это для назначения; никогда не течь логика (так никогда (foo) ? FooIsTrue(foo) : FooIsALie(foo)) Логика потока в троице сама по себе является ложью, игнорируйте эту последнюю точку.

Мне это нравится, потому что оно лаконично и элегантно для простых операций присваивания.

Кейт Уильямс
источник
хорошие рекомендации и хорошие примеры!
Nickf
Я думаю, что большинство языков не позволят вам использовать его для логики потока, поэтому вы не можете сделать это (foo)? True (foo): False (foo); если это не было заданием.
Генри Б.
Ой, да ты прав, мой плохой.
Кит Уильямс
21
Ваши два первых примера действительно плохие. Результаты сравнения уже являются логическими значениями, поэтому ваши троичные операторы бесполезны и только усложняют код.
Trillian
1
@Trillian +1 Да, должен был пойти с другим заданием. foo = (bar > baz);намного проще
Эрик
18

Как и многие вопросы мнения, ответ неизбежен: это зависит

Для чего-то вроде:

return x ? "Yes" : "No";

Я думаю, что это намного более кратко (и быстрее для меня разобрать) чем:

if (x) {
    return "Yes";
} else {
    return "No";
}

Теперь, если ваше условное выражение сложное, тогда троичная операция не является хорошим выбором. Что-то вроде:

x && y && z >= 10 && s.Length == 0 || !foo

не является хорошим кандидатом на троичный оператор.

Кроме того, если вы программист на C, GCC на самом деле имеет расширение, которое позволяет вам исключить часть троицы if-true, например:

/* 'y' is a char * */
const char *x = y ? : "Not set";

Который будет установлен xв yпредположении, yчто нет NULL. Хорошая вещь.

Шон Брайт
источник
Исправлена ​​небольшая проблема синтаксиса и грамматики, Шон :-) Отсутствует y в последнем бите кода, и «назначить x для y» означает «y = x», поэтому я chgd, чтобы «установить x в y».
paxdiablo
@Pax: Спасибо! Я откатил изменение синтаксиса, так как пытался указать, что с расширениями GCC вам не нужна часть троицы if-true.
Шон Брайт
Извините, не видел этот абзац. Не знаю, согласен ли я с такими вещами, потому что это позволяет людям писать код, который не будет компилироваться стандартным компилятором ISO. Тем не менее, когда GCC - последний стоящий человек, это не имеет значения :-)
paxdiablo
Это вуду, точно ... А кто не использует GCC? : D
Шон Брайт
14

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

В других случаях кажется, что троичный оператор уменьшает ясность.

Джон Малдер
источник
проблема в том, что язык составляет 99%, выражение может быть заменено функцией ... и ppl, избегая троичного оператора, даже предпочтет это решение.
PierreBdR
11

По мере цикломатической сложности использование ifоператоров или троичного оператора эквивалентно. Таким образом, ответ на этот вопрос - нет , сложность будет точно такой же, как и раньше.

С помощью других мер, таких как удобочитаемость, ремонтопригодность и СУХОЙ (не повторяй себя), любой выбор может оказаться лучше, чем другой.

Грег Хьюгилл
источник
10

Я использую его довольно часто в местах, где я вынужден работать в конструкторе - например, в новых конструкциях .NET 3.5 LINQ to XML - для определения значений по умолчанию, когда необязательный параметр имеет значение null.

Придуманный пример:

var e = new XElement("Something",
    param == null ? new XElement("Value", "Default")
                  : new XElement("Value", param.ToString())
);

или (спасибо звездочка)

var e = new XElement("Something",
    new XElement("Value",
        param == null ? "Default"
                      : param.ToString()
    )
);

Независимо от того, используете ли вы троичный оператор или нет, важно убедиться, что ваш код читабелен. Любая конструкция может быть сделана нечитаемой.

Эрик Форбс
источник
Или ... var e = new XElement ("Что-то", новый XElement ("значение", param == null? "По умолчанию": param.toString ()));
астерит
2
Мне нравится, что вы форматируете его для удобства чтения, поэтому многие этого не делают.
bruceatk
Что хорошего в удобочитаемом исходном коде, если он не читается человеком? =)
Эрик Форбс
9

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

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

оборота илилитрит
источник
1
Я полностью согласен. В этом нет ничего скрытого или хитрого.
mmattax
2
Это может вызвать проблемы с читаемостью, особенно когда они вложены.
Дэвид Торнли
Я думаю, что « чрезвычайно трудно читать» слишком уместно, но в целом я согласен с вами. В этом нет ничего сложного или мистического.
EpsilonVector
7

Я согласен с jmulder: он не должен использоваться вместо a if, но у него есть место для выражения возврата или внутри выражения:

echo "Result: " + n + " meter" + (n != 1 ? "s" : "");
return a == null ? "null" : a;

Первый - только пример, лучше использовать поддержку множественного числа в i18n!

PhiLho
источник
Первая троичная неверна - у вас есть: где? должен идти: (n! = 1 ? "s": "")
Эрик Форбс
Да, спасибо за указание на это! Исправлена.
PhiLho
7

(Хак дня)

#define IF(x) x ?
#define ELSE :

Тогда вы можете сделать if-then-else как выражение:

int b = IF(condition1)    res1
        ELSE IF(condition2)  res2
        ELSE IF(conditions3) res3
        ELSE res4;
John John
источник
это не отвечает на вопрос
Bruno Alexandre Rosa
6

Если вы используете троичный оператор для простого условного присваивания, я думаю, это нормально. Я видел, как он (ab) контролировал ход программы, даже не делая назначения, и я думаю, что этого следует избегать. Используйте оператор if в этих случаях.

Билл Ящерица
источник
5

Я думаю, что троичный оператор должен использоваться при необходимости. Это, очевидно, очень субъективный выбор, но я считаю, что простое выражение (особенно как выражение возврата) гораздо яснее, чем полный тест. Пример на C / C ++:

return (a>0)?a:0;

По сравнению с:

if(a>0) return a;
else return 0;

У вас также есть случай, когда решение находится между троичным оператором и созданием функции. Например в Python:

l = [ i if i > 0 else 0 for i in lst ]

Альтернатива:

def cap(value):
    if value > 0:
        return value
    return 0
l = [ cap(i) for i in lst ]

Это необходимо для того, чтобы в Python (в качестве примера) такую ​​идиому можно было регулярно видеть:

l = [ ((i>0 and [i]) or [0])[0] for i in lst ]

эта строка использует свойства логических операторов в Python: они ленивы и возвращают последнее вычисленное значение, если оно равно конечному состоянию.

PierreBdR
источник
3
Эта последняя строка ранит мой мозг ...
Эрик Форбс
5

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

JimDaniel
источник
5

Я видел таких зверей, как (на самом деле это было намного хуже, так как это isValidDate и проверял месяц и день, но я не мог потрудиться, пытаясь запомнить все это):

isLeapYear =
    ((yyyy % 400) == 0)
    ? 1
    : ((yyyy % 100) == 0)
        ? 0
        : ((yyyy % 4) == 0)
            ? 1
            : 0;

где, конечно, серия if-операторов была бы лучше (хотя эта все же лучше, чем макро-версия, которую я когда-то видел).

Я не возражаю против таких мелочей, как:

reportedAge = (isFemale && (Age >= 21)) ? 21 + (Age - 21) / 3 : Age;

или даже немного хитрые вещи, как:

printf ("Deleted %d file%s\n", n, (n == 1) ? "" : "s");
paxdiablo
источник
Как вы можете сказать серию изолирования, если заявления будут более читабельными? Я читаю вашего "зверя" просто отлично . reportedAgeэто пример, который требует некоторого выяснения - возможно, потому что это скорее частный случай, чем выяснениеisThisYearALeapYear
Axeman
4

Ну, синтаксис для него ужасен. Я нахожу функционал ifs очень полезным и часто делает код более читабельным.

Я бы предложил сделать макрос, чтобы сделать его более читабельным, но я уверен, что кто-то может придумать ужасный крайний случай (как всегда есть с CPP).

Marcin
источник
многие реализации и варианты BASIC имеют функцию IF, которая заменяет троичный оператор. Я видел несколько кодовых баз с этим определенным как макрос в C.
Sparr
Ну, я думал о функциональных языках программирования, но да.
Марчин
«Делая макрос, чтобы сделать его более читабельным», вы настоящий шутник!
niXar
4

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

int result = do_something();
if( result != 0 )
{
  debug_printf("Error while doing something, code %x (%s)\n", result,
                result == 7 ? "ERROR_YES" :
                result == 8 ? "ERROR_NO" :
                result == 9 ? "ERROR_FILE_NOT_FOUND" :
                "Unknown");
}
Indiv
источник
4

Я почти никогда не использую троичный оператор, потому что всякий раз, когда я ДЕЛАЮ его, он всегда заставляет меня думать намного больше, чем позже, когда я пытаюсь его поддерживать.

Мне нравится избегать многословия, но когда это значительно облегчает восприятие кода, я остановлюсь на многословности.

Рассматривать:

String name = firstName;

if (middleName != null) {
    name += " " + middleName;
}

name += " " + lastName;

Это немного многословно, но я нахожу это более читабельным, чем:

String name = firstName + (middleName == null ? "" : " " + middleName)
    + " " + lastName;

или:

String name = firstName;
name += (middleName == null ? "" : " " + middleName);
name += " " + lastName;

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

Майк Стоун
источник
1
Это не совсем то же самое, хотя. Во втором примере вы сжимаете все три оператора в одну строку. Это то, что снижает читабельность, а не троичный оператор.
ilitirit
Достаточно справедливо, я обновил, чтобы включить ваш комментарий, но он все еще кажется мне загроможденным ... но опять же, это субъективно ... Я не говорю, что троичный не читается, я говорю, что он не читается для меня (99 % времени)
Майк Стоун
3

Только тогда, когда:

$ var = (simple> test? simple_result_1: simple_result_2);

ПОЦЕЛУЙ.

Джаред Фарриш
источник
3

Я обычно использую в таких вещах:

before:

if(isheader)
    drawtext(x,y,WHITE,string);
else
    drawtext(x,y,BLUE,string);

after:

    drawtext(x,y,isheader==true?WHITE:BLUE,string);
KPexEA
источник
Конечно, в большинстве языков вам также не понадобится часть "== true" этой троичной части.
Майкл Харен
Я понимаю, что, хотя я стараюсь сделать это просто для того, чтобы сделать код более читабельным, так как компилятор должен оптимизировать его так же, как и без == true в любом случае
KPexEA
ни на одном языке вам может не понадобиться "== true"
niXar
У меня были трудные времена, решая, повышать ли голос или нет. Пример хорош, но == TRUE - это то, что я не могу видеть в коде других людей.
Петер Перхач
3

Как уже отмечали другие, они хороши для коротких простых условий. Я особенно люблю их по умолчанию (вроде как || и или использование в javascript и python), например

int repCount = pRepCountIn ? *pRepCountIn : defaultRepCount;

Другое распространенное использование - инициализация ссылки в C ++. Поскольку ссылки должны быть объявлены и инициализированы в одном и том же операторе, вы не можете использовать оператор if.

SomeType& ref = pInput ? *pInput : somethingElse;
maccullt
источник
2
Удивительно, что это первое упоминание об инициализации ссылок, которое является одним из немногих мест, где нельзя использовать «если» вместо?:. (Я думаю, потому что это не специфичный для C ++ вопрос ...) Они также полезны в списках инициализации конструктора по той же причине.
j_random_hacker
2

Я отношусь к троичным операторам очень похоже на GOTO. У них есть свое место, но их следует избегать, чтобы облегчить понимание кода.

Дэн Уокер
источник
2

Недавно я видел вариацию на троичные операторы (ну вроде), которые делают стандартный вариант "()?:" Кажущимся образцом ясности:

var Result = [CaseIfFalse, CaseIfTrue][(boolean expression)]

или, чтобы дать более ощутимый пример:

var Name = ['Jane', 'John'][Gender == 'm'];

Имейте в виду, это Javascript, поэтому подобные вещи могут быть невозможны на других языках (к счастью).

pilsetnieks
источник
1
вау, это ужасно! представьте, что вы вложите пару таких вместе! Единственно смутно полезная вещь, которую я могу видеть, это если у вас есть функция, которая возвращает массив из 2 элементов: var Name = getNames () [Gender == 'm']; ... но это даже МЕНЬШЕ читаемый!
Ник
2

Для простых случаев я люблю использовать это. На самом деле гораздо проще читать / кодировать, например, как параметры для функций или тому подобное. Также, чтобы избежать новой строки, я хотел бы сохранить все мои if / else.

Несет это было бы большим НЕТ-НЕТ в моей книге.

Итак, возобновляя, для одного if / else я буду использовать троичный оператор. Для других случаев обычный if / else if / else (или switch)

Родриго Гомес
источник
2

Мне нравится особый случай троичного оператора Groovy, называемый оператором Элвиса:?:

expr ?: default

Этот код оценивается как expr, если он не равен нулю, и по умолчанию, если это так. Технически это на самом деле не троичный оператор, но он определенно связан с ним и экономит много времени / печатать.

Стив Лош
источник
Да, мне это тоже нравится - это ??в C #, нулевом операторе слияния: stackoverflow.com/questions/278703/…
Джаррод Диксон
2

Для простых задач, таких как назначение другого значения в зависимости от условия, они хороши. Я бы не использовал их, когда есть более длинные выражения в зависимости от условий.

Svet
источник
2

А так много ответов сказали, это зависит . Я считаю, что если троичное сравнение не видно при быстром сканировании кода, его не следует использовать.

В качестве дополнительной проблемы, я мог бы также отметить, что само его существование на самом деле немного аномально из-за того факта, что в Си сравнительное тестирование является утверждением. В Icon ifконструкция (как и большинство Icon) фактически является выражением. Так что вы можете делать такие вещи, как:

x[if y > 5 then 5 else y] := "Y" 

... который я нахожу гораздо более читабельным, чем оператор сравнения ternery. :-)

Недавно обсуждалась возможность добавления ?:оператора в Icon, но несколько человек правильно отметили, что в этом нет никакой необходимости из-заif работает система .

Это означает, что если бы вы могли сделать это на C (или на любом другом языке, в котором есть оператор ternery), то вам фактически не понадобился бы оператор ternery.

staticsan
источник
2

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

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

Alex
источник