Ternaire ou pas ternaire? [fermé]


188

Je suis personnellement un partisan de l'opérateur ternaire: ()? :; Je me rends compte qu'il a sa place, mais j'ai rencontré de nombreux programmeurs qui sont totalement contre son utilisation, et certains qui l'utilisent trop souvent.

Quels sont vos sentiments à ce sujet? Quel code intéressant avez-vous vu l'utiliser?


8
Utilisez-le quand il est clair, évitez-le quand il confond. C'est une question de jugement. Cela peut rendre le code plus lisible, mais uniquement pour des expressions simples. Essayer de toujours l' utiliser est tout autant une menace que de l'éviter sans relâche.
Abel

4
En fait, c'est l'opérateur conditionnel. Une question presque en double est stackoverflow.com/questions/725973/… .
Daniel Daranas

J'utilisais parfois, x = x if x else ymais j'ai ensuite posé des questions à ce sujet et j'ai réalisé avec l'aide d'autres personnes que cela se réduisait vraiment à x = x ou y ( stackoverflow.com/questions/18199381/self-referencing-ternary/… )
Scruffy

L'opérateur ternaire peut être utilisé là où la construction if..else ne peut pas, par exemple dans les instructions return, et comme arguments de fonction. La même chose pourrait être obtenue sans utilisation ternaire, mais entraîne un code plus long et des exécutables plus volumineux.
Arif Burhan

Réponses:


246

Utilisez-le uniquement pour des expressions simples :

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

Ne pas enchaîner ou imbriquer les opérateurs ternaires car il est difficile à lire et déroutant:

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

De plus, lorsque vous utilisez l'opérateur ternaire, envisagez de formater le code de manière à améliorer la lisibilité:

int a = (b > 10) ? some_value                 
                 : another_value;

84
Tout à fait d'accord avec les premières affirmations mais totalement en désaccord avec votre exemple de «meilleure lisibilité». Si vous optez pour le multi-lignes, pourquoi ne pas simplement utiliser une instruction if?
Joe Phillips

3
juste parce que if else est un peu plus verbeux pour les décisions simples: int a = 0; si (b> 10) a = une_valeur; else a = autre_valeur; Que préfères-tu?
marcospereira

45
@ d03boy: Parce que if-statement est juste ça, une instruction, et ne fera pas quand tout ce que vous voulez est une expression.
falstro

2
@roe dans certaines langues si est une expression (par exemple en Scala), c'est donc val x = if(true) 0 else 1parfaitement légal
om-nom-nom

5
@ om-nom-nom cas d'espèce, cela en ferait une expression if, plutôt qu'une instruction if, et est essentiellement la même chose que l'opérateur?: -.
falstro

143

Cela rend le débogage légèrement plus difficile car vous ne pouvez pas placer de points d'arrêt sur chacune des sous-expressions. Je l'utilise rarement.


47
C'est le meilleur argument contre l'opérateur ternaire que j'aie jamais entendu. Je n'achète pas l'argument "non lisible" (il me semble que les gens sont trop paresseux pour s'y habituer) mais cela a de la substance.
EpsilonVector

Si vous faites quelque chose à l'intérieur d'un ternaire qui nécessite un débogage, c'est probablement qu'il est mal utilisé. Les ternaires devraient être pour une simple affectation IMO. Si vous avez besoin de parcourir le ternaire et que voir la valeur de l'affectation par la suite ne suffit pas, le ternaire n'est PAS votre problème réel. C'est la complexité de l'opération. Faites-en une instruction if à ce stade.
Julian

80

Je les adore, en particulier dans les langages de type sécurisé.

Je ne vois pas comment ça:

int count = (condition) ? 1 : 0;

est plus difficile que cela:

int count;

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

Je dirais que les opérateurs ternaires rendent tout moins complexe et plus soigné que l'alternative.


6
L'initialisation ternaire est encore plus utile en D ou C ++ lorsque la variable est constante. eg const int count = ...;
deft_code

Eh bien, vous faites une sorte de fausse représentation if/elseavec les accolades inutiles.
bobobobo

1
En outre, dans ce cas , si conditionest que bool vous pouvez simplement faire int count = condition;
bobobobo

1
@bobobobo ceci if/elseavec des accolades est la façon dont la majorité des programmeurs réécriront le ternaire ..
Andre Figueiredo

2
@bobobobo if / else sans accolades ne demande que des problèmes. Il est beaucoup trop facile pour quelqu'un d'ajouter une ligne, mais oubliez qu'il aura alors besoin d'accolades pour faire ce qu'il attend (exécutez la ligne supplémentaire dans le cadre d'un bloc): stackoverflow.com/a/381274/377225
George Marian

45

Enchaîné, je suis bien avec - imbriqué, pas tellement.

J'ai tendance à les utiliser davantage en C simplement parce qu'ils sont une instruction if qui a une valeur, donc cela réduit les répétitions ou les variables inutiles:

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

plutôt que

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

Dans des missions comme celle-ci, je trouve que c'est moins à refactoriser et plus clair.

Quand je travaille en rubis par contre, je suis plus susceptible d'utiliser if...else...endparce que c'est aussi une expression.

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

(Bien que, certes, pour quelque chose d'aussi simple, je pourrais quand même utiliser l'opérateur ternaire.)


40

L' opérateur ternaire ?: est simplement un équivalent fonctionnel de la ifconstruction procédurale . Ainsi, tant que vous n'utilisez pas d' ?:expressions imbriquées , les arguments pour / contre la représentation fonctionnelle de toute opération s'appliquent ici. Mais l'imbrication d'opérations ternaires peut aboutir à un code totalement déroutant (exercice pour le lecteur: essayez d'écrire un analyseur qui gérera les conditions ternaires imbriquées et vous apprécierez leur complexité).

Mais il existe de nombreuses situations où l'utilisation prudente de l' ?:opérateur peut aboutir à un code qui est en fait plus facile à lire qu'autrement. Par exemple:

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;
}

Maintenant, comparez cela avec ceci:

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

Comme le code est plus compact, il y a moins de bruit syntaxique, et en utilisant judicieusement l'opérateur ternaire (c'est-à-dire uniquement en relation avec la propriété reverseOrder ), le résultat final n'est pas particulièrement laconique.


Je recommanderais toujours d'utiliser des accolades sur chaque construction if / then / else qui n'est pas ternaire tho, donc votre deuxième exemple en manque à mon humble avis.
Kris

Oui, c'est fonctionnel. Il est comme une petite fonction qui a un seul argument booléen et, retourne tout type que vous voulez! En fait, un opérateur soigné.
bobobobo

24

C'est une question de style, vraiment; les règles subconscientes que j'ai tendance à suivre sont:

  • Évaluer seulement 1 expression - donc foo = (bar > baz) ? true : false, mais PASfoo = (bar > baz && lotto && someArray.Contains(someValue)) ? true : false
  • Si je l'utilise pour la logique d'affichage, par exemple <%= (foo) ? "Yes" : "No" %>
  • Ne l'utilisez vraiment que pour l'affectation; jamais logique de flux (donc jamais (foo) ? FooIsTrue(foo) : FooIsALie(foo)) La logique de flux en ternaire est elle-même un mensonge, ignorez ce dernier point.

Je l'aime car il est concis et élégant pour des opérations d'affectation simples.


En C #, vous pouvez l'utiliser pour le contrôle de flux si vous affectez un délégué à partir du ternaire, puis l'appelez par la suite. Eh bien, c'est une sorte de contrôle de flux ...
Erik Forbes

23
Vos deux premiers exemples sont vraiment mauvais. Les résultats des comparaisons sont déjà des valeurs booléennes, donc vos opérateurs ternaires sont inutiles et ne font que compliquer le code.
Trillian

1
@Trillian +1 Oui, j'aurais dû faire une autre tâche. foo = (bar > baz);est beaucoup plus simple
Eric

Pour le cas du retour booléen avec un tas de clauses, j'aime décomposer les exigences en bits plus petits en utilisant le conditionnel ternaire, un peu comme lorsque vous refactorisez en utilisant des retours anticipés pour simplifier le code. return bar <= baz ? false ! lotto ? false : someArray.Contains(someValue )
daotoad

18

Comme tant de questions d'opinion, la réponse est forcément: ça dépend

Pour quelque chose comme:

return x ? "Yes" : "No";

Je pense que c'est beaucoup plus concis (et plus rapide pour moi à analyser) que:

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

Maintenant, si votre expression conditionnelle est complexe, alors l'opération ternaire n'est pas un bon choix. Quelque chose comme:

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

n'est pas un bon candidat pour l'opérateur ternaire.

En passant, si vous êtes un programmeur C, GCC a en fait une extension qui vous permet d'exclure la partie if-true du ternaire, comme ceci:

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

Ce qui sera mis xà l' yhypothèse yn'est pas NULL. Bon produit.


Correction d'un léger problème de syntaxe et de grammaire, Sean :-) Y manquant dans le dernier bit de code et "assigner x to y" signifie "y = x", donc je chgd à "set x to y".
paxdiablo

@Pax: Merci! J'ai annulé le changement de syntaxe car j'essayais de souligner qu'avec les extensions GCC, vous n'avez pas besoin de la partie if-true du ternaire.
Sean Bright

Désolé, je n'ai pas vu ce paragraphe. Je ne sais pas si je suis d'accord avec ce genre de choses car cela permet aux gens d'écrire du code qui ne se compilera pas avec un compilateur ISO-standard. Pourtant, lorsque GCC sera le dernier homme debout, cela n'aura pas d'importance :-)
paxdiablo

C'est du vaudou, c'est sûr ... Et qui n'utilise pas GCC? : D
Sean Bright

13

Dans mon esprit, il n'a de sens d'utiliser l'opérateur ternaire que dans les cas où une expression est nécessaire.

Dans d'autres cas, il semble que l'opérateur ternaire diminue la clarté.


le problème étant que c'est 99% du langage, une expression peut être remplacée par une fonction ... et ppl évitant l'opérateur ternaire préférera même cette solution.
PierreBdR

11

Par la mesure de la complexité cyclomatique , l'utilisation d' ifinstructions ou de l'opérateur ternaire est équivalente. Donc, par cette mesure, la réponse est non , la complexité serait exactement la même qu'avant.

Par d'autres mesures telles que la lisibilité, la maintenabilité et le DRY (ne vous répétez pas), l'un ou l'autre des choix peut s'avérer meilleur que l'autre.


10

Je l'utilise assez souvent dans des endroits où je suis contraint de travailler dans un constructeur - par exemple, les nouvelles constructions .NET 3.5 LINQ to XML - pour définir des valeurs par défaut lorsqu'un paramètre facultatif est nul.

Exemple artificiel:

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

ou (merci astérite)

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

Que vous utilisiez ou non l'opérateur ternaire, il est important de s'assurer que votre code est lisible. Toute construction peut être rendue illisible.


Ou ... var e = new XElement ("Something", new XElement ("value", param == null? "Default": param.toString ()));
astérite

9

J'utilise l'opérateur ternaire partout où je peux, à moins que cela ne rende le code extrêmement difficile à lire, mais c'est généralement juste une indication que mon code pourrait utiliser un peu de refactoring.

Cela me rend toujours perplexe à quel point certaines personnes pensent que l'opérateur ternaire est une fonction «cachée» ou quelque peu mystérieuse. C'est l'une des premières choses que j'ai apprises lorsque je commence à programmer en C, et je ne pense pas que cela diminue du tout la lisibilité. C'est une partie naturelle de la langue.


2
Cela peut entraîner des problèmes de lisibilité, surtout lorsqu'ils sont imbriqués.
David Thornley

Je pense que " extrêmement difficile à lire" est un peu trop permissif, mais dans l'ensemble, je suis d'accord avec vous. Il n'y a rien de difficile ou de mystique à ce sujet.
EpsilonVector

7

Je suis d'accord avec jmulder: il ne doit pas être utilisé à la place de a if, mais il a sa place pour l'expression de retour ou à l'intérieur d'une expression:

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

Le premier n'est qu'un exemple, et un meilleur support de l' internationalisation et de la localisation du pluriel devrait être utilisé!


6

Si vous utilisez l'opérateur ternaire pour une simple affectation conditionnelle, je pense que c'est très bien. Je l'ai vu (ab) utilisé pour contrôler le flux du programme sans même faire une affectation, et je pense que cela devrait être évité. Utilisez une instruction if dans ces cas.


6

(Hack du jour)

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

Ensuite, vous pouvez faire if-then-else comme expression:

int b = IF(condition1)    res1
        ELSE IF(condition2)  res2
        ELSE IF(conditions3) res3
        ELSE res4;

1
cela ne répond pas à la question
Bruno Alexandre Rosa

5

Je pense que l'opérateur ternaire devrait être utilisé en cas de besoin. C'est évidemment un choix très subjectif, mais je trouve qu'une expression simple (spécialement en tant qu'expression de retour) est beaucoup plus claire qu'un test complet. Exemple en C / C ++:

return (a>0)?a:0;

Par rapport à:

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

Vous avez également le cas où la solution se situe entre l'opérateur ternaire et la création d'une fonction. Par exemple en Python:

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

L'alternative est:

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

Il est suffisamment nécessaire qu'en Python (à titre d'exemple), un tel idiome puisse être vu régulièrement:

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

cette ligne utilise les propriétés des opérateurs logiques en Python: ils sont paresseux et renvoie la dernière valeur calculée si elle est égale à l'état final.


5

J'ai vu de telles bêtes comme (c'était en fait bien pire depuis qu'il était isValidDate et vérifié mois et jour également, mais je ne pouvais pas être dérangé d'essayer de me souvenir de tout):

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

où, manifestement, une série d'instructions if aurait été meilleure (bien que celle-ci soit toujours meilleure que la version macro que j'ai vue une fois).

Cela ne me dérange pas pour de petites choses comme:

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

ou même des choses légèrement délicates comme:

printf ("Deleted %d file%s\n", n, (n == 1) ? "" : "s");

Comment pouvez-vous dire une série d' isolement si les déclarations seraient plus lisibles? J'ai lu votre "bête" très bien . reportedAgeest l'exemple qui demande un peu de réflexion - probablement parce que c'est plus un cas particulier que de comprendreisThisYearALeapYear
Axeman

4

J'aime utiliser l'opérateur dans le code de débogage pour imprimer les valeurs d'erreur afin de ne pas avoir à les rechercher tout le temps. Habituellement, je fais cela pour les impressions de débogage qui ne resteront pas une fois le développement terminé.

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


Comment fonctionne la préséance / court-circuit des opérateurs dans ce cas?
Peter Mortensen

4

J'utilise presque jamais l'opérateur ternaire, parce que chaque fois que je fais l' utiliser, il me fait penser toujours beaucoup plus que je dois plus tard , lorsque je tente de le maintenir.

J'aime éviter la verbosité, mais quand cela rend le code beaucoup plus facile à saisir, j'opterai pour la verbosité.

Considérer:

String name = firstName;

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

name += " " + lastName;

Maintenant, c'est un peu verbeux, mais je le trouve beaucoup plus lisible que:

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

Ou:

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

Il semble simplement compresser trop d'informations dans trop peu d'espace, sans indiquer clairement ce qui se passe. Chaque fois que j'ai vu l'opérateur ternaire utilisé, j'ai toujours trouvé une alternative qui semblait beaucoup plus facile à lire ... là encore, c'est une opinion extrêmement subjective, donc si vous et vos collègues trouvez le ternaire très lisible, allez-y.


1
Ce n'est cependant pas exactement la même chose. Dans le deuxième exemple, vous compressez les trois instructions en une seule ligne. C'est ce qui diminue la lisibilité, pas l'opérateur ternaire.
ilitirit

Assez juste, j'ai mis à jour pour incorporer votre commentaire, mais il me semble toujours encombré ... mais encore une fois, c'est subjectif ... je ne dis pas que ternaire n'est pas lisible, je dis que ce n'est pas lisible pour moi (99 % du temps)
Mike Stone

4

Je les aime. Je ne sais pas pourquoi, mais je me sens très cool quand j'utilise l'expression ternaire.


3

Je traite beaucoup les opérateurs ternaires comme GOTO. Ils ont leur place, mais c'est quelque chose que vous devez généralement éviter pour rendre le code plus facile à comprendre.


3

Eh bien, la syntaxe est horrible. Je trouve les ifs fonctionnels très utiles, et ils rendent souvent le code plus lisible.

Je suggérerais de créer une macro pour la rendre plus lisible, mais je suis sûr que quelqu'un peut proposer un cas de bord horrible (comme il y en a toujours avec C ++).


de nombreuses implémentations et variantes BASIC ont une fonction IF qui remplace l'opérateur ternaire. J'ai vu un certain nombre de bases de code avec celle définie comme une macro dans C.
Sparr

Eh bien, je pensais aux langages de programmation fonctionnels, mais oui.
Marcin

"Faire une macro pour la rendre plus lisible", vous êtes un farceur!
niXar

3

Je l'utilise généralement dans des choses comme celle-ci:

before:

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

after:

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

Bien sûr, dans la plupart des langues, vous n'avez pas non plus besoin de la partie "== true" de ce ternaire.
Michael Haren

Je me rends compte que, même si j'ai tendance à le mettre simplement pour rendre le code plus lisible, car le compilateur devrait l'optimiser de la même manière que sans le == true de toute façon
KPexEA

dans aucune langue ne pouvez-vous avoir besoin de "== true"
niXar

J'ai eu du mal à décider de voter ou non. L'exemple est sympa mais le == TRUE est quelque chose que je ne supporte pas de voir dans le code des autres.
Peter Perháč

3

Comme d'autres l'ont souligné, ils conviennent pour de courtes conditions simples. Je les aime particulièrement pour les valeurs par défaut (un peu comme || et ou l' utilisation en JavaScript et Python), par exemple

int repCount = pRepCountIn ? *pRepCountIn : defaultRepCount;

Une autre utilisation courante consiste à initialiser une référence en C ++. Puisque les références doivent être déclarées et initialisées dans la même instruction, vous ne pouvez pas utiliser une instruction if .

SomeType& ref = pInput ? *pInput : somethingElse;

2
Étonnant que ce soit la première mention de l'initialisation des références, qui est l'un des rares endroits où "if" ne peut pas être utilisé à la place de?:. (Je suppose que ce n'est pas une question spécifique à C ++ ...) Ils sont également utiles dans les listes d'initialisation des constructeurs, pour la même raison.
j_random_hacker

2

J'aime le cas particulier de Groovy de l'opérateur ternaire, appelé l'opérateur Elvis:?:

expr ?: default

Ce code évalue à expr s'il n'est pas nul et par défaut s'il l'est. Techniquement, ce n'est pas vraiment un opérateur ternaire, mais il y est définitivement lié et permet de gagner beaucoup de temps / de frappe.


Ouais, j'aime celui-là aussi - il est ??en C #, l'opérateur de fusion
Jarrod Dixon

2

J'ai récemment vu une variation sur les opérateurs ternaires (enfin, en quelque sorte) qui font que la variante standard "()?:" Semble être un parangon de clarté:

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

ou, pour donner un exemple plus tangible:

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

Remarquez qu'il s'agit de JavaScript, donc des choses comme celles-là pourraient ne pas être possibles dans d'autres langues (heureusement).


1
wow, c'est horrible! imaginez en nicher quelques-uns ensemble! La seule chose vaguement utile que je peux voir avec cela est si vous aviez une fonction qui renvoyait un tableau à 2 éléments: var Name = getNames () [Gender == 'm']; ... mais c'est encore moins lisible!
nickf


2

Pour les cas simples, j'aime bien l'utiliser. En fait, il est beaucoup plus facile de lire / coder par exemple en tant que paramètres pour des fonctions ou des choses comme ça. Aussi pour éviter la nouvelle ligne j'aime garder avec tous mes if / else.

L'imbrication serait un grand non-non dans mon livre.

Donc, pour reprendre, pour un seul if / else j'utiliserai l'opérateur ternaire. Pour les autres cas, un if / else if / else (ou switch) régulier.


2

Pour les tâches simples, comme l'attribution d'une valeur différente en fonction d'une condition, elles sont excellentes. Je ne les utiliserais pas quand il y a des expressions plus longues en fonction de la condition.


2

Si vous et vos collègues comprenez ce qu'ils font et qu'ils ne sont pas créés en groupes massifs, je pense qu'ils rendent le code moins complexe et plus facile à lire car il y a tout simplement moins de code.

Le seul moment où je pense que les opérateurs ternaires rendent le code plus difficile à comprendre, c'est lorsque vous en avez plus de trois ou plus dans une ligne. La plupart des gens ne se souviennent pas qu'ils sont basés sur la bonne priorité et quand vous en avez une pile, la lecture du code devient un cauchemar.


2

Comme tant de réponses l'ont dit, cela dépend . Je trouve que si la comparaison ternaire n'est pas visible lors d'une analyse rapide du code, elle ne doit pas être utilisée.

En tant que problème secondaire, je pourrais également noter que son existence même est en fait un peu une anomalie en raison du fait qu'en C, le test de comparaison est une déclaration. Dans Icon , la ifconstruction (comme la plupart des Icon) est en fait une expression. Vous pouvez donc faire des choses comme:

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

... que je trouve beaucoup plus lisible qu'un opérateur de comparaison ternaire. :-)

Il y a eu une discussion récemment sur la possibilité d'ajouter l' ?:opérateur à Icon, mais plusieurs personnes ont souligné à juste titre qu'il n'y avait absolument aucun besoin en raison de la façon dont iffonctionne.

Ce qui signifie que si vous pouviez faire cela en C (ou dans l'un des autres langages qui ont l'opérateur ternaire), alors vous n'auriez pas, en fait, besoin de l'opérateur ternaire du tout.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.