Affectation d'Addition += comportement dans l'expression

récemment, je suis tombé sur cette question: affectation opérateur chaîne compréhension .

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 toute autre operator= ( &= , *= , /= , etc.).

ma question est, quand est la variable a dans les expressions ci-dessous mis à jour en place, de sorte que sa valeur changée se reflète dans d'autres endroits dans l'expression au cours de l'évaluation, et quelle est la logique derrière elle? S'il vous plaît jeter un oeil aux 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 profonde (a += a) est évaluée, il semble qu'elle ne mette pas à jour la valeur de a , le résultat est donc 3 au lieu de 4 .

cependant, dans la seconde expression, la valeur de a est mise à jour et donc le résultat est 6.

Quand devons-nous supposer que a valeur sera reflété dans d'autres endroits dans l'expression et quand doit-on pas?

58
demandé sur pkpnd 2018-06-15 08:51:15

3 réponses

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 le a dans a + x est évalué avant x .

donc trouvons ce que b = (a += (a += a)) fait. Tout d'abord , nous utilisons la règle a += x signifie a = a + x , puis nous commençons à évaluer l'expression avec soin 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 de gauche a avant le terme de droite (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 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 de gauche avant le terme de droite
  • b = (a = 2) + (a = a + a)
  • b = 2 + (a = a + a) et a est maintenant de 2. Commencer à évaluer le bon terme
  • b = 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 à la fois a et b en Java/JavaScript (les deux ont le même comportement ici).


il pourrait également être utile de penser à ces expressions comme arbres d'analyse. Lorsque nous évaluons a + (b + c) , le LHS a est évalué avant le RHS (b + c) . Ceci est encodé dans la structure de l'arbre:

   +
  / \
 a   +
    / \
   b   c

notez que nous n'avons plus de parenthèses -- l'ordre des opérations est encodé dans la structure de l'arbre. Lorsque nous évaluons les noeuds dans l'arbre, nous traitons les enfants du noeud dans un ordre fixe (i.e., de gauche à droite pour + ). Par exemple, lorsque nous traitons le noeud racine + , nous évaluons le sous-arbre de gauche a avant le sous-arbre de droite (b + c) , indépendamment du fait que le sous-arbre de droite est enfermé entre parenthèses ou non (puisque les parenthèses ne sont même pas présentes dans l'arbre de parse).

pour cette raison, Java/JavaScript ne pas évaluez toujours les "parenthèses les plus imbriquées" en premier, contrairement aux règles que vous pourriez avoir été enseigné pour l'arithmétique.

voir la Java Language Specification :

15.7. Ordonnance D'Évaluation

le langage de programmation Java garantit que les opérandes des opérateurs semblent être évaluées dans un ordre d'évaluation , c'est-à-dire de gauche à droite.

...

15.7.1. Évaluer L'Opérande De Gauche D'Abord 15191360920"

La gauche opérande d'un opérateur binaire semble être pleinement évalués avant qu'une partie de la droite opérande est évaluée.

si l'opérateur est un opérateur d'assignation composé (§15.26.2), l'évaluation de l'opérande de gauche inclut à la fois la mémorisation de la variable que l'opérande de gauche désigne et la récupération et la sauvegarde de la valeur de cette variable pour utilisation dans l'opération binaire implicite.

D'autres exemples similaires à votre question peuvent être trouvés dans la partie liée du JLS, tels que:

exemple 15.7.1-1. L'Opérande De Gauche Est Évaluée En Premier

Dans le programme suivant, l'opérateur * a gauche opérande qui contient une assignation à une variable et un opérande de droite qui contient une référence à la même variable. La valeur produite par l' la référence tiendra compte du 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 le résultat:

9

It est interdit pour l'évaluation de l'opérateur de produire 6 au lieu de 9.

85
répondu pkpnd 2018-06-15 13:07:43

Voici les règles qui doivent être prises en charge de

  • priorité de L'opérateur
  • assignation Variable
  • l'é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)
    
7
répondu Nikhil Aggarwal 2018-06-15 07:06:54

il utilise juste une variation de l'ordre des opérations.

si vous avez besoin d'un rappel de l'ordre de ops:

PEMDAS:

P = Parenthèse

E = Exposants

MD = Multiplication / Division

COMME = Addition / Soustraction

Le reste de gauche à droite.

cette variation est juste lue de gauche à droite, mais si vous voyez une parenthèse faire tout à l'intérieur, et le remplacer par une constante puis passer à 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);

La dernière b = a += a += a car il n'y a pas de parenthèse, il devient automatiquement un b = 1 += 1 += 1 , qui est b = 3

1
répondu Sheshank S. 2018-06-15 06:16:22