Je suis récemment tombé sur cette question: compréhension de la chaîne d’opérateur .
En répondant à cette question, j'ai commencé à douter de ma propre compréhension du comportement de l'opérateur d'affectation d'addition +=
Ou de tout autre operator=
(&=
, *=
, /=
, Etc.).
Ma question est la suivante: quand la variable a
dans les expressions ci-dessous est-elle mise à jour sur place, de sorte que sa valeur modifiée soit reflétée à d'autres endroits dans l'expression au cours de l'évaluation, et quelle est la logique qui la sous-tend? S'il vous plaît jeter un oeil à deux expressions suivantes:
Expression 1
a = 1
b = (a += (a += a))
//b = 3 is the result, but if a were updated in place then it should've been 4
Expression 2
a = 1
b = (a += a) + (a += a)
//b = 6 is the result, but if a is not updated in place then it should've been 4
Dans la première expression, lorsque l'expression la plus interne (a += a)
Est évaluée, il semble qu'elle ne mette pas à jour la valeur de a
. Le résultat apparaît donc sous la forme 3
Au lieu de 4
.
Cependant, dans la deuxième expression, la valeur de a
est mise à jour et le résultat est 6.
Quand devrions-nous supposer que la valeur de a
sera reflétée à d'autres endroits dans l'expression et quand devrions-nous pas?
Rappelez-vous que a += x
Signifie vraiment a = a + x
. Le point clé à comprendre est que l’ajout est évalué de gauche à droite - c’est-à-dire que a
dans a + x
est évalué avant x
.
Voyons donc ce que fait b = (a += (a += a))
. Tout d'abord, nous utilisons la règle a += x
Signifie a = a + x
, Puis nous commençons à évaluer soigneusement l'expression dans le bon ordre:
b = (a = a + (a = a + a))
parce que a += x
signifie a = a + x
b = (a = 1 + (a = a + a))
parce que a
est actuellement 1
. Rappelez-vous que nous évaluons le terme gauche a
avant le terme correct (a = a + a)
b = (a = 1 + (a = 1 + a))
parce que a
est toujours 1
b = (a = 1 + (a = 1 + 1))
parce que a
est toujours 1
b = (a = 1 + (a = 2))
parce que 1 + 1
est 2
b = (a = 1 + 2)
parce que a
est maintenant 2
b = (a = 3)
parce que 1 + 2
est 3
b = 3
Parce que a
est maintenant 3
Cela nous laisse avec a = 3
Et b = 3
Comme expliqué ci-dessus.
Essayons ceci avec l'autre expression, b = (a += a) + (a += a)
:
b = (a = a + a) + (a = a + a)
b = (a = 1 + 1) + (a = a + a)
, rappelez-vous que nous évaluons le terme gauche avant celui de droiteb = (a = 2) + (a = a + a)
b = 2 + (a = a + a)
et a
est maintenant 2. Commencez à évaluer le bon termeb = 2 + (a = 2 + 2)
b = 2 + (a = 4)
b = 2 + 4
Et a
est maintenant 4
b = 6
Cela nous laisse avec a = 4
Et b = 6
. Ceci peut être vérifié en imprimant a
et b
en Java/JavaScript (les deux ont le même comportement ici).
Il peut également être utile de considérer ces expressions comme des arbres d'analyse syntaxique. Lorsque nous évaluons a + (b + c)
, le LHS a
est évalué avant le RHS (b + c)
. Ceci est encodé dans l'arborescence:
+
/ \
a +
/ \
b c
Notez que nous n'avons plus de parenthèses - l'ordre des opérations est codé dans l'arborescence. Lorsque nous évaluons les noeuds de l’arbre, nous traitons les enfants du noeud dans un ordre fixe (c’est-à-dire de gauche à droite pour +
). Par exemple, lorsque nous traitons le nœud racine +
, Nous évaluons le sous-arbre gauche a
avant le sous-arbre droit (b + c)
, Que le sous-arbre droit soit entre parenthèses ou non. (puisque les parenthèses ne sont même pas présentes dans l'arbre d'analyse).
De ce fait, Java/JavaScript ne pas évalue toujours les "parenthèses les plus imbriquées" en premier lieu, contrairement aux règles qui auraient pu vous être enseignées pour l'arithmétique.
Voir le Spécification du langage Java :
15.7. Commande d'évaluation
Le langage de programmation Java) garantit que les opérandes des opérateurs semblent être évalués dans un ordre d'évaluation spécifique , à savoir de gauche à droite.
...15.7.1. Évaluez d'abord l'opérande gauche
L'opérande gauche d'un opérateur binaire semble être entièrement évalué avant qu'une partie quelconque de l'opérande droit ne soit évaluée.
Si l'opérateur est un opérateur d'assignation composée (§15.26.2), l'évaluation de l'opérande de gauche inclut à la fois la mémorisation de la variable indiquée par l'opérande de gauche, l'extraction et la sauvegarde de la valeur de cette variable à utiliser dans l'opération binaire implicite. .
Vous trouverez d'autres exemples similaires à votre question dans la partie liée du JLS, tels que:
Exemple 15.7.1-1. L'opérande gauche est évalué en premier
Dans le programme suivant, l'opérateur * a un opérande de gauche contenant une affectation à une variable et un opérande de droite contenant une référence à la même variable. La valeur produite par la référence reflétera le fait que l'affectation a eu lieu en premier.
class Test1 { public static void main(String[] args) { int i = 2; int j = (i=3) * i; System.out.println(j); } }
Ce programme produit la sortie:
9
Il n’est pas permis à l’évaluateur * d’en produire 6 au lieu de 9.
Voici les règles à prendre en compte
évaluation de l'expression
Expression 1
a = 1
b = (a += (a += a))
b = (1 += (a += a)) // a = 1
b = (1 += (1 += a)) // a = 1
b = (1 += (1 += 1)) // a = 1
b = (1 += (2)) // a = 2 (here assignment is -> a = 1 + 1)
b = (3) // a = 3 (here assignment is -> a = 1 + 2)
Expression 2
a = 1
b = (a += a) + (a += a)
b = (1 += a) + (a += a) // a = 1
b = (1 += 1) + (a += a) // a = 1
b = (2) + (a += a) // a = 2 (here assignment is -> a = 1 + 1)
b = (2) + (2 += a) // a = 2 (here here a = 2)
b = (2) + (2 += 2) // a = 2
b = (2) + (4) // a = 4 (here assignment is -> a = 2 + 2)
b = 6 // a = 4
Expression 3
a = 1
b = a += a += a += a += a
b = 1 += 1 += 1 += 1 += 1 // a = 1
b = 1 += 1 += 1 += 2 // a = 2 (here assignment is -> a = 1 + 1)
b = 1 += 1 += 3 // a = 3 (here assignment is -> a = 1 + 2)
b = 1 += 4 // a = 4 (here assignment is -> a = 1 + 3)
b = 5 // a = 5 (here assignment is -> a = 1 + 4)
Il utilise simplement une variation de l'ordre des opérations.
Si vous avez besoin d’un rappel d’ordre des opérations:
PEMDAS:
P = Parenthèse
E = Exposants
MD = Multiplication/Division
COMME = Addition/Soustraction
Le reste de gauche à droite.
Cette variation est lue de gauche à droite, mais si vous voyez une parenthèse tout faire à l’intérieur, remplacez-la par une constante puis passez à autre chose.
Premier ex:
var b = (a+=(a+=a))
var b = (1+=(1+=1))
var b = (1+=2)
var b = 3
Deuxième ex:
var b = (a+=a)+(a+=a)
var b = (1+=1)+(a+=a)
var b = 2 + (2+=2)
var b = 2 + 4
var b = 6
var a = 1
var b = (a += (a += a))
console.log(b);
a = 1
b = (a += a) + (a += a)
console.log(b);
a = 1
b = a += a += a;
console.log(b);
Le dernier b = a += a += a
Puisqu'il n'y a pas de parenthèse, il devient automatiquement b = 1 += 1 += 1
Qui est b = 3