Pour ternaire ou de ne pas ternaire? [fermé]
Je suis personnellement un défenseur de l'opérateur ternaire: ()? :; Je me rends compte qu'il a sa place, mais j'ai rencontré de nombreux programmeurs qui sont complètement contre de l'utiliser, et certains qui l'utilisent trop souvent.
Quels sont vos sentiments à ce sujet? Quel code intéressant avez-vous vu l'utiliser?
30 réponses
Utiliser pour expressions simples seulement:
int a = (b > 10) ? c : d;
Ne pas enchaîner ou imbriquer 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 d'une manière qui améliore la lisibilité:
int a = (b > 10) ? some_value
: another_value;
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.
Je les aime, surtout dans les langues à sécurité de type.
Je ne vois pas comment cela:
int count = (condition) ? 1 : 0;
Est plus difficile que cela:
int count;
if (condition)
{
count = 1;
}
else
{
count = 0;
}
Modifier -
Je dirais que les opérateurs ternaires rendent tout moins complexe et plus soigné que l'alternative.
Enchaîné je suis bien avec-imbriqué, pas tellement.
J'ai tendance à les utiliser plus en C simplement b / C ils sont une instruction if qui a une valeur, donc elle réduit les répétitions inutiles ou les variables:
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 dans ruby d'autre part, je suis plus susceptible d'utiliser if...else...end
parce 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 de ce simple, je pourrais juste utiliser l'opérateur ternaire de toute façon).
L'opérateur ternaire ?:
n'est qu'un équivalent fonctionnel de la construction procédurale if
. Donc, 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 des opérations ternaires peut entraîner un code carrément 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 y a beaucoup de situations où l'utilisation conservatrice de l'opérateur ?:
peut entraîner 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 seulement en relation avec la propriété reverseOrder) le résultat final n'est pas particulièrement laconique.
C'est une question de style, vraiment; les règles subconscientes que j'ai tendance à suivre sont:
- évaluer Seulement 1 expression - si
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'utilise vraiment que pour l'affectation; jamais de logique de flux (donc jamaisla logique de flux dans ternaire est elle-même un mensonge, ignorez ce dernier point.(foo) ? FooIsTrue(foo) : FooIsALie(foo)
)
Je l'aime parce qu'il est concis et élégant pour les opérations d'affectation simples.
Comme tant de questions d'opinion, la réponse est inévitablement: Cela dépend
, Pour quelque chose comme:
return x ? "Yes" : "No";
Je pense que c'est beaucoup plus concis (et plus rapide pour moi pour 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 aparté, si vous êtes un programmeur C, GCC a en fait un extension {[24] } qui vous permet d'exclure la partie if-true du ternaire, comme ceci:
/* 'y' is a char * */
const char *x = y ? : "Not set";
, Qui va ensemble x
à y
en supposant que y
n'est pas NULL
. De bonnes choses.
Dans mon esprit, il est logique d'utiliser l'opérateur ternaire dans les cas où une expression est nécessaire.
Dans d'autres cas, il semble que l'opérateur ternaire diminue la clarté.
Par la mesure de complexité cyclomatique, l'utilisation de if
déclarations ou l'opérateur ternaire sont équivalentes. 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 séchage (ne pas répéter), l'un ou l'autre choix peut s'avérer meilleur que l'autre.
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 optionnel est null.
Exemple Artificiel:
var e = new XElement("Something",
param == null ? new XElement("Value", "Default")
: new XElement("Value", param.ToString())
);
Ou (merci asterite)
var e = new XElement("Something",
new XElement("Value",
param == null ? "Default"
: param.ToString()
)
);
Peu importe si vous utilisez l'opérateur ternaire ou non, assurez-vous que votre code est lisible est la chose importante. Toute construction peut être rendue illisible.
J'utilise l'opérateur ternaire 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.
Il me déroute toujours comment certaines personnes pensent que l'opérateur ternaire est une fonctionnalité "cachée" ou est quelque peu mystérieuse. C'est l'une des premières choses que j'ai apprises quand je commence à programmer en C, et je ne pense pas que cela diminue la lisibilité du tout. C'est une partie naturelle de la langue.
Je suis d'accord avec jmulder: il ne devrait pas être utilisé à la place d'un if
, mais il a sa place pour l'expression de retour ou dans une expression:
echo "Result: " + n + " meter" + (n != 1 ? "s" : "");
return a == null ? "null" : a;
Le premier n'est qu'un exemple, un meilleur support I18N du pluriel devrait être utilisé!
Si vous utilisez l'opérateur ternaire pour une simple affectation conditionnelle, je pense que c'est bien. Je l'ai vu (ab) utilisé pour contrôler le flux de programme sans même faire une affectation, et je pense que cela devrait être évité. Utilisez une instruction if dans ces cas.
(Hack du jour)
#define IF(x) x ?
#define ELSE :
, Alors vous pouvez faire si-alors-sinon comme expression:
int b = IF(condition1) res1
ELSE IF(condition2) res2
ELSE IF(conditions3) res3
ELSE res4;
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 comme 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 est 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 assez nécessaire que dans Python (comme un exemple), un tel idiome pourrait ê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.
Je les aime bien. Je ne sais pas pourquoi, mais je me sens très cool quand j'utilise l'expression ternaire.
J'ai vu de telles bêtes comme (c'était en fait bien pire puisque c'était isValidDate et vérifié mois et jour aussi, mais je ne pouvais pas être dérangé en essayant de me souvenir de tout):
isLeapYear =
((yyyy % 400) == 0)
? 1
: ((yyyy % 100) == 0)
? 0
: ((yyyy % 4) == 0)
? 1
: 0;
Où, clairement, 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");
J'aime utiliser l'opérateur dans le code de débogage pour imprimer les valeurs d'erreur, donc je n'ai pas besoin de les rechercher tout le temps. Habituellement, je le fais pour les impressions de débogage qui ne resteront pas une fois que j'ai terminé le développement.
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");
}
Je n'utilise presque jamais l'opérateur ternaire car chaque fois que je l'utilise, cela me fait toujours penser beaucoup plus que ce que je dois faire plus tard lorsque j'essaie de le maintenir.
J'aime éviter la verbosité, mais quand cela rend le code beaucoup plus facile à ramasser, je vais opter pour la verbosité.
Considérez:
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 juste compresser trop d'informations dans trop peu d'espace, sans le rendre clair ce qui se passe. Chaque fois que je vois 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 ternaire très lisible, allez-y.
Eh bien, la syntaxe pour cela est horrible. Je trouve les ifs fonctionnels très utiles, et rend souvent le code plus lisible.
Je suggère de faire une macro pour la rendre plus lisible, mais je suis sûr que quelqu'un peut trouver un horrible cas de bord (comme il y en a toujours avec CPP).
Seulement quand:
$ var = (simple > test ? simple_result_1: simple_result_2);
Baiser.
J'utilise généralement dans des choses comme ceci:
before:
if(isheader)
drawtext(x,y,WHITE,string);
else
drawtext(x,y,BLUE,string);
after:
drawtext(x,y,isheader==true?WHITE:BLUE,string);
Comme d'autres l'ont souligné, ils sont agréables pour des conditions simples et courtes. J'aime particulièrement pour les valeurs par défaut (un peu comme le || et ou l'utilisation de javascript et python), par exemple:
int repCount = pRepCountIn ? *pRepCountIn : defaultRepCount;
Une autre utilisation courante consiste à initialiser une référence en C++. Depuis références doivent être déclarées et initialisées dans la même déclaration, vous ne pouvez pas utiliser une instruction if.
SomeType& ref = pInput ? *pInput : somethingElse;
Je traite les opérateurs ternaires un peu comme GOTO. Ils ont leur place, mais ils sont quelque chose que vous devriez généralement éviter pour rendre le code plus facile à comprendre.
J'ai récemment vu une variation sur les opérateurs ternaires (enfin, en quelque sorte) qui font la norme" ()? : "variant semble être un parangon de clarté:
var Result = [CaseIfFalse, CaseIfTrue][(boolean expression)]
Ou, pour donner un exemple concret:
var Name = ['Jane', 'John'][Gender == 'm'];
Rappelez-vous, C'est Javascript, donc des choses comme ça pourraient ne pas être possibles dans d'autres langues (heureusement).
Pour les cas simples, j'aime 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 que j'aime garder avec tout mon if / else.
Neseting ce serait un grand NON-NON dans mon livre.
Donc, en reprenant, pour un seul if / else je vais utiliser l'opérateur ternaire. Pour les autres cas, un if/else if/else (ou switch)
J'aime le cas particulier de Groovy de l'opérateur ternaire, appelé l'opérateur Elvis:?:
expr ?: default
Ce code est évalué à expr s'il n'est pas null, et par défaut s'il l'est. Techniquement, ce n'est pas vraiment un opérateur ternaire, mais c'est définitivement lié à cela et permet d'économiser beaucoup de temps/Dactylographie.
Pour des tâches simples comme l'attribution d'une valeur différente en fonction d'une condition, elles sont géniales. Je ne les utiliserais pas quand il y a des expressions plus longues en fonction de la condition tho.
Tant de réponses ont dit, Cela dépend . Je trouve que si la comparaison ternaire n'est pas visible dans une analyse rapide du code, elle ne devrait pas être utilisée.
En tant que problème secondaire, je pourrais également noter que son existence même est en fait un peu anomolique en raison du fait qu'en C, le test de comparaison est une déclaration. Dans Icon, la construction if
(comme la plupart des Icon) est en fait une expression. Donc, vous pouvez 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 ternery. :-)
Il y a eu récemment une discussion sur la possibilité d'ajouter l'opérateur ?:
à Icon, mais plusieurs personnes ont souligné à juste titre qu'il n'y avait absolument pas besoin en raison de la façon dont if
fonctionne.
Ce qui signifie que si vous pouviez le faire en C (ou dans l'une des autres langues qui ont l'opérateur ternery), alors vous n'auriez pas du tout besoin de l'opérateur ternery.
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 simplement moins de code.
La seule fois où je pense que les opérateurs ternaires rendent le code plus difficile à comprendre est quand vous avez plus de 3 ou 4 dans une ligne. La plupart des gens ne se souviennent pas qu'ils sont une priorité basée sur le droit et quand vous en avez une pile, Cela fait de la lecture du code un cauchemar.