До трінарного чи не до трійкового? [зачинено]


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;

81
Повністю згоден з першими кількома твердженнями, але повністю не погоджуюся з вашим прикладом "покращеної читабельності". Якщо ви збираєтеся робити багаторядкові, чому б не просто використовувати оператор if?
Джо Філіпс

3
просто тому, що якщо інакше, є більш багатослівним для простих рішень: int a = 0; якщо (b> 10) a = деяке значення; else a = інший_значення; Що ви віддаєте перевагу?
marcospereira

44
@ d03boy: Тому що if-statement - це лише те, що заява, і він не буде робити, коли все, що ви хочете, є виразом.
falstro

2
@roe в деяких мовах, якщо це вираз (наприклад, у Scala), так val x = if(true) 0 else 1це цілком законно
om-nom-nom

5
@ om-nom-nom випадок у цьому, це зробить це виразом if, а не if-оператором, і по суті є тим же самим, що й оператор?: -.
falstro

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ненароком подаєш непотрібні дужки там.
bobobobo

1
Також у цьому випадку, якщо conditionце bool ти міг би просто зробити int count = condition;
bobobobo

1
@bobobobo це if/elseз дужками - це те, як більшість програмістів перепишуть тернар ..
Andre Figueiredo

1
@bobobobo if / else w / o braces просто задає проблеми. Хтось надто просто додавати рядок, але забувайте, що йому знадобляться дужки, щоб зробити те, що вони очікують (виконати додатковий рядок як частину блоку): 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"; }

У таких завданнях я вважаю, що це менше рефакторно і зрозуміліше.

Коли я працюю в рубіні з іншого боку, я скоріше використовую, 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 Я нещодавно почав це робити. Я насправді навіть роблю це для заміни операторів переключення.
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 ) кінцевий результат не особливо короткий.


Я б все-таки виступав за використання похвал у кожній конструкції if / then / else, яка не є потрійною, тому у вашому другому прикладі не вистачає кількох імхо.
Kris

Так, це функціонально. Це як крихітна функція, яка має єдиний булевий аргумент і повертає будь-який тип, який ви хочете! Насправді акуратний оператор.
бобобобо

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
Два ваші перші приклади справді погані. Результати порівнянь вже булеві значення, тому ваші потрійні оператори марні і лише ускладнюють код.
Трилліан

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-ternar з терміналом.
Шон Яскравий

Вибачте, цього абзацу не бачили. Не знаю, що я згоден з таким видом матеріалів, хоча він дозволяє людям писати код, який не буде компілюватись із компілятором стандарту ISO. І все-таки, коли GCC є останньою людиною, яка стоїть, це не має значення :-)
paxdiablo

Безумовно, це вуду ... І хто не використовує GCC? : D
Шон Яскравий

14

По-моєму, має сенс використовувати потрійний оператор лише у випадках, коли потрібне вираження.

В інших випадках здається, що потрійний оператор зменшує чіткість.


проблема полягає в тому, що це 99% мови, вираз можна замінити функцією ... і ppl, уникаючи потрійного оператора, навіть віддасть перевагу цьому рішенню.
PierreBdR

11

За мірою цикломатичної складності використання ifоператорів або потрійний оператор рівнозначні. Тож за цією мірою відповідь - ні , складність була б точно такою ж, як і раніше.

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


10

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

Надуманий приклад:

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

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

Мене завжди спантеличує, як деякі люди вважають, що потрійний оператор є "прихованою" особливістю або дещо загадковою. Це одне з перших речей, які я дізнався, коли починаю програмувати на С, і я не думаю, що це зовсім зменшує читабельність. Це природна частина мови.


1
Я повністю згоден. У цьому немає нічого прихованого або хитрого.
mmattax

2
Це може спричинити проблеми читабельності, особливо коли вони вкладені.
Девід Торнлі

Я думаю, що " надзвичайно важко читати" - це занадто вседозволено, але в цілому я з вами згоден. У цьому немає нічого складного чи містичного.
EpsilonVector

7

Я погоджуюся з jmulder: він не повинен використовуватися замість a if, але він має місце для виразного повернення або всередині виразу:

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

Перший - лише приклад, слід використовувати кращу підтримку i18n множини!


Перший термінал невірний - у вас є: де? мусить піти: (n! = 1 ? "s": "")
Ерік Форбс

Так, дякую, що вказали на це! Виправлено.
PhiLho


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


3
Останній рядок болить мій мозок ...
Ерік Форбс

5

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


5

Я бачив таких звірів на кшталт (це було насправді набагато гірше, оскільки це булоValidDate і перевірялося також місяць і день, але я не міг турбуватися, намагаючись згадати все це):

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");

Як ви можете сказати ряд відокремлених, якщо твердження будуть читабельнішими? Я читаю твого «звіра» просто чудово . reportedAgeце приклад, який займається деякими фігурами - можливо, тому, що це скоріше певний випадок, ніж з'ясуванняisThisYearALeapYear
Axeman

4

Ну, синтаксис для нього жахливий. Я вважаю функціональний ifs дуже корисним і часто робить код читабельнішим.

Я б запропонував зробити макрос, щоб зробити його більш читабельним, але я впевнений, що хтось може придумати жахливий край (як завжди це стосується CPP).


багато реалізацій та варіантів 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");
}


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
Це не зовсім те саме. У другому прикладі ви стискаєте всі три твердження в один рядок. Саме це зменшує читабельність, а не потрійний оператор.
ілітірит

Досить справедливо, я оновив, щоб включити ваш коментар, але він все ще просто затиснутий мною ... але знову ж таки, це суб'єктивно ... Я не кажу, що потрійний не читається, я кажу, що він не читається для мене (99 % часу)
Майк Стоун


3

Я зазвичай використовую такі речі:

before:

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

after:

    drawtext(x,y,isheader==true?WHITE:BLUE,string);

Звичайно, у більшості мов вам також не знадобиться "== true" частина цього потрійного.
Майкл Харен

Я розумію, що, хоча я схильний вводити його просто для того, щоб зробити код більш читабельним, оскільки компілятор повинен оптимізувати його до того ж, що і без == справжнього так чи інакше
KPexEA

в НЕ мовою ви можете , можливо , потрібно «== правда»
niXar

Мені важко було вирішувати, чи підкреслити чи ні. Приклад хороший, але == ІСТИНА - це те, чого я не витримую в коді інших людей.
Пітер Пергач

3

Як зазначають інші, вони приємні для коротких простих умов. Особливо мені подобаються вони за замовчуванням (на зразок || та або використання у javascript та python), наприклад

int repCount = pRepCountIn ? *pRepCountIn : defaultRepCount;

Ще одне поширене використання - ініціалізація посилання на C ++. Оскільки посилання повинні бути оголошені та ініціалізовані в одному операторі, ви не можете використовувати оператор if.

SomeType& ref = pInput ? *pInput : somethingElse;

2
Дивно, що це перша згадка про ініціалізацію посилань, що є одним із небагатьох місць, де замість? (Я думаю, тому що це не специфічне для C ++ питання ...) Вони також корисні в списках ініціалізації конструкторів з тієї ж причини.
j_random_hacker

2

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


2

Нещодавно я побачив зміну на потрійних операторах (ну, начебто), які роблять стандартний варіант "()?:", Здається, парагоном ясності:

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

або, щоб навести більш відчутний приклад:

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

Зауважте, що це Javascript, тому подібні речі можуть бути неможливі в інших мовах (на щастя).


1
вау, це жахливо! уявіть, як вкладете пару таких разом! Єдине розпливчато корисне, що я можу побачити з цим, це якщо у вас була функція, яка повертала 2-елементний масив: var Name = getNames () [Gender == 'm']; ... але це навіть менше ЧИТАТИ!
nickf

2

Для простих, якщо випадків, я люблю це використовувати. Насправді, набагато простіше читати / кодувати, наприклад, параметри для функцій або подібних речей. Крім того, щоб уникнути нового рядка, який я хотів би тримати з усіма своїми if / else.

Помітити це було б великим НІ-НІ в моїй книзі.

Отже, відновивши, для єдиного if / else я скористаюся потрійним оператором. Для інших випадків звичайний if / else if / else (або перемикач)


2

Мені подобається особливий випадок Гроови оператора потрійного виробництва, який називається оператором Елвіса:?:

expr ?: default

Цей код оцінюється як expr, якщо він не є нульовим, і за замовчуванням, якщо він є. Технічно це насправді не потрійний оператор, але це, безумовно, пов’язано з цим і економить багато часу / набору тексту.


Так, мені подобається і цей - він знаходиться ??в C #, операторі nul
Jarrod Dixon

2

Для простих завдань, таких як присвоєння різного значення залежно від стану, вони чудові. Я б не використовував їх, коли існують довші вирази залежно від стану Тхо.


2

Сказано так багато відповідей, це залежить . Я вважаю, що якщо потрійне порівняння не видно при швидкому скануванні коду, його не слід використовувати.

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

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

... що мені здається набагато більш читабельним, ніж оператор порівняння палець. :-)

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

Це означає, що якби ви могли це зробити на C (або будь-якій іншій мові, яка має оператора terner), то вам, власне, не знадобився б це оператор.


2

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

Єдиний раз, коли я думаю, що потрійні оператори роблять код складніше зрозуміти, коли у вас є більше 3 або 4 в одному рядку. Більшість людей не пам’ятають, що вони є правильним пріоритетом, і коли у вас є стек з них, це робить читання коду кошмаром.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.