2010-10-18 17 views
14

Doublons possibles:
Why doesn't Java have compound assignment versions of the conditional-and and conditional-or operators? (&&=, ||=)
Why does a “&&=” Operator not exist?&& = et || = opérateurs

Aujourd'hui, au travail, j'ai écrit la lettre de crédit suivante (les identités réelles de b et b1 sont confidentielles:

b &&= b1; // meaning b = b && b1; 

Je l'ai regardé pour un couple de secondes et s'est rendu compte qu'il n'existe pas un tel opérateur. Juste pour être sûr, j'ai cliqué sur compiler et il a échoué. Pour être sûr que j'ai consulté la norme.

Y a-t-il des raisons spécifiques à l'absence de tels opérateurs? Je pense à certains:

  1. b &&= b1 et b = b && b1 peuvent ne pas être équivalent à cause de l'évaluation de court-circuit de & &.
  2. & & = est laid
  3. & & = est rarement nécessaire

Je ne prétends pas que ce serait très utile d'avoir ces opérateurs, non. Je ne prétends pas non plus que l'une quelconque ou la totalité des trois raisons ci-dessus ne suffisent pas à ne pas créer cet opérateur. Ma question est la suivante: y a-t-il une raison beaucoup plus sérieuse que je supervise?

+0

Oui, vous pouvez avoir deux valeurs "vraies" booléennes (toutes deux supérieures à un), où leur bitwise et leur valeur sont "faux" ... Essayez bitwise et 2 et 1 et vous obtenez 0 (faux), où un logique et produirait vrai – LorenVS

+2

'b & = isSomethingWithSideEffects();' exécuterait la fonction indépendamment. mais s'il était changé en '&& =', il ne le serait probablement pas dans le cas où b était faux, non? – Dusty

+1

Désolé, je ne comprends pas pourquoi le commentaire de Dingo a été upvoted. Quelqu'un pourrait-il expliquer s'il vous plaît? – John

Répondre

8

Je ne sais pas pourquoi la question et certaines des réponses mentionnent le comportement de court-circuit des opérateurs logiques correspondants en tant que problème potentiel.

Il n'y a absolument aucun problème lié aux courts-circuits avec la définition des opérateurs &&= et ||=. Ils doivent être définis uniformément avec += et d'autres opérateurs similaires, ce qui signifie que a &&= b doit être équivalent à a = a && b, mais a étant évalué une seule fois en version &&=. Cela signifie à son tour que b n'est pas évalué du tout si a est à l'origine zéro. Facile. Donc, la seule raison pour laquelle ils n'existent pas dans la langue est, bien, "juste parce que".

+1

Exactement. Perl a ces opérateurs, avec ce comportement; et bien que j'ai entendu beaucoup de critiques de Perl, je n'ai jamais entendu quelqu'un se plaindre que '&& =' est confus. – Porculus

+0

Bon point sur la chose de court-circuit, mais techniquement parlant a = a + b n'est pas équivalent à un + = b si a a des effets secondaires, par ex. est une fonction qui imprime quelque chose et renvoie une référence (dans le premier cas, elle imprime deux fois et dans la seconde une seule fois). –

+0

@Armen Tsirunyan: Eh bien, je n'essayais pas de le faire officiellement préfet. La norme de langage définit 'a + = b' comme équivalent à' a = a + b' sauf que dans '+ =' la version 'a' est évaluée une seule fois. De la même manière, le '&& =' peut être défini. – AnT

0

EDIT: Langue incorrecte, mais applique toujours

Je suis d'accord avec vos trois raisons, bien qu'il y ait un scénario où je l'ai déploré le manque de cet opérateur, lors de l'écriture des routines de désérialisation personnalisé. Dans quelques cas où un objet incorrectement sérialisé n'était pas vraiment "exceptionnel" (et pour éviter une surcharge d'exception en C# pour des échecs très communs), j'utiliserais des valeurs de retour booléennes pour indiquer si une opération de désérialisation était réussie.

Ce code est tout à fait théorique, et Nested1, Nested2 et Nested3 sont tous struct:

public struct ComplexStruct 
{ 
    private Nested1 nested1; 
    private Nested2 nested2; 
    private Nested3[] nested3; 

    public bool Read(Reader reader) 
    { 
     bool ret = true; 
     int nested3Length = 0; 

     ret &&= nested1.Read(reader); 
     ret &&= nested2.Read(reader); 
     ret &&= reader.ReadInt32(ref nested3Length); 

     for(int i = 0; (ret && i < nested3Length); i++) 
     { 
      ret &&= nested3[i].Read(reader); 
     } 

     return ret; 
    } 
} 
+0

Eh bien, c'était presque le même scénario quand j'en avais besoin (la partie boucle). Je vais maintenant éditer ma question pour clarifier que –

+0

Dans ce cas, vous pouvez juste 'return false;' early. –

+0

J'ai relagged ma question :) –

9

Je les ai toujours désiré aussi bien. Je doute que la laideur soit en soi le facteur déterminant, le comportement de court-circuit est le meilleur argument contre eux que j'ai entendu jusqu'ici.

Un autre facteur contribuant est que C est conçu pour fonctionner près du métal, à peu près tous les opérateurs correspondent directement à une instruction dans les architectures majeures. Je ne pense pas qu'il existe une instruction sur x86, PPC, etc qui implémente directement b &&= b1;

+1

+1. Quelles opérations étaient disponibles sur le PDP-11 est probablement plus déterminante, mais je pense toujours que vous êtes sur quelque chose là-bas. –

+0

@ T.E.D.: Bien sûr, mais les architectures majeures modernes héritent la plupart de leurs instructions des architectures d'autrefois. Surtout sur les parties communes. –

+0

Huh? Le court-circuit est l'argument * pour * eux. Sans cela, utilisez simplement '& =' avec des valeurs booléennes. Comme pour les instructions, les mêmes sont utilisées que pour 'b && (b = b1);', (mais avec une seule évaluation de 'b' bien sûr). Le compilateur choisit entre une branche et un AND au niveau du bit. – Potatoswatter

1

Personnellement, je voterais pour la première raison que vous avez choisie. Les opérateurs booléens ont des sémantiques de court-circuit, ce qui rendrait certaines situations vraiment nettes si elles étaient traduites en opérateurs d'affectation. Soit vous ne les faites plus court-circuiter, soit vous créez un étrange opérateur d'assignation "optionnel" (faites les choses sur la droite et n'attribuez dans le résultat que si la valeur sur le LHS est déjà non nulle). D'une manière ou d'une autre, vous créeriez des bugs subtils car les gens s'attendraient à l'autre comportement.

2

La plus grande raison pour laquelle les opérateurs n'existent pas est probablement que K & R n'a pas pensé à un moyen attrayant de les définir. J'ai aussi parfois voulu un opérateur -> = (ptr -> = next serait équivalent à ptr = ptr-> quoi que ce soit).

Un problème que je pense avec & & = est que ce n'est pas évident que des éléments suivants serait le plus utile, ou qu'il est censé être:

 
    if (lhs && rhs) lhs = 1; else lhs = 0; 
    if (!rhs) lhs = 0; else lhs = !(!lhs)); 
    if (lhs && !rhs) lhs = 0; 
    if (!rhs) lhs = 0; 
La première variante est le plus clairement suggéré par la syntaxe, mais de d'un point de vue pratique, si aucun terme n'est nul, il serait souvent plus utile de laisser le côté gauche seul que de le mettre à "1".

BTW, j'ai souvent souhaité une variation de l'opérateur virgule qui permettrait d'évaluer le côté gauche, enregistrer la valeur, puis évaluer le côté droit et retourner la valeur du côté gauche. Equivalent à:

 
int foo(int p1, int p2) return p1; 
sauf applicable à tout type (p2 n'a pas besoin d'être du même type que p1, et pourrait être vide), et avec un ordre d'évaluation de gauche à droite garanti. Serait très utile pour des choses comme l'indexation post-incrément avec une étape non-unité, par exemple, arr [ptr ~, ptr + = 2]; ou pour certains types d'opérations d'échange de données, par exemple var1 = (var2 ~, var2 = var1);

+3

En C, tous les opérateurs d'affectation composés ('op =') sont définis comme 'lhs = lhs op rhs' avec l'expression' lhs' évaluée une seule fois. Donc je pense qu'il y a peu d'ambiguïté quant à la façon dont '&& =' serait défini (de la même manière). –

+0

@Michael Burr: Ce serait une implémentation naturelle, mais en pratique il y a des fois que le troisième serait plus utile que le premier, mais je ne peux pas penser à autant de cas où le premier serait réellement plus utile. – supercat

+1

J'avais proposé cette équivalence à une question maintenant supprimée: 'if (lhs) lhs = (rhs! = 0);' semble avoir le comportement de court-circuit attendu. Cependant, il est probablement plus rapide d'exécuter 'if (lhs) lhs = rhs;' qui court-circuite également mais ne force pas tous les rhs non-nuls à 1. –

2

Parce que le résultat de a && b est toujours 0 ou 1, je pense que l'interprétation de ce ne serait sans ambiguïté pour le type C99 _Bool. Comme cela n'existait pas au moment de la création de C, cet opérateur n'était alors pas inclus. Et de nos jours, personne n'ajoute facilement un autre opérateur à C, car cela aurait un impact sur tous les analyseurs existants.