В тройной или нет в тройной?
Я лично сторонник троичного оператора: ()?:; Я понимаю, что это имеет свое место, но я сталкивался со многими программистами, которые полностью против того, чтобы когда-либо использовать это, и некоторые, которые используют это слишком часто.
Каковы ваши чувства по этому поводу? Какой интересный код вы видели, используя его?
54 ответа
Используйте его только для простых выражений:
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;
Это немного затрудняет отладку, поскольку вы не можете ставить точки останова в каждом из подвыражений. Я использую это редко.
Я люблю их, особенно на типобезопасных языках.
Я не вижу, как это:
int count = (condition) ? 1 : 0;
это сложнее, чем это:
int count;
if (condition)
{
count = 1;
}
else
{
count = 0;
}
редактировать -
Я бы сказал, что троичные операторы делают все менее сложным и аккуратным, чем альтернативные.
Прикован цепью у меня все нормально - вложено, не так уж и много.
Я склонен использовать их больше в 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
(хотя, по общему признанию, для чего-то такого простого, я мог бы в любом случае просто использовать троичный оператор).
Троичный ?:
Оператор является просто функциональным эквивалентом процедурного 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) конечный результат не особенно лаконичен.
Это действительно вопрос стиля; подсознательные правила, которым я склонен следовать:
- Оцените только 1 выражение - так
foo = (bar > baz) ? true : false
, но нетfoo = (bar > baz && lotto && someArray.Contains(someValue)) ? true : false
- Если я использую его для отображения логики, например,
<%= (foo) ? "Yes" : "No" %>
Только действительно используйте это для назначения; никогда не течь логика (так никогдаПотоковая логика в троице сама по себе является ложью, игнорируйте эту последнюю точку.(foo) ? FooIsTrue(foo) : FooIsALie(foo)
)
Мне это нравится, потому что оно лаконично и элегантно для простых операций присваивания.
Как и многие вопросы мнения, ответ неизбежен: это зависит
Для чего-то вроде:
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
, Хорошая вещь.
На мой взгляд, имеет смысл использовать троичный оператор только в тех случаях, когда необходимо выражение.
В других случаях кажется, что троичный оператор уменьшает ясность.
По мере цикломатической сложности использование if
операторы или троичный оператор эквивалентны. Таким образом, ответом будет нет, сложность будет точно такой же, как и раньше.
С помощью других мер, таких как удобочитаемость, ремонтопригодность и СУХОЙ (не повторяй себя), любой выбор может оказаться лучше, чем другой.
Я использую троичный оператор везде, где только могу, если только это не делает код чрезвычайно трудным для чтения, но тогда это обычно просто признак того, что мой код может использовать небольшой рефакторинг.
Меня всегда удивляет, как некоторые люди думают, что троичный оператор является "скрытой" функцией или является чем-то загадочным. Это одна из первых вещей, которую я узнал, когда начал программировать на C, и я не думаю, что это вообще снижает читабельность. Это естественная часть языка.
Я использую его довольно часто в тех местах, где я вынужден работать в конструкторе - например, в новых конструкциях.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()
)
);
Независимо от того, используете ли вы троичный оператор или нет, важно убедиться, что ваш код читабелен. Любая конструкция может быть сделана нечитаемой.
Я согласен с Jmulder: он не должен использоваться вместо if
, но у него есть место для выражения возврата или внутри выражения:
echo "Result: " + n + " meter" + (n != 1 ? "s" : "");
return a == null ? "null" : a;
Первый - только пример, лучше использовать поддержку множественного числа в i18n!
Если вы используете троичный оператор для простого условного присваивания, я думаю, что это нормально. Я видел его (ab), используемый для управления потоком программ, даже не делая назначения, и я думаю, что этого следует избегать. Используйте оператор if в этих случаях.
(Хак дня)
#define IF(x) x ?
#define ELSE :
Тогда вы можете сделать if-then-else как выражение:
int b = IF(condition1) res1
ELSE IF(condition2) res2
ELSE IF(conditions3) res3
ELSE res4;
Я думаю, что троичный оператор должен использоваться при необходимости. Это, очевидно, очень субъективный выбор, но я считаю, что простое выражение (особенно как выражение возврата) намного понятнее, чем полный тест. Пример на 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: они ленивы и возвращают последнее вычисленное значение, если оно равно конечному состоянию.
Мне нравится их. Я не знаю почему, но я чувствую себя очень круто, когда использую троичное выражение.
Я видел таких зверей, как (на самом деле это было гораздо хуже, так как это 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");
Мне нравится использовать оператор в отладочном коде для вывода значений ошибок, поэтому мне не нужно постоянно их искать. Обычно я делаю это для отладочных отпечатков, которые не останутся после завершения разработки.
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");
}
Я почти никогда не использую троичный оператор, потому что всякий раз, когда я ДЕЛАЮ его, он всегда заставляет меня думать намного больше, чем позже, когда я пытаюсь его поддерживать.
Мне нравится избегать многословия, но когда это значительно облегчает восприятие кода, я остановлюсь на многословности.
Рассматривать:
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;
Кажется, он просто сжимает слишком много информации в слишком мало места, не давая понять, что происходит. Каждый раз, когда я вижу, что используется троичный оператор, я всегда находил альтернативу, которая казалась намного легче читать... опять же, это очень субъективное мнение, поэтому, если вы и ваши коллеги находите троичный очень читабельным, сделайте это.
Как уже отмечали другие, они хороши для коротких простых условий. Я особенно люблю их по умолчанию (вроде как || и или использование в javascript и python), например
int repCount = pRepCountIn ? *pRepCountIn : defaultRepCount;
Другое распространенное использование - инициализация ссылки в C++. Поскольку ссылки должны быть объявлены и инициализированы в одном и том же операторе, вы не можете использовать оператор if.
SomeType& ref = pInput ? *pInput : somethingElse;
Я обычно использую в таких вещах:
before:
if(isheader)
drawtext(x,y,WHITE,string);
else
drawtext(x,y,BLUE,string);
after:
drawtext(x,y,isheader==true?WHITE:BLUE,string);
Ну, синтаксис для него ужасен. Я нахожу функционал ifs очень полезным и часто делает код более читабельным.
Я бы предложил сделать макрос, чтобы сделать его более читабельным, но я уверен, что кто-то может придумать ужасный крайний случай (как всегда есть с CPP).
Только когда:
$ var = (simple> test? simple_result_1: simple_result_2);
ПОЦЕЛУЙ.
Я отношусь к троичным операторам очень похоже на GOTO. У них есть свое место, но их следует избегать, чтобы облегчить понимание кода.
Так много ответов сказали, это зависит. Я считаю, что если троичное сравнение не видно при быстром сканировании кода, его не следует использовать.
В качестве дополнительной проблемы, я мог бы также отметить, что само его существование на самом деле немного аномально из-за того факта, что в Си сравнительное тестирование является утверждением. В Icon if
Конструкция (как и большинство значков) на самом деле является выражением. Таким образом, вы можете делать такие вещи, как:
x[if y > 5 then 5 else y] := "Y"
... который я нахожу гораздо более читабельным, чем оператор сравнения ternery.:-)
Недавно была дискуссия о возможности добавления ?:
оператор в Icon, но несколько человек правильно указали, что не было никакой необходимости из-за способа if
работает.
Это означает, что если бы вы могли сделать это на C (или на любом другом языке, в котором есть оператор ternery), то вам фактически не понадобился бы оператор ternery.
Если вы и ваши коллеги понимаете, что они делают, и они не созданы в больших группах, я думаю, что они делают код менее сложным и легче для чтения, потому что кода просто меньше.
Мне кажется, единственный раз, когда троичные операторы усложняют понимание кода, это когда у вас более 3 или 4 строк. Большинство людей не помнят, что они основаны на правильных приоритетах, и когда у вас есть их стек, чтение кода становится кошмаром.
Для простых случаев мне нравится использовать это. На самом деле гораздо проще читать / кодировать, например, как параметры для функций или тому подобное. Также, чтобы избежать новой строки, я хотел бы сохранить все мои if / else.
Это было бы большим НЕТ-НЕТ в моей книге.
Итак, возобновляя, для одного if / else я буду использовать троичный оператор. Для других случаев обычный if/else if/else (или switch)
Для простых задач, таких как присвоение другого значения в зависимости от условия, они хороши. Я бы не стал их использовать, когда есть более длинные выражения в зависимости от условий.
Мне нравится особый случай троичного оператора Groovy, называемый оператором Элвиса:?:
expr ?: default
Этот код оценивается как expr, если он не равен нулю, и по умолчанию, если это так. Технически это на самом деле не троичный оператор, но он определенно связан с ним и экономит много времени / печатать.
Использовать его для:
- Свойства объекта доступа (массива):
var status = statuses[error == null ? 'working' : 'stopped'];
- возврат заявления:
function getFullName(){ return this.isMale() ? "Mr. " : "Ms. "+ this.name; }
инициализировать переменные:
var formMethod = DEBUG_FLAG == true ? "GET" : "POST";
- проверить аргументы:
function(object){ var prop1 = typeof object.property == 'undefined' ? "default prop" : object.property; //... }
примеры кода в JavaScript