Rozważ następujący przykład:
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
}
}
Nie jestem pewien, czy w specyfikacji języka Java znajduje się element, który nakazuje ładowanie poprzedniej wartości zmiennej do porównania z prawą stroną ( x = y
), która według kolejności nawiasowej powinna być obliczona jako pierwsza.
Dlaczego pierwsze wyrażenie ocenia jako false
, a drugie ocenia true
? Spodziewałbym (x = y)
się najpierw oceny, a potem porównania x
z samym sobą ( 3
) i zwrotu true
.
To pytanie różni się od kolejności oceny podwyrażeń w wyrażeniu Java, ponieważ x
zdecydowanie nie jest to tutaj „podwyrażenie”. Należy go załadować do porównania, a nie „ocenić”. Pytanie jest specyficzne dla Javy, a wyrażenie x == (x = y)
, w przeciwieństwie do dalekosiężnych, niepraktycznych konstrukcji, często tworzonych na trudne pytania podczas wywiadu, pochodzi z prawdziwego projektu. Miał to być jednowierszowy zamiennik idiomu „porównaj i zamień”
int oldX = x;
x = y;
return oldX == y;
który, będąc jeszcze prostszym niż instrukcja CMPXCHG x86, zasługiwał na krótsze wyrażenie w Javie.
źródło
x = y
jest z pewnością istotna i powoduje efekt ubocznyx
ustawiony na wartośćy
.Odpowiedzi:
Nie. To powszechne błędne przekonanie, że nawiasy mają jakikolwiek (ogólny) wpływ na kolejność obliczeń lub oceny. Przymuszają tylko części twojego wyrażenia do konkretnego drzewa, wiążąc odpowiednie operandy z odpowiednimi operacjami dla zadania.
(A jeśli ich nie użyjesz, ta informacja pochodzi z „pierwszeństwa” i asocjatywności operatorów, co jest wynikiem tego, jak zdefiniowane jest drzewo składniowe języka. W rzeczywistości jest to dokładnie tak, jak działa używaj nawiasów, ale upraszczamy i mówimy, że nie polegamy wówczas na żadnych regułach pierwszeństwa).
Po wykonaniu tej czynności (tj. Po przeanalizowaniu kodu do programu) operandy te nadal wymagają oceny, a sposób ich wykonania jest odrębny: wspomniane reguły (jak pokazał nam Andrew) stwierdzają, że LHS każdej operacji jest oceniany jako pierwszy w Javie.
Pamiętaj, że nie jest tak we wszystkich językach; na przykład w C ++, chyba że używasz operatora zwarciowego, takiego jak
&&
lub||
, kolejność operandów jest ogólnie nieokreślona i nie powinieneś na nim polegać w żaden sposób.Nauczyciele muszą przestać wyjaśniać pierwszeństwo operatorów za pomocą zwrotów wprowadzających w błąd, takich jak „to sprawia, że dodawanie jest pierwsze”. Biorąc pod uwagę wyrażenie,
x * y + z
właściwe wyjaśnienie brzmiałoby: „pierwszeństwo operatora powoduje, że dodawanie zachodzi pomiędzyx * y
iz
zamiast pomiędzyy
iz
”, bez wzmianki o jakimkolwiek „porządku”.źródło
==
jest binarnym operatorem równości .źródło
Jak powiedział LouisWasserman, wyrażenie jest oceniane od lewej do prawej. A java nie dba o to, co faktycznie robi „ocenianie”, zależy tylko na wygenerowaniu (nieulotnej, końcowej) wartości do pracy.
Aby obliczyć pierwsze wyjście
System.out.println()
, należy wykonać następujące czynności:i obliczyć drugi:
Zauważ, że druga wartość zawsze będzie miała wartość true, niezależnie od wartości początkowych
x
iy
, ponieważ efektywnie porównujesz przypisanie wartości do zmiennej, do której jest przypisana,a = b
ib
będzie oceniane w tej kolejności, zawsze będzie to samo zgodnie z definicją.źródło
Jest. Następnym razem, gdy nie będziesz wiedział, co mówi specyfikacja, przeczytaj specyfikację, a następnie zadaj pytanie, czy jest niejasne.
To stwierdzenie jest fałszywe. Nawiasy nie oznaczają kolejności oceny . W Javie kolejność oceny jest od lewej do prawej, niezależnie od nawiasów. Nawiasy określają, gdzie znajdują się granice podwyrażeń, a nie kolejność oceny.
Reguła dla
==
operatora to: oceń lewą stronę, aby wygenerować wartość, oceń prawą stronę, aby wygenerować wartość, porównaj wartości, porównanie jest wartością wyrażenia.Innymi słowy, znaczenie
expr1 == expr2
jest zawsze takie samo, jak gdybyś napisał,temp1 = expr1; temp2 = expr2;
a następnie oceniłtemp1 == temp2
.Reguła dla
=
operatora z lokalną zmienną po lewej stronie: oszacuj lewą stronę, aby wygenerować zmienną, oceń prawą stronę, aby wygenerować wartość, wykonaj przypisanie, wynikiem jest wartość, która została przypisana.Więc złóż to razem:
Mamy operatora porównania. Oszacuj lewą stronę, aby uzyskać wartość - otrzymamy bieżącą wartość
x
. Oceń prawą stronę: jest to przypisanie, więc oceniamy lewą stronę, aby utworzyć zmienną - zmiennąx
- oceniamy prawą stronę - bieżącą wartośćy
- przypisujemy jąx
, a wynikiem jest przypisana wartość. Następnie porównujemy oryginalną wartość zx
wartością, która została przypisana.Możesz to zrobić
(x = y) == x
jako ćwiczenie. Ponownie pamiętajcie, że wszystkie zasady oceny lewej strony mają miejsce przed wszystkimi zasadami oceny prawej strony .Twoje oczekiwania oparte są na zestawie niepoprawnych przekonań na temat reguł Java. Mam nadzieję, że masz teraz prawidłowe przekonania i w przyszłości będziesz oczekiwać prawdziwych rzeczy.
To stwierdzenie jest fałszywe. To pytanie jest całkowicie niemądre.
To stwierdzenie jest również fałszywe. Jest to podwyrażenie dwa razy w każdym przykładzie.
Nie mam pojęcia, co to znaczy.
Najwyraźniej nadal masz wiele fałszywych przekonań. Moja rada jest taka, że czytasz specyfikację, dopóki twoje fałszywe przekonania nie zostaną zastąpione prawdziwymi przekonaniami.
Pochodzenie tego wyrażenia nie ma znaczenia dla pytania. Zasady dotyczące takich wyrażeń są jasno opisane w specyfikacji; Przeczytaj to!
Ponieważ to zastąpienie jednego wiersza spowodowało w tobie wiele zamieszania, czytelniku kodu, sugerowałbym, że był to zły wybór. Uczynienie kodu bardziej zwięzłym, ale trudniejszym do zrozumienia, nie jest wygraną. Jest mało prawdopodobne, aby kod był szybszy.
Nawiasem mówiąc, C # ma porównywać i zamieniać jako metodę biblioteczną, którą można przypisać do instrukcji maszyny. Wierzę, że Java nie ma takiej metody, ponieważ nie może być reprezentowana w systemie typów Java.
źródło
Jest to związane z pierwszeństwem operatorów i sposobem ich oceny.
Nawiasy „()” mają wyższy priorytet i mają skojarzenia od lewej do prawej. Równość „==” jest następna w tym pytaniu i ma skojarzenia od lewej do prawej. Zadanie „=” jest ostatnie i ma skojarzenie od prawej do lewej.
System używa stosu do oceny wyrażenia. Wyrażenie jest oceniane od lewej do prawej.
Teraz dochodzi do pierwotnego pytania:
Pierwszy x (1) zostanie przesunięty na stos. następnie wewnętrzny (x = y) zostanie oceniony i wypchnięty na stos z wartością x (3). Teraz x (1) zostanie porównane z x (3), więc wynik jest fałszywy.
Tutaj (x = y) zostanie oszacowane, teraz wartość x staje się 3, a x (3) zostanie przesunięty na stos. Teraz x (3) ze zmienioną wartością po równości zostanie przesunięty na stos. Teraz wyrażenie zostanie ocenione i oba będą takie same, więc wynik jest prawdziwy.
źródło
To nie jest to samo. Lewa strona zawsze będzie oceniana przed prawą stroną, a nawiasy nie określają kolejności wykonywania, lecz grupę poleceń.
Z:
Zasadniczo robisz to samo, co:
I x będzie miało wartość y po porównaniu.
Podczas gdy z:
Zasadniczo robisz to samo, co:
Po x trwało y jest wartość. I zawsze zwróci się prawda .
źródło
W pierwszym teście sprawdzasz, czy 1 == 3.
W drugim teście sprawdzanie wynosi 3 == 3.
(x = y) przypisuje wartość i ta wartość jest testowana. W poprzednim przykładzie najpierw x = 1, a następnie x. 3. Czy 1 == 3?
W tym ostatnim x ma przypisane 3 i oczywiście nadal jest 3. Czy 3 == 3?
źródło
Rozważ ten inny, być może prostszy przykład:
Tutaj operator wstępnej inkrementacji
++x
musi zostać zastosowany przed dokonaniem porównania - tak jak(x = y)
w twoim przykładzie, musi zostać obliczony przed porównaniem.Jednak ocena wyrażenia nadal odbywa się od lewej → do → prawej , więc pierwsze porównanie jest w rzeczywistości,
1 == 2
podczas gdy drugie jest2 == 2
.To samo dzieje się w twoim przykładzie.
źródło
Wyrażenia są oceniane od lewej do prawej. W tym przypadku:
źródło
Zasadniczo pierwsza instrukcja x miała wartość 1, więc Java porównuje 1 == z nową zmienną x, która nie będzie taka sama
W drugim powiedziałeś, że x = y, co oznacza, że wartość x uległa zmianie, więc kiedy wywołasz ją ponownie, będzie to ta sama wartość, dlatego jest to prawda, a x == x
źródło
== jest operatorem porównania równości i działa od lewej do prawej.
tutaj stara przypisana wartość x jest porównywana z nową wartością przypisania x, (1 == 3) // false
Podczas gdy tutaj nowa wartość przypisania x jest porównywana z nową wartością trzymania x przypisaną do niej tuż przed porównaniem, (3 == 3) // true
Rozważ to teraz
Zatem nawiasy odgrywają główną rolę w wyrażeniach arytmetycznych, a nie w wyrażeniach porównawczych.
źródło
x + (x = y)
i(x = y) + x
wykazywałoby podobne zachowanie jak oryginał z operatorami porównania.Chodzi o to, że kolejność priorytetów operatorów arytmetycznych / operatorów relacyjnych spośród dwóch operatorów
=
względem==
dominującej to==
(operatory relacyjne dominują), ponieważ poprzedza ona=
operatory przypisania. Mimo pierwszeństwa kolejność oceny jest następująca LTR (LEWO DO PRAWO) po kolejności oceny. Niezależnie od jakichkolwiek ograniczeń ocena jest LTR.źródło
Łatwo jest w drugim porównaniu po lewej stronie przypisanie po przypisaniu y do x (po lewej), a następnie porównanie 3 == 3. W pierwszym przykładzie porównujesz x = 1 z nowym przypisaniem x = 3. Wydaje się, że zawsze pobierane są instrukcje odczytu stanu bieżącego od lewej do prawej strony x.
źródło
Pytanie, które zadałeś, jest bardzo dobrym pytaniem, jeśli chcesz napisać kompilator Java lub przetestować programy, aby sprawdzić, czy kompilator Java działa poprawnie. W Javie te dwa wyrażenia muszą dawać widoczne wyniki. Na przykład w C ++ nie muszą - więc jeśli ktoś ponownie używał części kompilatora C ++ w swoim kompilatorze Java, teoretycznie może się okazać, że kompilator nie zachowuje się tak, jak powinien.
Jako twórca oprogramowania, pisząc kod, który jest czytelny, zrozumiały i łatwy w utrzymaniu, obie wersje kodu byłyby uważane za okropne. Aby zrozumieć, co robi kod, trzeba dokładnie wiedzieć , jak definiowany jest język Java. Ktoś, kto pisze zarówno kod Java, jak i C ++, wzdrygnie się na niego. Jeśli musisz zapytać, dlaczego pojedynczy wiersz kodu robi to, co robi, powinieneś unikać tego kodu. (Przypuszczam i mam nadzieję, że faceci, którzy poprawnie odpowiedzieli na pytanie „dlaczego”, również unikną tego indeksu kodu).
źródło