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 y a un élément dans la spécification du langage Java qui dicte le chargement de la valeur précédente d'une variable pour la comparaison avec le côté droit ( x = y
) qui, par l'ordre impliqué par les crochets, devrait être calculé en premier.
Pourquoi la première expression est-elle évaluée false
, mais la seconde est-elle évaluée true
? Je m'attendais (x = y)
à être évalué en premier, puis il se comparerait x
à lui-même ( 3
) et reviendrait true
.
Cette question est différente de l' ordre d'évaluation des sous-expressions dans une expression Java en ce qu'elle 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)
, à la différence des constructions improbables farfelues généralement conçues pour des questions d'entrevue délicates, est venue d'un vrai projet. Il était censé être un remplacement d'une ligne pour l'idiome de comparaison et de 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.
la source
x = y
est certainement pertinente et provoque l'effet secondairex
défini sur la valeur dey
.Réponses:
Non. Il est faux de penser que les parenthèses ont un effet (général) sur l'ordre de calcul ou d'évaluation. Ils contraignent uniquement les parties de votre expression dans une arborescence particulière, 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 est le résultat de la façon dont l'arbre de syntaxe du langage est défini. En fait, c'est toujours exactement comme cela que vous travaillez lorsque vous utilisez des parenthèses, mais nous simplifions et disons que nous ne nous appuyons pas sur des règles de priorité.)
Une fois cela 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 façon dont cela est fait: 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 que vous n'utilisiez un opérateur de court-circuit comme
&&
ou||
, l'ordre d'évaluation des opérandes n'est généralement pas spécifié et vous ne devez pas vous y fier dans les deux cas.Les enseignants doivent cesser d'expliquer la priorité de l'opérateur en utilisant des phrases trompeuses telles que «ceci fait que l'ajout se produit en premier». Étant donné une expression,
x * y + z
l'explication appropriée serait "la priorité de l'opérateur fait que l'addition a lieu entrex * y
etz
, plutôt qu'entrey
etz
", sans mention d'aucun "ordre".la source
==
est un opérateur d'égalité binaire .la source
Comme l'a dit LouisWasserman, l'expression est évaluée de gauche à droite. Et java ne se soucie pas de ce que "évaluer" fait réellement, il se soucie seulement de générer une valeur (non volatile, finale) avec laquelle travailler.
Donc, pour calculer la première sortie de
System.out.println()
, la procédure suivante est effectuée:et pour calculer la seconde:
Notez que la deuxième valeur sera toujours évaluée à true, quelles que soient les valeurs initiales de
x
ety
, parce que vous comparez efficacement l'affectation d'une valeur à la variable à laquelle elle est affectée,a = b
etb
sera, évaluée dans cet ordre, toujours la même par définition.la source
Il y a. La prochaine fois que vous ne savez pas ce que dit la spécification, veuillez lire la spécification, puis posez la question si elle n'est pas claire.
Cette affirmation 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 où se trouvent les limites de la sous-expression, et non l'ordre d'évaluation.
La règle pour l'
==
opérateur est: é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 écrittemp1 = expr1; temp2 = expr2;
puis évaluétemp1 == temp2
.La règle pour l'
=
opérateur avec une variable locale sur le côté gauche est: évaluer le côté gauche pour produire une variable, évaluer le côté droit pour produire une valeur, effectuer l'affectation, le résultat est la valeur qui a été affectée.Alors mettez-le ensemble:
Nous avons un opérateur de comparaison. Évaluez le côté gauche pour produire une valeur - nous obtenons la valeur actuelle de
x
. Évaluer le côté droit: c'est une affectation, donc nous évaluons le côté gauche pour produire une variable - la variablex
- nous évaluons le côté droit - la valeur actuelle dey
- l'assigner àx
, et le résultat est la valeur assignée. Nous comparons ensuite la valeur d'origine dex
à la valeur qui a été attribuée.Vous pouvez le faire
(x = y) == x
comme un exercice. Encore une fois, rappelez-vous, toutes les règles d'évaluation du côté gauche se produisent avant toutes les règles d'évaluation du côté droit .Votre attente est basée sur un ensemble de croyances incorrectes sur les règles de Java. J'espère que vous avez maintenant des croyances correctes et que vous vous attendez à l'avenir à de vraies choses.
Cette affirmation est fausse. Cette question est tout à fait pertinente.
Cette affirmation est également fausse. Il s'agit d'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 lisiez les spécifications jusqu'à ce que vos fausses croyances soient remplacées par de vraies croyances.
La provenance de l'expression n'est pas pertinente pour la question. Les règles pour de telles expressions sont clairement décrites dans la spécification; lis le!
Étant donné que ce remplacement sur une ligne 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 de rendre le code plus rapide.
Soit dit en passant , C # a comparer et remplacer comme méthode de bibliothèque, qui peut être transposée en une instruction machine. Je crois que Java n'a pas une telle méthode, car elle ne peut pas être représentée dans le système de type Java.
la source
Elle est liée à la priorité des opérateurs et à la façon 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 l'associativité de gauche à droite. L'affectation '=' vient en dernier et a une associativité de droite à gauche.
Pile d'utilisation du système pour évaluer l'expression. L'expression est évaluée de gauche à droite.
Venons-en maintenant à la question originale:
Le premier x (1) sera poussé pour s'empiler. puis inner (x = y) sera évalué et poussé pour s'empiler avec la valeur x (3). Maintenant x (1) sera comparé à x (3) donc le résultat est faux.
Ici, (x = y) sera évalué, maintenant la valeur x devient 3 et x (3) sera poussé pour s'empiler. Maintenant x (3) avec une valeur modifiée après l'égalité sera poussé vers la pile. Maintenant, l'expression sera évaluée et les deux seront les mêmes, donc le résultat est vrai.
la source
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:
Vous faites essentiellement la même chose que:
Et x aura la valeur de y après la comparaison.
Avec:
Vous faites essentiellement la même chose que:
Après x ont y valeur de. Et cela reviendra toujours vrai .
la source
Dans le premier test que vous vérifiez, ne 1 == 3.
Dans le deuxième test, votre vérification fait 3 == 3.
(x = y) attribue la valeur et cette valeur est testée. Dans le premier exemple, x = 1 en premier, puis x est attribué 3. Est-ce que 1 == 3?
Dans ce dernier, x se voit attribuer 3, et évidemment c'est toujours 3. Est-ce que 3 == 3?
la source
Considérez cet autre exemple, peut-être plus simple:
Ici, l'opérateur de pré-incrémentation dans
++x
doit être appliqué avant la comparaison - tout comme(x = y)
dans votre exemple doit être calculé avant la comparaison.Cependant, l' évaluation de l'expression se produit toujours de gauche → à → droite , donc la première comparaison est en fait
1 == 2
alors que la seconde l'est2 == 2
.La même chose se produit dans votre exemple.
la source
Les expressions sont évaluées de gauche à droite. Dans ce cas:
la source
Fondamentalement, la première instruction x avait sa 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é et donc lorsque vous l'appelez à nouveau, ce sera la même valeur, d'où la raison pour laquelle c'est vrai et x == x
la source
== est un opérateur d'égalité de comparaison et fonctionne de gauche à droite.
ici l'ancienne valeur assignée de x est comparée à la nouvelle valeur assignée de x, (1 == 3) // false
Alors que, ici, la nouvelle valeur assignée de x est comparée à la nouvelle valeur de maintien de x qui lui est assignée juste avant la comparaison, (3 == 3) // true
Considérez maintenant ceci
Ainsi, les parenthèses ne jouent leur rôle majeur dans les expressions arithmétiques que dans les expressions de comparaison.
la source
x + (x = y)
et(x = y) + x
montrerait un comportement similaire à l'original avec les opérateurs de comparaison.La chose ici est l'ordre de priorité des opérateurs arithmatiques / opérateurs relationnels des deux opérateurs par
=
rapport à==
celui dominant==
(les opérateurs relationnels dominent) car il précède les=
opérateurs d'affectation. Malgré la priorité, l'ordre d'évaluation est LTR (GAUCHE À DROITE) la priorité apparaît dans l'image après l'ordre d'évaluation. Donc, quelle que soit l'évaluation des contraintes, le LTR.la source
Il est facile dans la deuxième comparaison à gauche d'affecter après avoir assigné y à x (à gauche) puis de comparer 3 == 3. Dans le premier exemple vous comparez x = 1 avec une nouvelle assignation x = 3. Il semble que il y a toujours des états de lecture de l'état courant de gauche à droite de x.
la source
Le type de question que vous avez posée est une très bonne question si vous voulez écrire un compilateur Java ou tester des programmes pour vérifier qu'un compilateur Java fonctionne correctement. En Java, ces deux expressions doivent produire les résultats que vous avez vus. En C ++, par exemple, ils n'ont pas à le faire - donc si quelqu'un a réutilisé des parties d'un compilateur C ++ dans son compilateur Java, vous pourriez théoriquement trouver que le compilateur ne se comporte pas comme il se doit.
En tant que développeur de logiciels, écrivant du code qui est lisible, compréhensible et maintenable, les deux versions de votre code seraient considérées comme horribles. Pour comprendre ce que fait le code, il faut savoir exactement comment le langage Java est défini. Quelqu'un qui écrit à la fois du code Java et du code C ++ frémirait en regardant le code. Si vous devez vous demander pourquoi une seule ligne de code fait ce qu'elle fait, vous devez éviter ce code. (Je suppose et j'espère que les gars qui ont répondu correctement à votre question «pourquoi» éviteront eux-mêmes cet indice de code).
la source