Przypisanie dodawania + = zachowanie w wyrażeniu

Ostatnio natknąłem się na to pytanie: assignment operator Chain understanding.

Odpowiadając na to pytanie zacząłem wątpić we własne zrozumienie zachowania operatora przypisania dodawania += lub jakiegokolwiek innego operator= (&=, *=, /=, itd.).

Moje pytanie brzmi, kiedy zmienna a w wyrażeniach poniżej jest aktualizowana w miejscu, tak aby jej zmieniona wartość była odzwierciedlana w innych miejscach w wyrażeniu podczas oceny i co to jest logika za tym stoi? Proszę spojrzeć na następujące dwa wyrażenia:

Wyrażenie 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

Wyrażenie 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

W pierwszym wyrażeniu, gdy oceniane jest najbardziej wewnętrzne wyrażenie (a += a), wydaje się, że nie aktualizuje ono wartości a, więc wynik jest wyświetlany jako 3 zamiast 4.

Jednak w drugim wyrażeniu wartość a jest aktualizowana, a więc wynikiem jest 6.

Kiedy powinniśmy założyć, że a ' s wartość będzie odzwierciedlona w innych miejscach w wyrażeniu, a kiedy nie powinniśmy?

Author: pkpnd, 2018-06-15

3 answers

Pamiętaj, że a += x naprawdę oznacza a = a + x. Kluczowym punktem do zrozumienia jest to, że dodawanie jest oceniane od lewej do prawej - to znaczy a w a + x jest oceniane przed x.

Więc zastanówmy się, co b = (a += (a += a)) robi. Najpierw używamy reguły a += x oznacza a = a + x, a następnie zaczynamy dokładnie Oceniać wyrażenie w odpowiedniej kolejności:

  • b = (a = a + (a = a + a)) ponieważ a += x oznacza a = a + x
  • b = (a = 1 + (a = a + a)) ponieważ a jest obecnie 1. Pamiętaj, że oceniamy termin lewy a przed terminem prawym (a = a + a)
  • b = (a = 1 + (a = 1 + a)) ponieważ a jest nadal 1
  • b = (a = 1 + (a = 1 + 1)) ponieważ a jest nadal 1
  • b = (a = 1 + (a = 2)) ponieważ 1 + 1 jest 2
  • b = (a = 1 + 2) ponieważ a jest teraz 2
  • b = (a = 3) ponieważ 1 + 2 jest 3
  • b = 3 ponieważ a jest teraz 3

To pozostawia nam a = 3 i b = 3, jak uzasadniono powyżej.

Spróbujmy z innym wyrażeniem, b = (a += a) + (a += a):

  • b = (a = a + a) + (a = a + a)
  • b = (a = 1 + 1) + (a = a + a), pamiętaj, że oceniamy lewy termin przed prawym]}
  • b = (a = 2) + (a = a + a)
  • b = 2 + (a = a + a) i a jest teraz 2. Zacznij Oceniać właściwy termin
  • b = 2 + (a = 2 + 2)
  • b = 2 + (a = 4)
  • b = 2 + 4 i a jest teraz 4
  • b = 6

To pozostawia nam a = 4 i b = 6. Można to sprawdzić, drukując zarówno a, jak i b w Javie/JavaScript (oba mają takie samo zachowanie tutaj).


Pomocne może być również myślenie o tych wyrażeniach jako o drzewach parsowania. Kiedy oceniamy a + (b + c), LHS a jest oceniany przed RHS (b + c). Jest to zakodowane w strukturze drzewa:

   +
  / \
 a   +
    / \
   b   c

Zauważ, że nie mamy już żadnych nawiasów -- kolejność operacji jest zakodowana w strukturze drzewa. Kiedy oceniamy węzły w drzewie, przetwarzamy dzieci węzła w stałej kolejności (tj. od lewej do prawej dla +). Na przykład, gdy przetwarzamy węzeł korzeniowy +, oceniamy lewy podzbiór a przed prawym podzbiorem (b + c), niezależnie od tego, czy prawy podzbiór jest zamknięty w nawiasach, czy nie (ponieważ nawiasy nie są nawet obecne w drzewie analiz).

Z tego powodu Java/JavaScript wykonuje , a nie zawsze najpierw ocenia "najbardziej zagnieżdżone nawiasy", w przeciwieństwie do reguł, których można było nauczyć się arytmetyki.

Zobacz specyfikacja języka Java :

15.7. Zlecenie Oceny

Język programowania Java gwarantuje, że operandy operatorów wydają się być oceniane w określonej kolejności, a mianowicie od lewej do prawej.
...

15.7.1. Oceń Najpierw Operand Lewej Ręki

Lewostronny operand operatora binarnego wydaje się być w pełni oceniany, zanim jakakolwiek część prawostronnego operandu zostanie oceniona.

Jeśli operator jest operatorem złożonym-przypisanie operator (§15.26.2), następnie ocena lewego operandu obejmuje zarówno zapamiętanie zmiennej, którą lewy operand oznacza, jak i pobranie i zapisanie wartości tej zmiennej do użycia w domyślnej operacji binarnej.

Więcej przykładów podobnych do twojego pytania można znaleźć w połączonej części JLS, takich jak:

Przykład 15.7.1-1. Operand Lewej Ręki Jest Oceniany Jako Pierwszy

W poniższym programie operator * ma operand lewostronny, który zawiera przypisanie do zmiennej i operand prawej ręki, który zawiera odniesienie do tej samej zmiennej. Wartość wytworzona przez odniesienie będzie odzwierciedlać fakt, że przypisanie nastąpiło jako pierwsze.

class Test1 {
    public static void main(String[] args) {
        int i = 2;
        int j = (i=3) * i;
        System.out.println(j);
    }
}

Ten program wytwarza wyjście:

9

Nie wolno Oceniać operatora*, aby wyprodukować 6 zamiast 9.

 85
Author: pkpnd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-15 13:07:43

Oto zasady, o które należy zadbać

  • pierwszeństwo operatora
  • przypisanie zmiennej
  • Ocena ekspresji

    Wyrażenie 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)
    

    Wyrażenie 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
    

    Wyrażenie 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
Author: Nikhil Aggarwal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-15 07:06:54

Używa tylko odmiany kolejności operacji.

Jeśli potrzebujesz przypomnienia o kolejności ops:

PEMDAS:

P = Nawias

E = Wykładniki

MD = mnożenie/dzielenie

AS = dodawanie / odejmowanie

Reszta od lewej do prawej.

Ta odmiana jest po prostu czytana od lewej do prawej, ale jeśli widzisz nawias zrób wszystko w środku i zastąp go stałą, a następnie przejdź dalej.

Pierwszy ex:

var b = (a+=(a+=a))

var b = (1+=(1+=1))

var b = (1+=2)

var b = 3

Drugi 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);

Ostatni b = a += a += a ponieważ nie ma nawiasów, automatycznie staje się b = 1 += 1 += 1 czyli b = 3

 1
Author: Sheshank S.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-15 06:16:22