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 totalement 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?
Utilisez-le pour expressions simples uniquement:
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 de manière à améliorer la lisibilité:
int a = (b > 10) ? some_value
: another_value;
Cela rend le débogage un peu 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 à caractères sûrs.
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 net que l'alternative.
Enchaîné, je vais bien - imbriqué, pas tellement.
J'ai tendance à les utiliser davantage en C simplement b/c, c'est une instruction if qui a de la 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 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 d'aussi simple, je pourrais simplement 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
. 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 des opérations ternaires peut entraîner un code qui prête à confusion (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ù une utilisation prudente 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 concis.
C'est une question de style, vraiment; les règles subconscientes que j'ai tendance à suivre sont:
foo = (bar > baz) ? true : false
, mais PAS foo = (bar > baz && lotto && someArray.Contains(someValue)) ? true : false
<%= (foo) ? "Yes" : "No" %>
(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 à analyser) que:
if (x) {
return "Yes";
} else {
return "No";
}
Maintenant, si votre expression conditionnelle est complexe, 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 ne 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 définira x
sur y
en supposant que y
n'est pas NULL
. Bon produit.
Dans mon esprit, cela n'a de sens que 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 des instructions if
ou de l'opérateur ternaire sont équivalentes. Donc, par cette mesure, la réponse est non , la complexité serait exactement la même qu'auparavant.
Par d'autres mesures telles que la lisibilité, la maintenabilité et DRY (Don't-Repeat-Yourself)), chaque 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 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 vous assurer que votre code est lisible. Toute construction peut être rendue illisible.
J'utilise l'opérateur ternaire partout où je peux, sauf si cela rend 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 m'étonne toujours de voir comment certaines personnes pensent que l'opérateur ternaire est une caractéristique "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 la lisibilité du tout. C'est une partie naturelle de la langue.
(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;
Je suis d'accord avec jmulder: il ne doit pas être utilisé à la place d'un 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, un meilleur support i18n du pluriel devrait être utilisé!
Si vous utilisez l'opérateur ternaire pour une affectation conditionnelle simple, je pense que ça va. Je l'ai vu (ab) utilisé pour contrôler le déroulement du programme sans même faire une affectation, et je pense que cela devrait être évité. Utilisez une instruction if dans ces cas.
Je les aime. 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 car c'était isValidDate et vérifié mois et jour aussi, 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ù, 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).
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 un peu délicates comme:
printf ("Deleted %d file%s\n", n, (n == 1) ? "" : "s");
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 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 retourne la dernière valeur calculée si elle est égale à l'état final.
Je n'utilise presque jamais l'opérateur ternaire parce que chaque fois que je l'utilise, cela me fait toujours penser beaucoup plus que je ne dois le faire plus tard lorsque j'essaye de le maintenir.
J'aime éviter la verbosité, mais quand cela rend le code beaucoup plus facile à saisir, je vais opter 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 préciser ce qui se passe. Chaque fois que je vois un 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.
J'aime utiliser l'opérateur dans le code de débogage pour imprimer les valeurs d'erreur, donc je n'ai pas à 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");
}
Comme d'autres l'ont souligné, ils sont agréables pour de courtes conditions simples. Je les aime particulièrement pour les valeurs par défaut (un peu comme l'utilisation et o en javascript et python), par exemple.
int repCount = pRepCountIn ? *pRepCountIn : defaultRepCount;
Une autre utilisation courante consiste à initialiser une référence en C++. Étant donné que 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;
Seulement quand:
$ var = (simple> test? simple_result_1: simple_result_2);
BAISER.
Eh bien, la syntaxe est horrible. Je trouve les ifs fonctionnels très utiles et rend 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 horrible boîtier Edge (comme il en existe toujours avec CPP).
J'utilise généralement dans des choses comme ça:
before:
if(isheader)
drawtext(x,y,WHITE,string);
else
drawtext(x,y,BLUE,string);
after:
drawtext(x,y,isheader==true?WHITE:BLUE,string);
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.
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.
La seule fois où je pense que les opérateurs ternaires rendent le code plus difficile à comprendre, c'est lorsque vous avez plus de 3 ou 4 sur une ligne. La plupart des gens ne se souviennent pas qu'ils ont une bonne priorité et lorsque vous en avez une pile, cela fait de la lecture du code un cauchemar.
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 modèle de clarté:
var Result = [CaseIfFalse, CaseIfTrue][(boolean expression)]
ou, pour donner un exemple plus tangible:
var Name = ['Jane', 'John'][Gender == 'm'];
Attention, c'est Javascript, donc des choses comme ça pourraient ne pas être possibles dans d'autres langues (heureusement).
Beaucoup 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 doit pas être utilisée.
Comme 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, les tests de comparaison sont une déclaration. Dans Icon, la construction if
(comme la plupart de 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 de ternery. :-)
Il y a eu récemment une discussion sur la possibilité d'ajouter le ?:
à Icon, mais plusieurs personnes ont correctement souligné qu'il n'y avait absolument aucun besoin en raison de la façon dont if
fonctionne.
Ce qui signifie que si vous pouviez le faire en C (ou dans n'importe quel autre langage ayant l'opérateur de ternerie), vous n'auriez en fait pas du tout besoin de l'opérateur de ternerie.
Pour les cas simples, j'aime bien l'utiliser. En fait, il est beaucoup plus facile de lire/coder par exemple comme paramètres pour des fonctions ou des choses comme ça. Aussi pour éviter la nouvelle ligne que j'aime garder avec tous mes if/else.
Nesetting ce serait un gros NON-NON dans mon livre.
Donc, en reprenant, pour un seul si/sinon j'utiliserai l'opérateur ternaire. Pour les autres cas, un if/else if/else (ou un interrupteur) normal
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 nul et par défaut s'il l'est. Techniquement, ce n'est pas vraiment un opérateur ternaire, mais il est définitivement lié et économise beaucoup de temps/de frappe.
Je traite les opérateurs ternaires un peu comme GOTO. Ils ont leur place, mais c'est quelque chose que vous devriez généralement éviter pour rendre le code plus facile à comprendre.
L'opérateur ternaire, haut la main. Ils ne sont pas complexes si vous formatez correctement. Prenez l'exemple de l'année bissextile de @paxdiablo:
$isLeapYear =
(($year % 400) == 0)
? 1
: ((($year % 100) == 0)
? 0
: ((($year % 4) == 0)
? 1
: 0));
Cela peut être écrit plus concis et être rendu beaucoup plus lisible avec ce formatage:
//--------------test expression-----result
$isLeapYear = (($year % 400) == 0) ? 1 :
((($year % 100) == 0)? 0 :
((($year % 4) == 0) ? 1 :
0));//default result
l'utiliser pour:
var status = statuses[error == null ? 'working' : 'stopped'];
function getFullName(){
return this.isMale() ? "Mr. " : "Ms. "+ this.name;
}
initialiser les variables:
var formMethod = DEBUG_FLAG == true ? "GET" : "POST";
function(object){
var prop1 = typeof object.property == 'undefined'
? "default prop"
: object.property;
//...
}
les exemples de code sont en javascript
Ma règle empirique récemment formulée pour déterminer si vous devez utiliser l'opérateur ternaire est la suivante:
Et soyez gentil avec les lecteurs de votre code. Si vous imbriquez des opérateurs ternaires, formatez le code pour rendre cette imbrication évidente.
Je dirais que le nombre de conditions dans une expression logique rend la lecture plus difficile. Cela est vrai d'une instruction if et cela est vrai d'un ternaire opérateur. Dans un monde parfait, il devrait y avoir ne raison résumable de prendre une branche plutôt que d'autres. Il y a de fortes chances qu'il s'agisse davantage d'une "règle commerciale" si votre explication est "uniquement lorsque ce groupe d'états se produit".
Cependant, dans le monde réel, nous n'ajoutons pas d'étapes intermédiaires pour plier les états en un seul état exprimable simplement pour obéir au cas idéal. Nous avons fait des déductions sur plusieurs États et devons prendre une décision sur la façon de les gérer.
I comme ternaires car il est possible de faire n'importe quoi avec une instruction if.
if( object.testSomeCondition()) {
System.exec( "format c:" );
}
else {
a++;
}
D'autre part:
a += ( object.testSomeCondition() ? 0 : 1 );
indique clair que le but est de trouver une valeur pour a
. Bien sûr, conformément à cela, il y a probablement ne devrait pas être plus que des effets secondaires raisonnables.
J'utilise un if
pour les conditions longues ou complexes après avoir décidé si j'ai le temps de retravailler les conditions en amont afin de répondre à une question plus simple. Mais quand j'utilise un if, j'encore essaie de faire parallèle le traitement, juste sous une condition différente.
if ( user.hasRepeatedlyPressedOKWithoutAnswer()
&& me.gettingTowardMyLunchtime( time )
) {
...
}
Mon objectif est également near - traitement à flux unique. Donc j'essaie souvent pas de faire un else
et un if
est simplement une étape hors du chemin commun. Lorsque vous effectuez beaucoup de traitement à flux unique, il est beaucoup plus difficile pour les bogues de se cacher dans votre code en attendant cette seule condition qui sautera et cassera les choses.
Comme je l'ai dit ci-dessus, si vous utilisez un ternaire pour définir ne chose, ou si vous avez un petit nombre de cas que vous souhaitez tester afin de le définir sur une valeur, alors je viens de comme le lisibilité d'un ternaire.
Avec une mise en garde -> PAS DE CLAUSES COMPLEXES vraies
a = b == c ? ( c == d ? ( c == e ? f : g ) : h ) : i;
Bien sûr, cela peut être décomposé en:
a = b != c ? i
: c != d ? h
: c == e ? f
: g
;
Et cela ressemble à une table de vérité (compressée).
N'oubliez pas qu'il existe plus important facteurs de lisibilité, l'un d'eux est la longueur du bloc et l'autre le niveau d'indentation. Faire des choses simples dans les ternaires ne crée pas un élan vers des niveaux d'indentation de plus en plus poussés.
Non, ils sont difficiles à lire. If/Else est beaucoup plus facile à lire.
C'est mon opinion. Votre kilométrage peut varier.
Je l'aime beaucoup. Quand je l'utilise, je l'écris comme un if-then-else: une ligne pour la condition, l'action vraie et l'action fausse. De cette façon, je peux les imbriquer facilement.
Exemple:
x = (a == b ? (sqrt (a) -2) : (a * a + b * b) ); x = (a == b ? (sqrt (a) -2) : (a * a + b * b) ) ; x = (a == b ? (c> d ? (sqrt (a) -2) : (c + cos (d) ) ) : (a * a + b * b) );
Pour moi, c'est assez facile à lire. Cela facilite également l'ajout de sous-cas ou la modification de cas existants.
Personne ne semble mentionner l'utilisation de l'opérateur ternaire, au moins dans des langages comme D qui prennent en charge l'inférence de type, pour permettre à l'inférence de type de fonctionner pour des types de modèles incroyablement compliqués.
auto myVariable = fun();
// typeof(myVariable) == Foo!(Bar, Baz, Waldo!(Stuff, OtherStuff)).
// Now I want to declare a variable and assign a value depending on some
// conditional to it.
auto myOtherVariable = (someCondition) ? fun() : gun();
// If I didn't use the ternary I'd have to do:
Foo!(Bar, Baz, Waldo!(Stuff, OtherStuff)) myLastVariable; // Ugly.
if(someCondition) {
myLastVariable = fun();
} else {
myLastVariable = gun():
}
Non, les opérateurs ternaires pas augmentent la complexité. Malheureusement, certains développeurs sont tellement orientés vers un style de programmation impératif qu'ils rejettent (ou n'apprendront pas) autre chose. Je ne pense pas que, par exemple:
int c = a < b ? a : b;
est "plus complexe" que l'équivalent (mais plus verbeux):
int c;
if (a < b) {
c = a;
} else {
c = b;
}
ou encore plus maladroit (que j'ai vu):
int c = a;
if (!a < b) {
c = b;
}
Cela dit, examinez attentivement vos alternatives au cas par cas. En supposant un développeur correctement formé, demandez lequel exprime le plus succinctement l'intention de votre code et suivez-le.
J’étais dans le camp "les opérateurs ternaires rendent une ligne illisible", mais ces dernières années, j’ai appris à les aimer lorsqu’ils sont utilisés avec modération. Les opérateurs ternaires sur une seule ligne peuvent augmenter la lisibilité si tout le monde dans votre équipe comprend ce qui se passe. C'est une façon concise de faire quelque chose sans les frais généraux de nombreuses accolades pour le bien des accolades.
Les deux cas où je ne les aime pas: s’ils vont trop loin au-delà de la barre des 120 colonnes ou s’ils sont intégrés dans d’autres opérateurs ternaires. Si vous ne pouvez pas exprimer rapidement, facilement et de manière lisible ce que vous faites chez un opérateur ternaire. Utilisez ensuite l'équivalent if/else.
Ça dépend :)
Ils sont utiles lorsqu'il s'agit de références éventuellement nulles (btw: Java a vraiment besoin d'un moyen pour comparer facilement deux chaînes éventuellement nulles).
Le problème commence lorsque vous imbriquez plusieurs opérateurs ternaires dans une seule expression.
Non (sauf s'ils sont mal utilisés). Lorsque l'expression fait partie d'une expression plus large, l'utilisation d'un opérateur ternaire est souvent beaucoup plus claire.
L'opérateur ternaire est extrêmement utile pour produire de manière concise des listes séparées par des virgules. Voici un exemple Java:
int[] iArr = {1,2,3};
StringBuilder sb = new StringBuilder();
for (int i = 0; i < iArr.length; i++) {
sb.append(i == 0 ? iArr[i] : "," + iArr[i]);
}
System.out.println(sb.toString());
produit: "1,2,3"
Sinon, le boîtier spécial pour la dernière virgule devient ennuyeux.
Si vous essayez de réduire la quantité de lignes dans votre code ou de refactoriser le code, alors allez-y.
Si vous vous souciez du prochain programmeur qui doit prendre 0,1 milliseconde supplémentaire pour comprendre l'expression, allez-y quand même.
Réduire le code ne signifie pas toujours qu'il est plus facile à analyser. En diffère d'une langue à l'autre. Dans PHP par exemple, les espaces et les sauts de ligne sont encouragés car le lexeur de PHP décompose d'abord le code en bits commençant par les sauts de ligne, puis les espaces. Donc, je ne vois pas de problème de performances, sauf si moins d'espace est utilisé.
Mauvais:
($var)?1:0;
Bien:
($var) ? 1 : 0;
Cela ne semble pas être un gros problème, mais avec le code de lexing en PHP, les espaces sont essentiels. De plus, il se lit également un peu mieux de cette façon.
chaîne someSay = bCanReadThis? "Non Oui";
J'utilise et recommande des ternaires pour éviter les lignes de code dans des situations où la logique est triviale.
int i;
if( piVal ) {
i = *piVal;
} else {
i = *piDefVal;
}
Dans le cas ci-dessus, je choisirais un ternaire, car il a moins de bruit:
int i = ( piVal ) ? *piVal : *piDefVal;
De même, les valeurs de retour conditionnelles sont de bons candidats:
return ( piVal ) ? *piVal : *piDefVal;
Je pense que la compacité peut améliorer la lisibilité, ce qui contribue à améliorer la qualité du code.
Mais lisibilité dépend toujours de l'audience du code.
Les lecteurs doivent être capables de comprendre le a ? b : c
modèle sans aucun effort mental. Si vous ne pouvez pas présumer cela, optez pour la version longue.
Je pense que cela dépend vraiment du contexte dans lequel ils sont utilisés.
Quelque chose comme ça serait une façon vraiment déroutante, bien qu'efficace, de les utiliser:
__CRT_INLINE int __cdecl getchar (void)
{
return (--stdin->_cnt >= 0)
? (int) (unsigned char) *stdin->_ptr++
: _filbuf (stdin);
}
Cependant, ceci:
c = a > b ? a : b;
est parfaitement raisonnable.
Personnellement, je pense qu'ils devraient être utilisés lorsqu'ils réduisent les déclarations FI trop verbeuses. Le problème est que les gens en sont pétrifiés ou les aiment tellement qu'ils sont utilisés presque exclusivement au lieu des déclarations des FI.
Comment gagnerait-on un concours de code obscurci sans l'opérateur ternaire?!
Personnellement, je l'utilise, le cas échéant, mais je ne pense pas que je l'aurais jamais imbriqué. C'est très utile, mais il a quelques inconvénients en ce sens qu'il rend le code plus difficile à lire et est utilisé dans d'autres langages dans d'autres opérations (comme le contrôle nul de Groovy).
Je suis d'accord avec les sentiments de nombreuses affiches ici. L'opérateur ternaire est parfaitement valide tant qu'il est utilisé correctement et n'introduit pas d'ambiguïté (pour être juste, vous pouvez dire cela à propos de n'importe quel opérateur/construction).
J'utilise souvent l'opérateur ternaire dans du code intégré pour clarifier ce que fait mon code. Prenez les exemples de code suivants (simplifiés à des fins de clarté):
Extrait 1:
int direction = read_or_write(io_command);
// Send an I/O
io_command.size = (direction==WRITE) ? (32 * 1024) : (128 * 1024);
io_command.data = &buffer;
dispatch_request(io_command);
Extrait 2:
int direction = read_or_write(io_command);
// Send an I/O
if (direction == WRITE) {
io_command.size = (32 * 1024);
io_command.data = &buffer;
dispatch_request(io_command);
} else {
io_command.size = (128 * 1024);
io_command.data = &buffer;
dispatch_request(io_command);
}
Ici, j'expédie une demande d'entrée ou de sortie. Le processus est le même que la demande soit en lecture ou en écriture, seule la taille d'E/S par défaut change. Dans le premier exemple, j'utilise l'opérateur ternaire pour indiquer clairement que la procédure est la même et que le champ size
obtient une valeur différente selon la direction des E/S. Dans le deuxième exemple, il n'est pas aussi clair que l'algorithme pour les deux cas est le même (d'autant plus que le code croît beaucoup plus longtemps que trois lignes). Le deuxième exemple serait plus difficile à synchroniser avec le code commun. Ici, l'opérateur ternaire exprime mieux la nature largement parallèle du code.
L'opérateur ternaire a un autre avantage (bien qu'il ne soit normalement qu'un problème avec les logiciels embarqués). Certains compilateurs ne peuvent effectuer certaines optimisations que si le code n'est pas "imbriqué" au-delà d'une certaine profondeur (c'est-à-dire à l'intérieur d'une fonction, vous augmentez la profondeur d'imbrication de 1 chaque fois que vous entrez une instruction if, loop ou switch et la diminuez de 1 lorsque vous le laissez). À l'occasion, l'utilisation de l'opérateur ternaire peut minimiser la quantité de code qui doit être à l'intérieur d'un conditionnel (parfois au point où le compilateur peut optimiser le conditionnel) et peut réduire la profondeur d'imbrication de votre code. Dans certains cas, j'ai pu restructurer une logique à l'aide de l'opérateur ternaire (comme dans mon exemple ci-dessus) et réduire suffisamment la profondeur imbriquée de la fonction pour que le compilateur puisse effectuer des étapes d'optimisation supplémentaires. Certes, c'est un cas d'utilisation assez étroit, mais je pensais que cela valait la peine d'être mentionné de toute façon.
À petites doses, ils peuvent réduire le nombre de lignes et rendre le code plus lisible; en particulier si le résultat est quelque chose comme définir une chaîne de caractères sur "Oui" ou "Non" en fonction du résultat d'un calcul.
Exemple:
char* c = NULL;
if(x) {
c = "true";
}else {
c = "false";
}
comparé à:
char* c = x ? "Yes" : "No";
Le seul bogue qui peut se produire dans des tests simples comme celui-ci est l'attribution d'une valeur incorrecte, mais comme le conditionnel est généralement simple, il est moins probable que le programmeur se trompe. Le fait que votre programme imprime la mauvaise sortie n'est pas la fin du monde et devrait être pris en compte dans toutes les phases de révision de code, de test en laboratoire et de test de production.
Je vais contrer mon propre argument car il est désormais plus difficile d'utiliser des mesures de couverture de code pour vous aider à savoir à quel point vos cas de test sont bons. Dans le premier exemple, vous pouvez tester la couverture sur les deux lignes d'affectation; si l'un n'est pas couvert, vos tests n'exercent pas tous les flux de code possibles.
Dans le deuxième exemple, la ligne s'affiche comme étant exécutée quelle que soit la valeur de X, vous ne pouvez donc pas être certain d'avoir testé le chemin alternatif (YMMV selon la capacité de vos outils de couverture).
Cela est d'autant plus important avec la complexité croissante des tests.
si votre opérateur ternaire finit par prendre toute la largeur de l'écran, je ne l'utiliserais pas. Je le garde juste pour vérifier une condition simple et retourner des valeurs simples:
int x = something == somethingElse ? 0 : -1;
Nous avons en fait un code désagréable comme celui-ci en production ... pas bon:
int x = something == (someValue == someOtherVal ? string.Empty : "Blah blah") ? (a == b ? 1 : 2 ): (c == d ? 3 : 4);
J'aime l'opérateur dans certaines situations, mais je pense que certaines personnes ont tendance à trop l'utiliser et que cela peut rendre le code plus difficile à lire.
J'ai récemment trébuché sur cette ligne dans un code open source que je travaille à modifier.
where
(active == null ? true :
((bool)active ? p.active : !p.active)) &&...
Au lieu de
where ( active == null || p.active == active) &&...
Je me demande si l'utilisation ternaire ajoute une surcharge supplémentaire à l'instruction LINQ dans ce cas.
Anecdote intéressante: j'ai vu l'optimiseur peser l'opérateur ternaire comme moins "lourd" aux fins de l'inline que l'équivalent si. J'ai remarqué cela avec les compilateurs Microsoft, mais cela pourrait être plus répandu.
En particulier, des fonctions comme celle-ci impliqueraient:
int getSomething()
{
return m_t ? m_t->v : 0;
}
Mais cela ne serait pas:
int getSomething()
{
if( m_t )
return m_t->v;
return 0;
}