web-dev-qa-db-fra.com

Pourquoi x == (x = y) n'est-il pas identique à (x = y) == x?

Prenons l'exemple suivant:

class Quirky {
    public static void main(String[] args) {
        int x = 1;
        int y = 3;

        System.out.println(x == (x = y)); // false
        x = 1; // reset
        System.out.println((x = y) == x); // true
     }
}

Je ne sais pas s'il existe un élément dans la spécification de langage Java qui charge le chargement de la valeur précédente d'une variable pour la comparaison avec le côté droit (x = y) qui, selon l'ordre implicite les crochets doivent être calculés en premier.

Pourquoi la première expression est évaluée à false, alors que la seconde est évaluée à true? J'aurais pensé que (x = y) serait évalué en premier, puis il comparerait x avec lui-même (3) et renverrait true.


Cette question est différente de ordre d'évaluation des sous-expressions dans une expression Java en ce que x n'est certainement pas une "sous-expression" ici. Il doit être chargé pour la comparaison plutôt que d'être "évalué". La question est spécifique à Java et l'expression x == (x = y), contrairement à des constructions impraticables trop recherchées, généralement conçues pour des questions d'entretien délicates, provient d'un projet réel. Il était supposé être un remplacement d'une ligne pour l'idiome de comparaison et remplacement

int oldX = x;
x = y;
return oldX == y;

qui, étant encore plus simple que l’instruction x86 CMPXCHG, méritait une expression plus courte en Java.

204
John McClane

qui, selon l'ordre indiqué entre parenthèses, devrait être calculé en premier

C'est une idée fausse commune que les parenthèses ont un effet (général) sur le calcul ou l'ordre d'évaluation. Ils ne font que contraindre les parties de votre expression dans un arbre particulier, liant les bons opérandes aux bonnes opérations pour le travail.

(Et, si vous ne les utilisez pas, ces informations proviennent de la "priorité" et de l'associativité des opérateurs, ce qui découle de la définition de l'arbre de syntaxe du langage. En fait, c'est toujours exactement comme cela que utilisez des parenthèses, mais nous simplifions et disons que nous ne nous basons pas sur des règles de priorité alors).

Une fois que cela est fait (c'est-à-dire une fois que votre code a été analysé dans un programme), ces opérandes doivent encore être évalués, et il existe des règles distinctes sur la manière de le faire: lesdites règles (comme Andrew nous l'a montré) indiquent que le LHS de chaque opération est évalué en premier en Java.

Notez que ce n'est pas le cas dans toutes les langues. Par exemple, en C++, à moins d'utiliser un opérateur de court-circuitage tel que && ou ||, l'ordre d'évaluation des opérandes est généralement non spécifié et vous ne devez en aucun cas vous en fier.

Les enseignants doivent cesser d’expliquer la priorité des opérateurs en utilisant des expressions trompeuses telles que "ceci fait que l’ajout a lieu en premier". Avec une expression x * y + z l'explication appropriée serait "la priorité des opérateurs rend l'addition se produit entre x * y et z, plutôt qu'entre y et z", avec aucune mention d'aucun "ordre".

96

== est un binaire opérateur d'égalité .

L'opérande de gauche d'un opérateur binaire semble être complètement évalué avant une partie quelconque de l'opérande de droite est évalué.

Spécification Java 11> Ordre d'évaluation> Évaluer d'abord l'opérande de la main gauche

162
Andrew Tobilko

Comme l'a dit Louis Wasserman, l'expression est évaluée de gauche à droite. Et Java ne se soucie pas de ce que "évaluer" fait, il se soucie seulement de générer une valeur (non volatile, finale) avec laquelle travailler.

//the example values
x = 1;
y = 3;

Donc, pour calculer la première sortie de System.out.println(), procédez comme suit:

x == (x = y)
1 == (x = y)
1 == (x = 3) //assign 3 to x, returns 3
1 == 3
false

et calculer la seconde:

(x = y) == x
(x = 3) == x //assign 3 to x, returns 3
3 == x
3 == 3
true

Notez que la deuxième valeur sera toujours considérée comme vraie, quelles que soient les valeurs initiales de x et y, car vous comparez effectivement l'affectation d'une valeur à la variable à laquelle elle est affectée et a = b et b, évalués dans cet ordre, seront toujours les mêmes par définition.

149
Poohl

Je ne sais pas s'il existe dans la spécification de langage Java qui charge le chargement de la valeur précédente d'une variable ...

Il y a. La prochaine fois que vous ne savez pas ce que dit la spécification, veuillez lire la spécification et , puis posez la question si elle n’est pas claire.

... le côté droit (x = y) qui, selon l'ordre indiqué entre parenthèses, devrait être calculé en premier.

Cette déclaration est fausse. Les parenthèses n'impliquent pas un ordre d'évaluation. En Java, l'ordre d'évaluation est de gauche à droite, quelles que soient les parenthèses. Les parenthèses déterminent l'emplacement des limites de la sous-expression et non l'ordre d'évaluation.

Pourquoi la première expression est évaluée à false, alors que la seconde est évaluée à true?

La règle pour l'opérateur == est la suivante: évaluer le côté gauche pour produire une valeur, évaluer le côté droit pour produire une valeur, comparer les valeurs, la comparaison est la valeur de l'expression.

En d'autres termes, la signification de expr1 == expr2 est toujours la même que si vous aviez écrit temp1 = expr1; temp2 = expr2; puis évalué temp1 == temp2.

La règle pour l'opérateur = avec une variable locale à gauche est la suivante: évaluer le côté gauche pour produire une variable, évaluer le côté droit pour générer une valeur, effectuer l'affectation, le résultat est la valeur attribuée .

Alors rassemblez-le:

x == (x = y)

Nous avons un opérateur de comparaison. Evaluez le côté gauche pour produire une valeur - nous obtenons la valeur actuelle de x. Evaluez le côté droit: c'est une affectation, nous évaluons donc le côté gauche pour produire une variable - la variable x - nous évaluons le côté droit - la valeur actuelle de y - l'assigner à x, et le résultat est la valeur assignée. Nous comparons ensuite la valeur initiale de x à la valeur attribuée.

Vous pouvez faire (x = y) == x comme exercice. Encore une fois, rappelez-vous, toutes les règles pour évaluer le côté gauche se produisent avant toutes les règles pour évaluer le côté droit.

J'aurais pensé que (x = y) serait évalué en premier, puis il comparerait x avec lui-même (3) et renverrait true.

Vos attentes sont basées sur un ensemble de convictions incorrectes concernant les règles de Java. J'espère que vos croyances sont correctes et que, dans le futur, vous attendez de vraies choses.

Cette question est différente de "l'ordre d'évaluation des sous-expressions dans une expression Java"

Cette déclaration est fausse. Cette question est totalement germane.

x n'est certainement pas une "sous-expression" ici.

Cette déclaration est également fausse. C'est une sous-expression deux fois dans chaque exemple.

Il doit être chargé pour la comparaison plutôt que d'être "évalué".

Je n'ai aucune idée de ce que cela signifie.

Apparemment, vous avez encore beaucoup de fausses croyances. Mon conseil est que vous lisiez la spécification jusqu'à ce que vos fausses croyances soient remplacées par de vraies croyances.

La question est spécifique à Java et l'expression x == (x = y), à la différence des constructions impraticables trop recherchées, généralement conçues pour des questions d'entretien délicates, provenait d'un projet réel.

La provenance de l'expression n'est pas pertinente pour la question. Les règles applicables à ces expressions sont clairement décrites dans le cahier des charges. lis le!

Il était supposé être un remplacement d'une ligne pour l'idiome de comparaison et remplacement

Étant donné que ce remplacement d’une ligne a causé beaucoup de confusion chez vous, lecteur du code, je dirais que c’était un mauvais choix. Rendre le code plus concis mais plus difficile à comprendre n’est pas une victoire. Il est peu probable que le code soit plus rapide.

Incidemment, C # a comparer et remplacer en tant que méthode de bibliothèque qui peut être rejeté à une instruction de la machine. Je crois que Java ne dispose pas d'une telle méthode, car elle ne peut pas être représentée dans le système de types Java.

25
Eric Lippert

Cela est lié à la priorité des opérateurs et à la manière dont les opérateurs sont évalués.

Les parenthèses '()' ont une priorité plus élevée et ont une associativité de gauche à droite. L'égalité '==' vient ensuite dans cette question et a une associativité de gauche à droite. Assignment '=' vient en dernier et a une associativité droite à gauche.

La pile d’utilisation du système pour évaluer une expression. L'expression est évaluée de gauche à droite.

Vient maintenant à la question initiale:

int x = 1;
int y = 3;
System.out.println(x == (x = y)); // false

D'abord, x(1) sera mis en pile. alors intérieure (x = y) sera évaluée et poussée à empiler avec la valeur x (3). Maintenant, x(1) sera comparé à x(3) donc le résultat est faux.

x = 1; // reset
System.out.println((x = y) == x); // true

Ici, (x = y) sera évalué, maintenant la valeur x devient 3 et x(3) sera poussé dans la pile. Maintenant, x(3) avec la valeur modifiée après égalité sera placé dans la pile. Maintenant, l'expression sera évaluée et les deux seront identiques, le résultat est donc vrai.

16
Amit

Ce n'est pas pareil. Le côté gauche sera toujours évalué avant le côté droit et les crochets ne spécifient pas un ordre d'exécution, mais un regroupement de commandes.

Avec:

      x == (x = y)

Vous faites essentiellement la même chose que:

      x == y

Et x aura la valeur de y après la comparaison.

Bien avec:

      (x = y) == x

Vous faites essentiellement la même chose que:

      x == x

Après que x ait pris la valeur de y. Et il reviendra toujours vrai.

12
Or10n

Dans le premier test, vous vérifiez que 1 == 3.

Dans le deuxième test, votre vérification fait 3 == 3.

(x = y) assigne la valeur et cette valeur est testée. Dans l'exemple précédent, x = 1 en premier puis x est attribué à 3. Est-ce que 1 == 3?

Dans ce dernier cas, x est assigné à 3 et, évidemment, il en reste 3. Est-ce que 3 == 3?

9
Michael Puckett II

Considérons cet autre exemple, peut-être plus simple:

int x = 1;
System.out.println(x == ++x); // false
x = 1; // reset
System.out.println(++x == x); // true

Ici, l'opérateur de pré-incrémentation dans ++x doit être appliqué avant la comparaison est effectuée - exactement comme (x = y) dans votre exemple. doit être calculé avant la comparaison.

Cependant, l'évaluation de l'expression se fait toujours de gauche → à → droite, la première comparaison est donc en fait 1 == 2 alors que la seconde est 2 == 2.
La même chose se passe dans votre exemple.

8
walen

Les expressions sont évaluées de gauche à droite. Dans ce cas:

int x = 1;
int y = 3;

x == (x = y)) // false
x ==    t

- left x = 1
- let t = (x = y) => x = 3
- x == (x = y)
  x == t
  1 == 3 //false

(x = y) == x); // true
   t    == x

- left (x = y) => x = 3
           t    =      3 
-  (x = y) == x
-     t    == x
-     3    == 3 //true

Fondamentalement, la première instruction x avait la valeur 1 Donc Java compare 1 == à la nouvelle variable x qui ne sera pas la même

Dans le second, vous avez dit x = y, ce qui signifie que la valeur de x a changé. Ainsi, lorsque vous l'appelez à nouveau, ce sera la même valeur, c'est pourquoi elle est vraie et x == x

5
Ahmad Bedirxan

== est un opérateur d'égalité de comparaison et il fonctionne de gauche à droite.

x == (x = y);

ici, l'ancienne valeur attribuée de x est comparée à la nouvelle valeur d'attribution de x, (1 == 3) // false

(x = y) == x;

Considérant que, dans ce cas, la nouvelle valeur d’attribution de x est comparée à la nouvelle valeur de conservation de x qui lui est attribuée juste avant la comparaison, (3 == 3) // true

Maintenant, considérez ceci

    System.out.println((8 + (5 * 6)) * 9);
    System.out.println(8 + (5 * 6) * 9);
    System.out.println((8 + 5) * 6 * 9);
    System.out.println((8 + (5) * 6) * 9);
    System.out.println(8 + 5 * 6 * 9);

Sortie:

342

278

702

342

278

Ainsi, les parenthèses jouent leur rôle majeur dans les expressions arithmétiques, mais pas dans les expressions de comparaison.

4
Nisrin Dhoondia

La chose ici est l'ordre de priorité des opérateurs arithmatiques/opérateurs relationnels sur les deux opérateurs = vs == le dominant est == (les opérateurs relationnels dominent) comme il précède = opérateurs d'affectation. Malgré la priorité, l'ordre d'évaluation est LTR (GAUCHE À DROITE). La priorité entre en image après l'ordre d'évaluation. Donc, indépendamment de toute contrainte, l'évaluation est LTR.

2
Himanshu Ahuja