Par exemple, préférez-vous ce monoplace
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;
}
ou une solution if/else impliquant plusieurs déclarations de retour?
Quand est ?:
approprié, et quand n'est-ce pas? Doit-il être enseigné ou caché aux débutants?
L'opérateur ternaire est-il mauvais?
Non, c'est une bénédiction.
Quand est?: Approprié?
Quand c'est quelque chose de si simple, vous ne voulez pas perdre beaucoup de lignes.
et quand n'est-ce pas?
Lorsque la lisibilité et la clarté du code en souffrent et qu'un risque d'erreur par une attention insuffisante augmente, par exemple, avec de nombreux opérateurs chaînés, comme dans votre exemple.
Le test décisif est lorsque vous commencez à douter que votre code soit facilement lisible et maintenable à long terme. Alors ne le fais pas.
Je pense que l'opérateur ternaire non imbriqué (c'est-à-dire une déclaration où il n'est utilisé qu'une seule fois) est bien, mais si vous en imbriquez plusieurs, il devient quelque peu difficile à lire.
Quand est?: Approprié
et quand n'est-ce pas?
Si vous avez des appels de logique ou de fonction à l'intérieur de l'expression ternaire, vous le rendez horrible à regarder.
Une différence que (je pense) personne n'a souligné est que if-else ne peut pas retourner une valeur, contrairement à l'opérateur ternaire.
Venant de F #, j'aime parfois utiliser l'opérateur ternaire pour imiter la correspondance de motifs.
match val with
| A -> 1
| B -> 3
| _ -> 0
contre
return val == A ? 1 :
val == B ? 3 :
0;
Un exemple (à mon humble avis) d'une utilisation valide:
printf("Success in %d %s\n", nr_of_tries, (nr_of_tries == 1 ? "try" : "tries"));
Il en résulte un code plus lisible que d'avoir 2 instructions d'impression distinctes. Les exemples imbriqués dépendent: (compréhensible? Oui: non)
Absolument pas mal. En fait, c'est pur , et si-alors-autre ne l'est pas.
Dans les langages fonctionnels comme Haskell, F #, ML etc., ce sont les déclarations si-alors-autre qui sont considérées comme mauvaises.
La raison en est que toute "action" comme une instruction if-then-else impérative vous oblige à séparer une déclaration de variable de sa définition et introduit état à votre fonction.
Par exemple, dans le code suivant:
const var x = n % 3 == 1
? Parity.Even
: Parity.Odd;
vs.
Parity x;
if (n % 3 == 1)
x = Parity.Even;
else
x = Parity.Odd;
Le premier a deux avantages en plus d'être plus court:
x
est une constante, et offre donc beaucoup moins de chances d'introduire des bogues, et peut potentiellement être optimisé d'une manière que le second ne pourrait jamais être.x
doit être de type Parity
.De façon confuse, dans les langages fonctionnels, l'opérateur ternaire est souvent appelé if-then-else. À Haskell, vous pourriez dire x = if n mod 3 == 1 then Odd else Even
.
Cette expression particulière me fait mal aux yeux; Je dénigrerais n'importe quel développeur de mon équipe qui l'aurait utilisé parce qu'il est impossible à maintenir.
Les opérateurs ternaires ne sont pas mauvais lorsqu'ils sont bien utilisés. Ils n'ont même pas besoin d'être des lignes simples; Un long formaté peut être très clair et facile à comprendre:
return
( 'a' == $s ) ? 1
: ( 'b' == $s ) ? 2
: ( 'c' == $s ) ? 3
: 4;
J'aime mieux que la chaîne if/then/else équivalente:
if ( 'a' == $s ) {
$retval = 1;
}
elsif ( 'b' == $s ) {
$retval = 2;
}
elsif ( 'c' == $s ) {
$retval = 3;
}
else {
$retval = 4;
}
return $retval;
Je vais les reformater en:
if ( 'a' == $s ) { $retval = 1; }
elsif ( 'b' == $s ) { $retval = 2; }
elsif ( 'c' == $s ) { $retval = 3; }
else { $retval = 4; }
return $retval;
si les conditions et affectations permettent un alignement facile. Je préfère toujours la version ternaire car elle est plus courte et n'a pas autant de bruit autour des conditions et des affectations.
ReSharper dans VS.NET suggère parfois de remplacer if...else
avec le ?:
opérateur.
Il semble que ReSharper ne suggère que si les conditions/blocs sont inférieurs à un certain niveau de complexité, sinon il s'en tient à if...else
.
Voici un exemple de quand il est mal:
oldValue = newValue >= 0 ? newValue : oldValue;
C'est déroutant et inutile. Le compilateur pourrait optimiser la deuxième expression (oldValue = oldValue), mais pourquoi le codeur a-t-il fait cela en premier lieu?
Un autre doozy:
thingie = otherThingie != null ? otherThingie : null;
Certaines personnes ne sont tout simplement pas censées être des codeurs ...
Greg dit que la déclaration if équivalente est "bruyante". C'est si vous l'écrivez bruyamment. Mais ce même si peut être écrit comme:
if ('a' == $s) return 1;
if ('b' == $s) return 2;
if ('c' == $s) return 3;
return 4;
Ce qui n'est pas plus bruyant que le ternaire. Je me demande si les raccourcis ternaires; toutes les expressions sont-elles évaluées?
Loin d'être méchant, l'opérateur ternaire est une aubaine.
Il est très utile lorsque vous souhaitez prendre une décision dans une expression imbriquée. L'exemple classique est un appel de fonction:
printf("I see %d evil construct%s in this program\n", n, n == 1 ? "" : "s");
Dans votre exemple particulier, le ternaire est presque gratuit, car il s'agit de l'expression de niveau supérieur sous un return
. Vous pouvez lever le conditionnel au niveau de l'instruction sans dupliquer autre chose que le mot clé return
.
N.B. Rien ne rendra jamais cet algorithme particulier pour la médiane facile à lire.
Mis à part les arguments "diaboliques", dans mon expérience, j'ai trouvé une forte corrélation entre l'utilisation d'un opérateur ternaire par un programmeur et la probabilité que sa base de code entière soit difficile à lire, à suivre et à maintenir (sinon carrément non documentée). Si un programmeur est plus soucieux de sauvegarder quelques lignes de 1 à 2 caractères que quelqu'un capable de comprendre son code, alors toute confusion mineure comprenant une déclaration ternaire n'est généralement que la pointe de l'iceberg.
Les opérateurs ternaires attirent des nombres magiques comme s ** t attire les mouches.
Si je cherchais une bibliothèque Open Source pour résoudre un problème particulier et que je voyais du code tel que les opérateurs ternaires de l'affiche originale chez un candidat pour cette bibliothèque, des cloches d'avertissement commenceraient à sonner dans ma tête et je commencerais à envisager de passer à autre chose. à un autre projet à emprunter.
Mal? Regardez, ils sont juste différents.
if
est une instruction. (test ? a : b)
est une expression. Ce n'est pas la même chose.
Les expressions existent pour exprimer des valeurs. Des instructions existent pour effectuer des actions. Les expressions peuvent apparaître à l'intérieur des instructions, mais pas vice-versa. Vous pouvez donc utiliser des expressions ternaires dans d'autres expressions, comme pour les termes d'une sommation, ou pour les arguments d'une méthode, etc. Vous n'avez pas devez, mais vous pouvez si vous voulez. Il n'y a rien de mal à cela. Certaines personnes pourraient dire que c'est mal, mais c'est leur opinion.
Une valeur d'une expression ternaire est qu'elle vous permet de gérer à la fois les vrais et les faux cas. Les instructions if
ne le font pas.
Si vous vous inquiétez de la lisibilité, vous pouvez les formater de manière lisible.
D'une certaine manière, le "mal" s'est glissé dans le vocabulaire de la programmation. J'adorerais savoir qui l'a abandonné en premier. (En fait, j'ai un suspect - il est au MIT.) Je préfère que nous ayons des raisons objectives de juger la valeur dans ce domaine, pas seulement le goût des gens et les injures.
Cela peut être reformaté pour être aussi joli que la combinaison 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;
}
Mais le problème est que je ne suis pas vraiment sûr d'avoir le droit d'indentation pour représenter ce qui se passera réellement. :-)
Puis-je le dire? Je n'arrive pas à trouver cette application particulière de l'opération ternaire evil:
Veuillez avoir pitié, ma réputation est déjà assez pitoyable.
Tout ce qui rend votre code plus laid est mauvais.
Si vous utilisez ternaire pour rendre votre code plus propre, utilisez-le. Parfois, comme PHP, c'est génial de faire des substitutions en ligne, par exemple.
"Hello ".($Male?"Mr":"Ms")." $Name
Cela économise quelques lignes, et c'est assez clair, mais votre exemple a besoin d'au moins un meilleur formatage pour être clair, et le ternaire n'est pas vraiment bon pour les lignes multiples, alors vous pourriez aussi bien utiliser if/else.
Plus grande victoire: montrant qu'il n'y a qu'une seule cible d'action.
if ( $is_whatever )
$foo = 'A';
else
$foo = 'B';
Il existe deux chemins de code que vous pouvez suivre, et le lecteur doit lire attentivement pour voir quelles deux variables sont définies. Dans ce cas, ce n'est qu'une variable, mais le lecteur a plus à lire pour le comprendre. Après tout, cela aurait pu être ceci:
if ( $is_whatever )
$foo = 'A';
else
$bar = 'B';
Avec l'opérateur ternaire, il est clair qu'une seule variable est définie.
$foo = $is_whatever ? 'A' : 'B';
Au niveau le plus bas, c'est le principe DRY (Don't Repeat Yourself) à sa base). Si vous pouvez spécifier $foo
une seule fois, faites-le.
Il a une place. J'ai travaillé dans de nombreuses entreprises où les niveaux de compétence des développeurs vont de terrible à sorcier. Étant donné que le code doit être maintenu et que je ne serai pas là pour toujours, j'essaie d'écrire des trucs pour qu'ils semblent y appartenir (sans regarder les commentaires avec mes initiales, il est extrêmement rare que vous puissiez regardez le code sur lequel j'ai travaillé pour voir où j'ai apporté des modifications), et que quelqu'un avec moins de compétence que moi peut le maintenir.
Alors que l'opérateur ternaire a l'air nitziffique et très cool, mon expérience est que la ligne de code sera à peu près impossible à maintenir. Chez mon employeur actuel, nous avons des produits qui sont expédiés depuis près de 20 ans. Je n'utiliserais cet exemple nulle part.
Je ne pense pas que l'opérateur ternaire soit mauvais.
Voici un piège qui m'a fait perplexe, cependant. J'étais programmeur C pour beaucoup (10+) et à la fin des années 1990, je suis passé à la programmation d'applications Web. En tant que programmeur Web, j'ai rapidement rencontré PHP qui a également un opérateur ternaire. J'ai eu un bogue dans un programme PHP que j'ai finalement tracé jusqu'à une ligne) de code avec un opérateur ternaire imbriqué. Il s'avère que PHP opérateur ternaire associé de gauche à droite mais l'opérateur ternaire C (auquel j'étais habitué) s'associe de droite à gauche.
Quand est-ce approprié et quand ne l'est-il pas?
Je pense que lors du développement pour un groupe homogène de personnes, il n'y a pas de problème, mais lorsque vous devez traiter avec des personnes qui gèrent différents niveaux, ce type d'oneliners n'introduit qu'un niveau supplémentaire de complexité dans le code. Donc, ma politique en la matière est la suivante: code clair et ne pas expliquer plutôt que code court et expliquer 123123 fois.
Doit-il être enseigné ou caché aux débutants?
Je ne devrais pas être enseigné aux débutants, préférez-les plutôt à comprendre quand le besoin émerge, donc il ne sera utilisé que lorsque cela sera nécessaire et pas à chaque fois que vous aurez besoin d'un if.
Un magasin qui régulièrement écrit des méthodes de 600-1200 lignes ne devrait pas me dire qu'un ternaire est "difficile à comprendre". Tout magasin qui régulièrement autorise cinq conditions pour évaluer une branche de code ne devrait pas me dit que les conditions résumées concrètement dans un ternaire sont "difficiles à lire".
OMI, l'opérateur lui-même n'est pas mauvais, mais la syntaxe utilisée pour cela en C (et C++) est excessivement laconique. IMO, ALGOL 60 a fait mieux, donc quelque chose comme ça:
A = x == y ? B : C;
ressemblerait plus à ceci (mais en respectant la syntaxe de type C en général):
A = if (x==y) B else C;
Même avec cela, une imbrication excessivement profonde peut entraîner des problèmes de lisibilité, mais au moins A) quiconque a déjà programmé peut le comprendre simplement, et B) les personnes qui le comprennent peuvent gérer l'imbrication considérablement plus profonde assez facilement. OTOH, je noterais également que dans LISP (par exemple) un cond
est à peu près comme une instruction ternaire - pas un ensemble d'instructions, mais une seule expression qui donne une valeur (là encore, la plupart des LISP est comme ça ...)
Quand est?: Approprié, et quand ne l'est-il pas?
Doit-il être enseigné ou caché aux débutants?
Peu importe, mais cela ne devrait pas être intentionnellement caché car ce n'est pas trop complexe pour un "débutant" d'apprendre.
Si ... alors ... d'autre a tendance à mettre l'accent sur la condition et donc à mettre l'accent sur les opérations effectuées de manière conditionnelle.
l'opérateur ternaire est l'inverse, il a tendance à masquer la condition et est donc utile lorsque l'opération en cours est plus importante que la condition elle-même.
Il y a le petit problème technique, dans certaines langues, qu'ils ne sont pas tout à fait interchangeables car l'un est une déclaration et l'autre une expression, par exemple initialisation conditionnelle des consts en C++