Operatore ternario considerato dannoso? [chiuso]


79

Ad esempio, preferiresti questo one-liner

int median(int a, int b, int c) {
    return (a<b) ? (b<c) ? b : (a<c) ? c : a : (a<c) ? a : (b<c) ? c : b;
}

o una soluzione if / else che coinvolge più dichiarazioni di reso?

Quando è ?:appropriato e quando non lo è? Dovrebbe essere insegnato o nascosto ai principianti?


221
Questo particolare utilizzo è :)
karmajunkie,

6
Chi l'ha codificato e come appare la loro versione di una mediana per quattro numeri? O cinque?
Mason Wheeler,

3
Il nome più corretto è "operatore condizionale". Capita solo di essere l'operatore ternario più comune in uso.
Alan Pearce,

1
Questo è stato chiesto più di due anni fa su StackOverflow. Ora chiederemo di nuovo tutto qui? stackoverflow.com/questions/160218/to-ternary-or-not-to-ternary
webbiedave

3
Sono sorpreso dal motivo per cui tali domande continuano a sorgere. La risposta è sempre "qualunque cosa funzioni ed è leggibile". - l'ultimo è altrettanto importante.
Apoorv Khurasia,

Risposte:


234

L'operatore ternario è malvagio?

No è una benedizione.

Quando è?: Appropriato?

Quando è qualcosa di così semplice per cui non vuoi sprecare molte linee.

e quando no?

Quando la leggibilità e la chiarezza del codice soffre e aumenta la possibilità di un errore a causa di un'attenzione insufficiente, ad esempio con molti operatori concatenati, proprio come nel tuo esempio.


La cartina di tornasole è quando inizi a dubitare che il tuo codice sia facilmente leggibile e gestibile a lungo termine. Quindi non farlo.


23
+1 per Quando soffre la leggibilità e la chiarezza del codice. Con molti operatori concatenati, proprio come nel tuo esempio. L'esempio impiega più tempo a comprendere dell'equivalente if / else.
sange,

23
+1 bravo per la grande spiegazione! Gli sviluppatori non tendono a rendersi conto che alcune cose sono chiamate di giudizio, vogliono che tutto sia in bianco e nero. Mi fa impazzire. Ho incontrato molte persone dell'opinione "X è malvagio, non usiamolo mai". Preferisco "X è fantastico se lo usi per quello in cui è bravo".
Ripristina Monica il

54
È anche malvagio se viene utilizzato: myVar = (someExpression)? vero falso; Aaaaarrgh!
Adamk,

20
@adamk: prova questo per il male:myVar = someExpression ? false : true;
Dean Harding,

8
Che ne dici di (someExpression ? var1 : var2)++:-)
fredoverflow il

50

Penso che l'operatore ternario innestato (cioè un'affermazione in cui viene usato solo una volta) vada bene, ma se ne nidificano più di uno, diventa in qualche modo difficile da leggere.


3
Questa potrebbe essere considerata una semplificazione eccessiva, ma è una linea guida molto semplice da seguire e funziona nella maggior parte dei casi.
Alan Pearce,

2
In realtà è la mia regola empirica: non dovresti mai nidificarli, altrimenti sostituirli con if / else, sarà più chiaro in questo modo.
Piovezan,

Se le useresti e le annidassi, allora per amore dell'umanità usa la parentesi e lo spazio bianco per renderla leggibile. If-else può essere reso altrettanto brutto. Resistete alla tentazione di mostrare quanto siete "intelligenti" scrivendo cose che i compilatori possono leggere ma che gli umani non possono. Un giorno sarai l'umano che non può.
candied_orange,

24

Quando è?: Appropriato

  • Quando rende il tuo codice più conciso e leggibile.

e quando no?

  • Quando rende il tuo codice illeggibile.
  • Se lo stai facendo solo per compiacere uno strumento di refactoring come ReSharper e non la persona che deve mantenere il codice

Se hai espressioni logiche o di funzione all'interno dell'espressione ternaria, stai rendendo orribile da guardare.


Questo merita molti voti!
Piovezan,

22

Una differenza che (penso) nessuno ha sottolineato è che if-else non può restituire un valore, mentre l'operatore ternario può farlo.

Provenendo da F #, a volte mi piace usare l'operatore ternario per imitare la corrispondenza del modello.

match val with
| A -> 1
| B -> 3
| _ -> 0

vs

return val == A ? 1 : 
       val == B ? 3 : 
       0;

È abbastanza bello. Non ci ho mai pensato.
Rei Miyasaka,

+1 @Benjol: stavo per sottolineare la stessa cosa (che in F #, tutto è un'espressione, incluso if / elif / else). Uso anche i ternari come nel tuo esempio, finora ancora da scoprire :). Un'altra cosa che ho fatto, in Javascript, forse sopra le righe, è: var res = function() {switch(input) {case 1: return "1"; case 2: return "2"; ...}}()emulare gli switch come espressioni.
Stephen Swensen,

@Stephen, stavo per usare le parole expressione statementsono sempre preoccupato che le
capirò nel

@ Benjol: so cosa intendi!
Stephen Swensen,

6
Utile anche in C e C ++ per inizializzare constvariabili che non possono essere modificate in seguito.
David Thornley,

13

Un esempio (IMHO) di un uso valido:

printf("Success in %d %s\n", nr_of_tries, (nr_of_tries == 1 ? "try" : "tries"));

Ciò si traduce in un codice più leggibile rispetto ad avere 2 istruzioni di stampa distinte. Gli esempi nidificati dipendono: (comprensibile? Sì: no)


11
Tieni presente che se lo fai, lo stai rendendo un inferno per chiunque debba localizzare la tua applicazione. Naturalmente, se questo non è un problema, vai avanti.
Anon.

1
Questa è la mia regola empirica: 1 livello di nidificazione (in alcune circostanze).
Oliver Weiler,

7

Assolutamente non male. In realtà, è puro , e se-allora-altro non lo è.

In linguaggi funzionali come Haskell, F #, ML ecc., Sono le dichiarazioni if-then-else che sono considerate malvagie.

Il motivo di ciò è che qualsiasi "azione" come un'istruzione if-then-else imperativa richiede di separare una dichiarazione di variabile dalla sua definizione e introduce lo stato nella funzione.

Ad esempio, nel seguente codice:

const var x = n % 3 == 1
    ? Parity.Even
    : Parity.Odd;

vs.

Parity x;
if (n % 3 == 1)
    x = Parity.Even;
else
    x = Parity.Odd;

Il primo ha due vantaggi oltre ad essere più breve:

  1. x è una costante e offre quindi molte meno possibilità di introdurre bug e può essere potenzialmente ottimizzato in modi che il secondo non potrebbe mai essere.
  2. Il tipo viene chiarito dall'espressione, quindi il compilatore può facilmente dedurre che xdeve essere di tipo Parity.

Confusamente, nei linguaggi funzionali, l'operatore ternario viene spesso chiamato if-then-else. A Haskell, potresti dire x = if n mod 3 == 1 then Odd else Even.


sì, questo è anche il punto @Benjol. Vedi il mio commento alla sua risposta per un modo divertente di emulare le istruzioni switch come espressioni in Javascript.
Stephen Swensen,

7

Quella particolare espressione mi fa male agli occhi; Vorrei sfuggire a qualsiasi sviluppatore del mio team che lo usasse perché non è realizzabile.

Gli operatori ternari non sono malvagi se usati bene. Non devono nemmeno essere linee singole; Un lungo ben formattato può essere molto chiaro e facile da capire:

return
      ( 'a' == $s ) ? 1
    : ( 'b' == $s ) ? 2
    : ( 'c' == $s ) ? 3
    :                 4;

Mi piace meglio dell'equivalente catena if / then / else:

if ( 'a' == $s ) {
    $retval = 1;
}
elsif ( 'b' == $s ) {
    $retval = 2;
}
elsif ( 'c' == $s ) {
    $retval = 3;
}
else {
    $retval = 4;
}

return $retval;

Li riformatterò in:

if    ( 'a' == $s ) { $retval = 1; }
elsif ( 'b' == $s ) { $retval = 2; }
elsif ( 'c' == $s ) { $retval = 3; }
else                { $retval = 4; }

return $retval;

se le condizioni e le assegnazioni consentono un facile allineamento. Preferisco comunque la versione ternaria perché è più corta e non ha così tanto rumore attorno alle condizioni e ai compiti.


Perché non posso inserire interruzioni di riga nei commenti? Arrgghh!
Christopher Mahan,

3

ReSharper in VS.NET a volte suggerisce di sostituire if...elsecon l' ?:operatore.

Sembra che ReSharper suggerisca solo se le condizioni / i blocchi sono al di sotto di un certo livello di complessità, altrimenti si attacca if...else.


4
un'altra grande caratteristica che adoro di ReSharper
Anonymous Type

2

Questo può essere riformattato per essere bello come la combinazione if / else:

int median(int a, int b, int c)
{
    return
        (a<b)
        ?
            (b<c)
            ? b
            :
                (a<c)
                ? c
                : a
        :
            (a<c)
            ? a
            :
                (b<c)
                ? c
                : b;
}

Ma il problema è che non sono davvero sicuro di avere il rientro giusto per rappresentare ciò che accadrà effettivamente. :-)


3
+1 Penso che questo sia molto meglio di una if-elsestruttura equivalente . La chiave è la formattazione.
Orbling

13
Se lo vedessi in una base di codice, prenderei seriamente in considerazione la ricerca di un nuovo lavoro.
Nick Larsen,

+1 Non per questo rientro effettivo, ma questa è la soluzione migliore per questo esempio.
Mark Hurd,

2
Solo che un'altra formattazione automatica accidentale avrebbe spazzato via tutto quel rientro e tempo per un altro giro di ristrutturazione - estremamente produttivo :)
nawfal

2

Lungi dall'essere malvagio, l'operatore ternario è una manna dal cielo.

  • È molto utile quando si desidera prendere una decisione in un'espressione nidificata . L'esempio classico è una chiamata di funzione:

    printf("I see %d evil construct%s in this program\n", n, n == 1 ? "" : "s");
    
  • Nel tuo esempio particolare, il ternario è quasi gratuito, poiché è l'espressione di livello superiore in a return. È possibile elevare il condizionale a livello di istruzione senza duplicare altro che la returnparola chiave.

NB Nulla renderà mai quel particolare algoritmo per la mediana di facile lettura.


È difficile essere così leggibili che non puoi migliorarlo. printf("I see %d evil construct%s in this program\n", n, "s" unless (n == 1) "s");
Pacerier,

2
  1. Argomenti "malvagi" a parte, nella mia esperienza ho trovato un'alta correlazione tra l'uso da parte di un programmatore dell'operatore ternario e la probabilità che l'intera sua base di codice sia difficile da leggere, seguire e mantenere (se non del tutto priva di documenti). Se un programmatore è più interessato a salvare alcune righe di 1-2 caratteri rispetto a qualcuno che è in grado di comprendere il proprio codice, allora qualsiasi confusione minore che capisca un'affermazione ternaria è di solito la punta dell'iceberg.

  2. Gli operatori ternari attirano numeri magici come s ** t attira le mosche.

Se stavo cercando una biblioteca Open Source per risolvere un problema particolare, e vedessi codice come gli operatori ternari del poster originale in un candidato per detta biblioteca, le campane di avvertimento inizierebbero a suonare nella mia testa e inizierei a pensare di andare avanti a qualche altro progetto da cui prendere in prestito.


2

Ecco un esempio di quando è malvagio:

oldValue = newValue >= 0 ? newValue : oldValue;

È confuso e dispendioso. Il compilatore potrebbe ottimizzare la seconda espressione (oldValue = oldValue), ma perché il programmatore lo ha fatto in primo luogo?

Un altro doozy:

thingie = otherThingie != null ? otherThingie : null;

Alcune persone non sono pensate per essere programmatori ...

Greg afferma che l'equivalente if è "rumoroso". Lo è se lo scrivi rumorosamente. Ma lo stesso se può essere scritto come:

if ('a' == $s) return 1;
if ('b' == $s) return 2;
if ('c' == $s) return 3;
return 4;

Che non è più rumoroso del ternario. Mi chiedo se le scorciatoie ternarie; vengono valutate tutte le espressioni?


Il tuo secondo esempio mi ricorda if (x != 0) x = 0;...
Fredoverflow

2

Il male? Guarda, sono solo diversi.

ifè una dichiarazione. (test ? a : b)è un'espressione. Non sono la stessa cosa.

Esistono espressioni per esprimere valori. Esistono dichiarazioni per eseguire azioni. Le espressioni possono apparire all'interno delle istruzioni, ma non viceversa. Quindi è possibile utilizzare espressioni ternarie all'interno di altre espressioni, ad esempio per i termini in un riepilogo o per argomenti relativi a un metodo e così via. Non è necessario , ma è possibile se si vuole . Non c'è niente di sbagliato in questo. Alcune persone potrebbero dire che è malvagio, ma questa è la loro opinione.

Un valore di un'espressione ternaria è che ti fa gestire sia i casi veri che falsi. ifle dichiarazioni no.

Se sei preoccupato per la leggibilità, puoi formattarli in modo leggibile.

In qualche modo "male" si insinuò nel vocabolario della programmazione. Mi piacerebbe sapere chi l'ha lasciato cadere per primo. (In realtà, ho un sospettato - è al MIT.) Preferirei che avessimo ragioni oggettive per giudizi di valore in questo campo, non solo per il gusto e la vocazione delle persone.


1
Posso avere un indizio su chi sia il sospettato? Solo per migliorare un po 'la mia conoscenza del campo.
mlvljr,

1
@mlvljr: Beh, potrei sbagliarmi, quindi meglio non farlo.
Mike Dunlavey,

1

Ha un posto. Ho lavorato in molte aziende in cui i livelli di abilità degli sviluppatori vanno dal terribile al mago. Dato che il codice deve essere mantenuto, e non ci sarò per sempre, provo a scrivere cose in modo che sembri che vi appartenga (senza guardare i commenti con le mie iniziali, è estremamente raro che tu sia in grado di guarda il codice su cui ho lavorato per vedere dove ho apportato le modifiche) e che qualcuno con meno abilità di me stesso può mantenerlo.

Mentre l'operatore ternario sembra nitziffic e molto bello, la mia esperienza è che la linea di codice sarà quasi impossibile da mantenere. Al mio attuale datore di lavoro, abbiamo prodotti che spediscono da quasi 20 anni. Non userei questo esempio da nessuna parte.


1

Non penso che l'operatore ternario sia malvagio.

Ecco un gotcha che mi ha lasciato perplesso, però. Sono stato un programmatore C per molti (10+) e alla fine degli anni '90 sono passato alla programmazione di applicazioni basate sul web. Come programmatore web, mi sono imbattuto presto in PHP che ha anche un operatore ternario. Ho avuto un bug in un programma PHP che ho finalmente rintracciato in una riga di codice con un operatore ternario annidato. Si scopre che l'operatore ternario PHP associato da sinistra a destra ma l'operatore ternario C (a cui ero abituato) associa da destra a sinistra.


1

Tutto ciò che rende il tuo codice più brutto è malvagio.

Se usi ternary per rendere il tuo codice più pulito, assicurati di usarlo. A volte come in php è bello fare sostituzioni in linea, ad es

"Hello ".($Male?"Mr":"Ms")." $Name

Ciò consente di risparmiare alcune righe, ed è abbastanza chiaro, ma il tuo esempio ha bisogno di una formattazione migliore per essere chiaro, e ternario non è davvero buono per la multilinea, quindi potresti anche usare if / else.


1

Posso dirlo? Non riesco a trovare male questa particolare applicazione dell'operazione ternaria :

  1. l'operazione che esegue è piuttosto banale, e una volta che l'avete superata una volta è quasi impossibile che emergano alcuni bug;
  2. ciò che fa è chiaramente indicato nel nome della funzione;
  3. prendendo> 1 linea per qualcosa di così ovvio e che così chiaramente non sarebbe migliorato in futuro (a meno che un algoritmo mediano magico non sia vissuto fino ad ora inosservato).

Per favore abbi pietà, la mia reputazione è già abbastanza penosa.


1

La più grande vittoria: dimostrare che esiste un solo obiettivo di azione.

if ( $is_whatever )
    $foo = 'A';
else
    $foo = 'B';

Esistono due percorsi di codice che è possibile seguire e il lettore deve leggere attentamente per vedere quali due variabili vengono impostate. In questo caso, è solo una variabile, ma il lettore ha più da leggere per capirlo. Dopotutto, avrebbe potuto essere questo:

if ( $is_whatever )
    $foo = 'A';
else
    $bar = 'B';

Con l'operatore ternario, è chiaro che è stata impostata solo una variabile.

$foo = $is_whatever ? 'A' : 'B';

Al livello più basso, è il principio DRY (Don't Repeat Yourself) nella sua forma più elementare. Se puoi specificare $foouna sola volta, fallo.


0

Se ... allora ... altro tende a enfatizzare la condizione e quindi a de-enfatizzare le operazioni condotte in modo condizionale.

l'operatore ternario è l'opposto, tende a nascondere la condizione ed è quindi utile quando l'operazione in corso è più importante della condizione stessa.

C'è un piccolo inconveniente tecnico, in alcune lingue, che non sono abbastanza intercambiabili a causa del fatto che uno è un'affermazione e uno un'espressione, ad esempio inizializzazione condizionale di cons in C ++


0

Quando è appropriato e quando non lo è?

Penso che quando si sviluppa per un gruppo omogeneo di persone, non ci sono problemi, ma quando si ha a che fare con persone che gestiscono livelli diversi, questo tipo di oneliner introduce solo un ulteriore livello di complessità nel codice. Quindi, la mia politica in materia è: codice chiaro e non spiegare piuttosto che codice breve e spiegare 123123 volte.

Dovrebbe essere insegnato o nascosto ai principianti?

Non dovrei insegnare ai principianti, piuttosto preferisco che capiscano quando emerge la necessità, quindi sarà usato solo quando necessario e non tutte le volte che hai bisogno di un se.


0

IMO, l'operatore stesso non è malvagio, ma la sintassi usata per esso in C (e C ++) è eccessivamente concisa. IMO, Algol 60 ha fatto di meglio, quindi qualcosa del genere:

A = x == y ? B : C;

apparirebbe più simile a questo (ma attenendosi alla sintassi di tipo C in generale):

A = if (x==y) B else C;

Anche con ciò, un annidamento eccessivamente profondo può portare a problemi di leggibilità, ma almeno A) chiunque abbia finito di programmare può capirne uno semplice, e B) le persone che lo capiscono possono gestire un annidamento considerevolmente più profondo abbastanza facilmente. OTOH, noterei anche che in LISP (per esempio) a condè praticamente una dichiarazione ternaria - non un insieme di istruzioni, ma una singola espressione produce un valore (poi, di nuovo, la maggior parte di LISP è così ... .)


Perché non farlo solo per leggibilità? A = (x==y) ? B : C
Jeremy Heiler,

@Jeremy: mentre alcune persone trovano utili le parentesi, anche nella migliore delle ipotesi non aiutano molto . Nidifica più di una coppia in profondità e hai ancora bisogno di un rientro accurato (come minimo indispensabile) per mantenere le cose risolte. Indubbiamente lo stesso accadrebbe alla fine in Algol, ma non dico mai che si presenta un problema come faccio spesso in C ...
Jerry Coffin

Ho solo pensato che tutti fossero d'accordo sul fatto che nidificare un operatore ternario fosse un male. Stavo parlando specificamente degli esempi che hai fornito. In particolare, come il primo può essere più simile al secondo nella maggior parte delle lingue.
Jeremy Heiler,

0

Un negozio che scrive regolarmente metodi a 600-1200 righe non dovrebbe dirmi che un ternario è "difficile da capire". Qualsiasi negozio che consenta regolarmente cinque condizioni per valutare un ramo di codice non dovrebbe dirmi che le condizioni concretamente riassunte in un ternario sono "difficili da leggere".


0

Quando è?: Appropriato e quando non lo è?

  • Se non ottieni un guadagno in termini di prestazioni, non utilizzarlo; influisce sulla leggibilità del tuo codice.
  • Usalo una volta e non annidarlo.
  • È più difficile eseguire il debug.

Dovrebbe essere insegnato o nascosto ai principianti?

Non importa, ma non dovrebbe essere nascosto intenzionalmente in quanto non è troppo complesso per essere appreso da un "principiante".


-2

Nel tuo esempio:

def median(a, b, c):
    if a < b < c: return b
    if a < c < b: return c
    if b < a < c: return a
    if b < c < a: return c
    if c < a < b: return a
    if c < b < a: return b

è molto semplice da leggere ed evidente. La variabile tra <<è il valore restituito.

Aggiornare

stesso, ma meno righe di codice. Ancora semplice, penso.

def median(a, b, c):
    if b<a<c or c<a<b: return a
    if a<b<c or c<b<a: return b
    if a<c<b or b<c<a: return c

Ciò richiede 12 confronti nel peggiore dei casi ...
Fredoverflow

1
Forse, ma è leggibile.
Christopher Mahan,

-2

È anche necessario per const

const int nLegs  = isChicken ? 2: 4 ;

Strano. Penso che sia C ++ o qualcosa del genere. Ho pensato che const fosse sempre stata una costante di tempo di compilazione (come in C #)
nawfal,

@nawfal - se non sai isChicken fino al runtime
Martin Beckett,

sì, ecco cosa. penso che constsia così in alcune lingue. In C # constdovrebbe sempre essere compilato il valore noto in tempo. il che significa che const int nLegs = isChicken ? 2: 4 ;non funzionerà, ma const int nLegs = true ? 2: 4 ;sarà
nawfal
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.