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?
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
oznaczaa = a + x
-
b = (a = 1 + (a = a + a))
ponieważa
jest obecnie1
. Pamiętaj, że oceniamy termin lewya
przed terminem prawym(a = a + a)
-
b = (a = 1 + (a = 1 + a))
ponieważa
jest nadal1
-
b = (a = 1 + (a = 1 + 1))
ponieważa
jest nadal1
-
b = (a = 1 + (a = 2))
ponieważ1 + 1
jest2
-
b = (a = 1 + 2)
ponieważa
jest teraz2
-
b = (a = 3)
ponieważ1 + 2
jest3
-
b = 3
ponieważa
jest teraz3
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)
ia
jest teraz 2. Zacznij Oceniać właściwy termin b = 2 + (a = 2 + 2)
b = 2 + (a = 4)
-
b = 2 + 4
ia
jest teraz4
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.
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)
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
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