Pourquoi est x == (x = y) pas la même chose que (x = y) == x?
Considérons 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 si il y a un élément dans le Langage Java Spécification qui dicte le chargement de la valeur précédente de la variable pour la comparaison avec le côté droit (x = y
) qui, par l'ordre implicite par des crochets, doit être calculé en premier.
Pourquoi la première expression à évaluer à false
, mais la seconde d'évaluer à true
? Je me serais attendu à (x = y)
être évaluées en premier, puis les comparer x
avec lui-même (3
) et retour true
.
Cette question est différente de l'ordre d'évaluation des sous-expressions dans une expression de Java dans ce x
est certainement pas un "sous-expression' ici. Il doit être chargé pour la comparaison plutôt que d'être "évalué'. La question est de Java spécifiques et l'expression x == (x = y)
, contrairement à farfelue impossible constructions communément conçu pour les délicates questions de l'entrevue, venaient de un projet réel. C'était censé être une ligne de remplacement pour les comparer et de les remplacer idiome
int oldX = x;
x = y;
return oldX == y;
qui, en étant encore plus simple que x86 CMPXCHG d'instruction, s'est mérité une expression plus courte en Java.
- Le côté gauche est toujours évalué avant le côté droit. Les crochets de ne pas faire une différence pour qu'.
- Le terme évaluation est, à mon avis, inapplicable ici parce que
x
n'a pas besoin d'être évaluées, il vient d'être chargée à partir de la mémoire. - L'évaluation de l'expression
x = y
est certainement pertinente, et provoque un effet secondaire quix
est définie à la valeur dey
. - Assurez-vous. Je le savais, et c'était l'idée principale qui m'a conduit à
quickReplaceAndCompare()
. - Faire vous-même et vos coéquipiers une faveur et ne mélange pas l'état de la mutation dans la même ligne que l'examen d'état. Faire considérablement réduit la lisibilité de votre code. (Il y a des cas où c'est absolument nécessaire en raison de l'atomicité des exigences, mais les fonctions de ceux qui existent déjà et leur but serait reconnu instantanément.)
- Pour un exemple de la frustration "raccourcis", comme cela peut causer, il m'a pris la meilleure partie de une minute pour traiter de ce bien suffisant pour voir ce que ces sont en train de faire. Je sais aussi que C++, et les règles sont différentes en C++ qu'ils sont ici en Java. En fait, en C++, c'est un comportement indéfini, il jette toutes sortes d'mentale des drapeaux rouges pour moi, le ralentissement de moi vers le bas plus que ce qu'ils devraient probablement. J'ai probablement lu par
replaceAndCompare
100 fois plus rapide que je n'aiquickReplaceAndCompare
. - La vraie question est de savoir pourquoi vous voulez écrire du code comme ceci.
- Ne jamais écrire un tel code en production.
- Une autre priorité de la question: Quelles sont les règles pour l'ordre d'évaluation en Java?
- Off: si c'était de la C, il serait à l'évidence un cas d'UB, sens le compilateur peut générer quelque chose de lui.
- Quelqu'un m'a dit une fois que toutes les expressions booléennes ont trois valeurs possibles: True, False et "putain de Stupide".
- Ne des constructions telles que celles de l'OP effectivement donner aucune garantie de l'atomicité?
- La clé de votre question, c'est votre croyance fausse que les parenthèses implique ordre d'évaluation. C'est une croyance commune en raison de la façon dont on enseigne les mathématiques à l'école primaire et parce que certains débutants livres de programmation de toujours se tromper, mais c'est une fausse croyance. C'est plutôt une question fréquente. Vous pouvez bénéficier de la lecture de mes articles sur le sujet, ils sont sur le C#, mais ils s'appliquent à Java: ericlippert.com/2008/05/23/precedence-vs-associativity-vs-order ericlippert.com/2009/08/10/precedence-vs-order-redux
- Non, je fais référence à des choses comme ceci. Les opérations atomiques sont explicites, c'est pourquoi ils sont instantanément reconnus. Désolé de ne pas être plus clair.
- En particulier, pensez à quelque chose comme
x() * (y() + z())
L'ordre des appels de fonction est pas y puis z puis x parce que "les parenthèses venir en premier". Les parenthèses de déterminer la sous-expression de frontières, pas de l'ordre d'évaluation. Le*
a deux sous-expressions:x()
et(y() + z())
. Celui de gauche,x()
, qui se produit en premier. Puis celui de droite qui se passe:y()
est appelé,z()
est appelé, et ils sont additionnés. Ensuite, la multiplication a lieu. Ainsi, le+
qui se passe avant le*
, comme il se doit, mais les opérandes en plus de ne pas arriver en premier. - Je pense qu'une partie du problème est que les enseignants de mathématiques se réfèrent à des règles de priorité des opérateurs comme “l'ordre des opérations”, et le mot “ordre” voyage les gens.
- "x est certainement pas un" sous-expression "ici" - Il est vraiment. Pourquoi pensez-vous que le "chargement" de la valeur d'une variable n'est pas à l'exemple de "l'évaluation"? L'évaluation donne une valeur, pas une variable ou d'un emplacement mémoire.
- L'affiche originale est, comme vous le notez, un peu confus. Cependant votre relevé que l'évaluation donne une valeur, pas une variable, n'est pas étayée par des preuves. Considérons par exemple
array()[index()] = value();
La sémantique de Java sont ce que nous appelons laarray()
, puisindex()
, puisvalue()
; l'ensemble de ces valeurs. Mais il faut alors vérifier si le tableau de référence est valide et de le jeter si elle n'est pas, et de vérifier ensuite si l'index est valide, et de le jeter si elle ne l'est pas. Mais absolument la sous-expression à gauche de l'affectation n'est pas une valeur. Il s'agit d'une variable. - Votre déclaration est directement contredite par la spécification de Java. Les langages tels que C la distinction entre "lvalues" et "rvalues"; un "lvalue" est la "valeur", produit par l'évaluation de la partie gauche d'une affectation. C'est critiquée à juste titre comme étant source de confusion; Java et C#) d'éviter ce problème en évitant cette notion que assignables choses sont des "valeurs", et au lieu de les appeler ce qu'ils sont: des variables. Les spécifications de Java pour l'affectation le rend très clair: "tout d'Abord, la gauche opérande est évaluée pour produire une variable..."
- n'est certainement pas un "sous-expression "ici" - sur la base de quels critères pensez-vous cela?
- Parce que l'expression implique opérateur(s).
- Que l'énoncé est faux; une expression qui n'a pas besoin d'impliquer tout opérateur. Encore une fois, vous faites très bien de lire le cahier des charges, parce que vous avez un grand nombre de fausses croyances. Détromper vous d'eux!
- Afin de voir à partir de la spécification (docs.oracle.com/javase/specs/jls/se11/html/jls-15.html), qu'il y a des expressions sans opérateurs, vous pouvez suivre la syntaxe de la
Expression
terminal par le biais de plusieurs niveaux, par le biais dePostFixExpression
etExpressionName
. Ou regarderMethodInvocation
, dontArgumentList
se compose de comma-separated expressions. Si toutes les expressions ont été nécessaires pour impliquer les opérateurs, vous ne pouvais pas laisser passerx
comme argument à une méthode. - une expression n'est pas nécessairement un opérateur(s). Ici, vous verrez que
Expression
peut être unNameExpression
(par exemple, le signalement d'une variable locale), unPrimaryExpression
(par exemple, en se référant à une primitive littérale), etc - Regardez la mise en œuvre de
Map.computeIfAbsent()
et le blâme de son auteur trop. Ouais je sais qu'ils sont autorisés à le faire et je ne suis pas 🙂 - Je pense que nous parlons à contre-ici, depuis l'identifiant "x" apparaît deux fois dans l'expression en vertu de la discussion, une fois sur la gauche de l'opérateur d'égalité, et sur la gauche d'un opérateur d'affectation. Je suppose que la discussion était sur l'ancien, depuis l'OP parle de "chargement" de la valeur. La partie de la spécification de vous citer est à propos de l'opérande de gauche d'un opérateur d'affectation. Je le concède, j'ai parlé de manière imprécise. Mon point est que "l'évaluation" du côté gauche de l'expression ne donne pas une sorte de paresseux valeur qui peut changer ou modifier avant l'opération de comparaison évalue.
- Mon interprétation de l'OP du modèle mental était: 1. évaluer
==
opérandes gauche à droite. 2.x
n'a pas besoin de l'évaluation, il est juste "x" (une erreur). 3. évaluer(x = y)
4. Variablex
est mis à jour à la valeur dey
, l'expression a la valeur dey
, qui est3
. 5. évaluer==
de l'opérateur. 6. Récupérer la valeur actuelle dex
et de le comparer à5
. Si c'est le cas, ce n'est pas l'ordre d'évaluation qu'ils ont obtenu confus. C'est le sens de "l'évaluation de x".
Vous devez vous connecter pour publier un commentaire.
Pas. C'est une idée fausse commune que les parenthèses ont tout (général) effet sur le calcul ou l'évaluation de l'ordre. Ils ne contraignent les parties de votre expression dans un arbre en particulier, la liaison le droit d'opérandes pour le droit des opérations pour le travail.
(Et, si vous ne les utilisez pas, cette information vient de la "priorité" et l'associativité des opérateurs, quelque chose qui est un résultat de la façon dont la syntaxe du langage de l'arbre est défini. En fait, c'est encore exactement comment il fonctionne lorsque vous utilisez des parenthèses, mais nous simplifier et dire que nous ne sommes pas de se fonder sur des règles de priorité alors.)
Une fois que c'est fait (c'est à dire une fois que votre code a été analysé dans un programme) les opérandes doivent encore être évalués, et il existe des règles sur la façon dont c'est fait: ces règles (comme Andrew nous a montré), le membre de GAUCHE de chaque opération est d'abord évaluée en Java.
Noter que ce n'est pas le cas dans toutes les langues; par exemple, en C++, sauf si vous êtes à l'aide d'un court-circuit de l'opérateur comme
&&
ou||
, l'ordre d'évaluation des opérandes est généralement quelconques et vous ne devriez pas compter sur elle de toute façon.Les enseignants doivent arrêter d'expliquer la priorité de l'opérateur à l'aide trompeuse des phrases comme "ce qui rend le plus arriver en premier". Donné une expression
x * y + z
l'explication correcte serait "la priorité de l'opérateur permet l'addition se produire entrex * y
etz
, plutôt qu'entrey
etz
", sans mention d'une quelconque "ordre".==
est un binaire opérateur d'égalité.Comme LouisWasserman dit, l'expression est évaluée de gauche à droite. Et java ne se soucie pas de ce que "évaluer" en fait, il se soucie uniquement de la génération d'un (non volatile, finale) de la valeur à travailler avec.
Pour calculer la première sortie de
System.out.println()
, le suivant est fait:et pour calculer le deuxième:
Noter que la deuxième valeur est toujours true, quelle que soit la valeur initiale de
x
ety
, parce que vous êtes effectivement en comparant l'assignation d'une valeur à la variable à laquelle il est affecté, eta = b
etb
sera, évalué dans cet ordre, toujours le même, par définition.Il est. La prochaine fois, vous n'êtes pas sûr de ce que la spécification dit, veuillez lire la spécification et la puis poser la question si il n'est pas clair.
Que l'énoncé est faux. Parenthèses n'implique pas un ordre d'évaluation. En Java, l'ordre d'évaluation est de gauche à droite, que ce soit entre parenthèses. Entre parenthèses, de déterminer où la sous-expression de limites, pas de l'ordre d'évaluation.
La règle pour la
==
opérateur est: d'évaluer le côté gauche pour produire une valeur, d'évaluer le côté droit pour produire une valeur, comparer les valeurs, la comparaison est la valeur de l'expression.En d'autres termes, le sens de
expr1 == expr2
est toujours le même, comme si vous aviez écrittemp1 = expr1; temp2 = expr2;
, puis de les évaluertemp1 == temp2
.La règle pour la
=
opérateur avec une variable locale sur le côté gauche est: d'évaluer le côté gauche de produire une variable, d'évaluer le côté droit pour produire une valeur, la mission, le résultat est la valeur qui a été attribué.Donc mettre ensemble:
Nous avons un opérateur de comparaison. Évaluer le côté gauche de produire une valeur-nous obtenir la valeur actuelle de
x
. Évaluer le côté droit: c'est une mission nous avons donc évaluer le côté gauche de produire une variable, la variablex
-- nous évaluons le côté droit -- la valeur actuelle dey
-- l'attribuer àx
, et le résultat est la valeur assignée. On compare ensuite la valeur d'origine dex
à la valeur qui lui a été assigné.Vous pouvez faire
(x = y) == x
comme un exercice. Encore une fois, rappelez-vous, toutes les règles pour l'évaluation de la gauche se produire avant que toutes les règles de l'évaluation de la droite.Votre attente est basé sur un ensemble de croyances erronées sur les règles de Java. Espérons que vous avez maintenant correcte croyances et seront à l'avenir s'attendre à des choses vraies.
Cette déclaration est fausse. Cette question est totalement pertinente.
Cette déclaration est également faux. C'est une sous-expression deux fois dans chaque exemple.
Je n'ai aucune idée de ce que cela signifie.
Apparemment, vous avez encore beaucoup de fausses croyances. Mon conseil est que vous avez lu la spécification jusqu'à ce que vos fausses croyances sont remplacés par des croyances vraies.
L'origine de l'expression n'est pas pertinente pour la question. Les règles de telles expressions sont clairement décrites dans le cahier des charges; le lire!
Depuis que le remplacement de la conduite a causé beaucoup de confusion en vous, le 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 pour rendre le code plus rapide.
D'ailleurs, C# a les comparer et de les remplacer comme une méthode de bibliothèque, qui peut être jitted vers le bas à une instruction machine. Je crois que Java ne dispose pas d'une telle méthode, car il ne peut pas être représenté dans le système de type Java.
Elle est liée à la priorité de l'opérateur et de la façon dont les exploitants obtenir une évaluation.
Parenthèses '()' a une priorité plus élevée et a l'associativité de gauche à droite.
L'égalité '==' viennent ensuite, dans cette question, et a l'associativité de gauche à droite.
Affectation '=' dernière et a l'associativité de droite à gauche.
L'utilisation du système de pile à évaluer l'expression. Expression obtient évaluée de gauche à droite.
Vient maintenant à la question initiale:
Premier x(1) seront poussés à pile.
ensuite intérieure (x = y) seront évalués et poussé à pile avec la valeur x(3).
Maintenant, x(1) seront comparés à ceux des x(3) donc le résultat est faux.
Ici,
(x = y) sera évalué, maintenant x valeur deviennent des 3 et x(3) seront poussés à pile.
Maintenant, x(3) avec la valeur modifiée après l'égalité sera poussé à pile.
Maintenant, l'expression sera évaluée et les deux seront même si le résultat est vrai.
Ce n'est pas la même. La gauche sera toujours évaluée avant le côté droit, et les supports de ne pas spécifier un ordre d'exécution, mais un groupement de commandes.
Avec:
Vous êtes essentiellement faire la même chose que:
Et x aura la valeur de y après la comparaison.
Tout avec:
Vous êtes essentiellement faire la même chose que:
Après x a pris y's de la valeur. Et il retournera toujours vrai.
Dans la première épreuve, vous êtes à la vérification ne 1 == 3.
Dans le second test votre la vérification ne 3 == 3.
(x = y) affecte la valeur et que la valeur est testée. Dans le premier exemple x = 1 en premier, alors x est affecté 3. Ne 1 == 3?
Dans le second, x est affecté à 3, et, évidemment, c'est toujours 3. Ne 3 == 3?
Considérer cet autre, peut-être plus simple exemple:
Ici, le pré-opérateur d'incrémentation dans
++x
doit être appliqué avant la comparaison est faite — comme(x = y)
dans votre exemple, doit être calculée avant la comparaison.Cependant, l'évaluation de l'expression arrive encore à gauche → → droit, de sorte que la première comparaison, est en fait
1 == 2
tandis que le second est2 == 2
.La même chose se passe dans votre exemple.
Les Expressions sont évaluées de gauche à droite. Dans ce cas:
Essentiellement la première déclaration de x a une valeur de 1
Donc Java compare 1 == pour la nouvelle variable x qui ne sera pas le même
Dans le second, vous avez dit que x=y, ce qui signifie que la valeur de x est changé et donc, lorsque vous appelez encore, il va être la même valeur donc pourquoi il est vrai, et x ==x
== est une comparaison opérateur d'égalité et il fonctionne à partir de la gauche vers la droite.
ici le vieux affecté de la valeur de x est comparé avec les nouvelles d'attribuer de la valeur de x, (1==3)//false
Alors que, ici, d'attribuer de la valeur de x est comparé avec la nouvelle holding de la valeur de x, est attribué à juste avant la comparaison, (3==3)//vrai
Considérons maintenant ce
Ainsi, entre Parenthèses, joue son rôle majeur dans les expressions arithmétiques pas seulement dans l'expression de comparaison.
x + (x = y)
et(x = y) + x
ne montrent le même comportement que l'original avec les opérateurs de comparaison.La chose ici est la arithmatic exploitants/opérateurs relationnels precedency l'ordre des deux opérateurs
=
vs==
le dominant est==
(Opérateurs Relationnels domine ), car il précède=
opérateurs d'affectation.En dépit de la priorité, l'ordre d'évaluation est LTR (de GAUCHE À DROITE) préséance entre en image après l'ordre d'évaluation.
Donc, Indépendamment de toutes les contraintes de l'évaluation est de la LTR.
Il est facile dans la deuxième comparaison sur la gauche est d'affectation après affectation de y à x (sur la gauche), puis de les comparer 3 == 3. Dans le premier exemple de la comparaison de x = 1 avec de nouvelles affecter x = 3. Il semble qu'il y a toujours eu de l'état actuel de la lecture des déclarations de gauche à droite de x.
Le genre de question à vous poser est une très bonne question, si vous voulez écrire un compilateur Java, ou des programmes de test pour vérifier qu'un compilateur Java fonctionne correctement. En Java, ces deux expressions doivent produire les résultats que vous avez vu. En C++, par exemple, ils n'ont pas à - donc, si quelqu'un a réutilisé les parties d'un compilateur C++ dans leur compilateur Java, vous pourrait théoriquement trouver que le compilateur ne se comporte pas comme il le devrait.
En tant que développeur de logiciels, d'écrire du code lisible, compréhensible et maintenable, les deux versions de votre code peut être considéré comme terrible. Pour comprendre ce que fait le code, on doit savoir exactement comment le langage Java est défini. Quelqu'un qui écrit à Java et en C++ code frisson en regardant le code. Si vous avez à se demander pourquoi une seule ligne de code est ce qu'il fait, alors vous devriez éviter que le code. (Je suppose et espère que les gars qui ont répondu à votre "pourquoi" question correctement par eux-mêmes éviter que ind de code).