Perhatikan contoh berikut:
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
}
}
Saya tidak yakin apakah ada item dalam Spesifikasi Bahasa Jawa yang menentukan pemuatan nilai variabel sebelumnya untuk perbandingan dengan sisi kanan ( x = y
) yang, berdasarkan urutan yang ditunjukkan oleh tanda kurung, harus dihitung terlebih dahulu.
Mengapa ungkapan pertama dievaluasi false
, tetapi yang kedua mengevaluasi true
? Saya akan diharapkan (x = y)
untuk dievaluasi terlebih dahulu, dan kemudian akan membandingkan x
dengan dirinya sendiri ( 3
) dan kembali true
.
Pertanyaan ini berbeda dari urutan evaluasi subekspresi dalam ekspresi Java yang x
jelas bukan 'subekspresi' di sini. Perlu dimuat untuk perbandingan daripada harus 'dievaluasi'. Pertanyaannya adalah khusus-Jawa dan ekspresinya x == (x = y)
, tidak seperti konstruksi tidak praktis yang dibuat-buat yang biasanya dibuat untuk pertanyaan wawancara yang rumit, datang dari proyek nyata. Itu seharusnya menjadi pengganti satu baris untuk idiom bandingkan-dan-ganti
int oldX = x;
x = y;
return oldX == y;
yang, bahkan lebih sederhana dari instruksi x86 CMPXCHG, layak mendapatkan ekspresi yang lebih pendek di Java.
sumber
x = y
tentu relevan, dan menyebabkan efek samping yangx
diatur ke nilaiy
.Jawaban:
Tidak. Ini adalah kesalahpahaman umum bahwa tanda kurung memiliki efek (umum) pada perhitungan atau urutan evaluasi. Mereka hanya memaksa bagian dari ekspresi Anda ke pohon tertentu, mengikat operan yang tepat ke operasi yang tepat untuk pekerjaan itu.
(Dan, jika Anda tidak menggunakannya, informasi ini berasal dari "presedensi" dan asosiasi operator, sesuatu yang merupakan hasil dari bagaimana pohon sintaks bahasa didefinisikan. Bahkan, ini masih persis bagaimana cara kerjanya ketika Anda gunakan tanda kurung, tetapi kami menyederhanakan dan mengatakan bahwa kami tidak bergantung pada aturan prioritas sebelumnya.)
Setelah selesai (yaitu setelah kode Anda diurai menjadi sebuah program) operan tersebut masih perlu dievaluasi, dan ada aturan terpisah tentang bagaimana hal itu dilakukan: kata aturan (seperti yang ditunjukkan Andrew kepada kami) menyatakan bahwa LHS dari setiap operasi dievaluasi pertama kali di Jawa.
Perhatikan bahwa ini tidak terjadi di semua bahasa; misalnya, dalam C ++, kecuali jika Anda menggunakan operator hubungan pendek seperti
&&
atau||
, urutan evaluasi operan umumnya tidak ditentukan dan Anda tidak boleh mengandalkan itu baik.Para guru perlu berhenti menjelaskan prioritas operator menggunakan frasa menyesatkan seperti "ini membuat penambahan terjadi terlebih dahulu". Diberikan ekspresi
x * y + z
penjelasan yang tepat akan menjadi "operator diutamakan membuat penambahan terjadi antarax * y
danz
, bukan antaray
danz
", tanpa menyebutkan "pesanan".sumber
==
adalah operator kesetaraan biner .sumber
Seperti yang dikatakan LouisWasserman, ekspresi dievaluasi dari kiri ke kanan. Dan java tidak peduli apa yang "dievaluasi" sebenarnya, ia hanya peduli tentang menghasilkan nilai (non volatile, final) untuk bekerja dengannya.
Jadi untuk menghitung output pertama
System.out.println()
, berikut ini dilakukan:dan untuk menghitung yang kedua:
Perhatikan bahwa nilai kedua akan selalu bernilai true, terlepas dari nilai awal
x
dany
, karena Anda secara efektif membandingkan penetapan nilai dengan variabel yang ditugaskan padanya, dana = b
danb
akan, dievaluasi dalam urutan itu, selalu sama Menurut definisi.sumber
Ada. Lain kali Anda tidak jelas apa spesifikasi mengatakan, silakan baca spesifikasi dan kemudian ajukan pertanyaan jika tidak jelas.
Pernyataan itu salah. Tanda kurung tidak menyiratkan urutan evaluasi . Di Jawa, urutan evaluasi dibiarkan ke kanan, terlepas dari tanda kurung. Tanda kurung menentukan di mana batas subekspresi berada, bukan urutan evaluasi.
Aturan untuk
==
operator adalah: mengevaluasi sisi kiri untuk menghasilkan nilai, mengevaluasi sisi kanan untuk menghasilkan nilai, membandingkan nilai-nilai, perbandingannya adalah nilai ekspresi.Dengan kata lain, arti
expr1 == expr2
selalu sama seolah-olah Anda telah menulistemp1 = expr1; temp2 = expr2;
dan kemudian mengevaluasitemp1 == temp2
.Aturan untuk
=
operator dengan variabel lokal di sisi kiri adalah: mengevaluasi sisi kiri untuk menghasilkan variabel, mengevaluasi sisi kanan untuk menghasilkan nilai, melakukan penugasan, hasilnya adalah nilai yang ditugaskan.Jadi kumpulkan:
Kami memiliki operator pembanding. Mengevaluasi sisi kiri untuk menghasilkan nilai - kami mendapatkan nilai saat ini
x
. Mengevaluasi sisi kanan: itu adalah tugas jadi kami mengevaluasi sisi kiri untuk menghasilkan variabel - variabelx
- kami mengevaluasi sisi kanan - nilai saat iniy
- menetapkannyax
, dan hasilnya adalah nilai yang diberikan. Kami kemudian membandingkan nilai aslix
dengan nilai yang ditugaskan.Anda bisa melakukannya
(x = y) == x
sebagai latihan. Sekali lagi, ingat, semua aturan untuk mengevaluasi sisi kiri terjadi sebelum semua aturan mengevaluasi sisi kanan .Harapan Anda didasarkan pada seperangkat keyakinan yang salah tentang aturan Jawa. Semoga Anda sekarang memiliki keyakinan yang benar dan di masa depan akan mengharapkan hal-hal yang benar.
Pernyataan ini salah. Pertanyaan itu benar-benar erat.
Pernyataan ini juga salah. Ini adalah subekspresi dua kali dalam setiap contoh.
Saya tidak tahu apa artinya ini.
Tampaknya Anda masih memiliki banyak kepercayaan salah. Saran saya adalah Anda membaca spesifikasi sampai keyakinan salah Anda digantikan oleh keyakinan sejati.
Asal usul ungkapan tidak relevan dengan pertanyaan. Aturan untuk ekspresi semacam itu dijelaskan dengan jelas dalam spesifikasi; membacanya!
Karena penggantian satu baris menyebabkan banyak kebingungan pada Anda, pembaca kode, saya akan menyarankan bahwa itu adalah pilihan yang buruk. Membuat kode lebih ringkas tetapi lebih sulit untuk dipahami bukanlah suatu kemenangan. Tidak mungkin membuat kode lebih cepat.
Kebetulan, C # telah membandingkan dan mengganti sebagai metode perpustakaan, yang dapat dimasukkan ke instruksi mesin. Saya percaya Java tidak memiliki metode seperti itu, karena tidak dapat diwakili dalam sistem tipe Java.
sumber
Ini terkait dengan prioritas operator dan bagaimana operator dievaluasi.
Tanda kurung '()' memiliki prioritas lebih tinggi dan memiliki asosiativitas dari kiri ke kanan. Kesetaraan '==' datang berikutnya dalam pertanyaan ini dan memiliki asosiatif dari kiri ke kanan. Tugas '=' datang terakhir dan memiliki hak asosiatif ke kiri.
Sistem menggunakan tumpukan untuk mengevaluasi ekspresi. Ekspresi dievaluasi dari kiri ke kanan.
Sekarang sampai pada pertanyaan asli:
Pertama x (1) akan didorong ke stack. maka inner (x = y) akan dievaluasi dan didorong ke stack dengan nilai x (3). Sekarang x (1) akan dibandingkan dengan x (3) sehingga hasilnya salah.
Di sini, (x = y) akan dievaluasi, sekarang nilai x menjadi 3 dan x (3) akan didorong ke tumpukan. Sekarang x (3) dengan nilai yang diubah setelah kesetaraan akan didorong ke tumpukan. Sekarang ekspresi akan dievaluasi dan keduanya akan sama sehingga hasilnya benar.
sumber
Itu tidak sama. Sisi kiri akan selalu dievaluasi sebelum sisi kanan, dan kurung tidak menentukan urutan eksekusi, tetapi pengelompokan perintah.
Dengan:
Anda pada dasarnya melakukan hal yang sama seperti:
Dan x akan memiliki nilai y setelah perbandingan.
Sementara dengan:
Anda pada dasarnya melakukan hal yang sama seperti:
Setelah x mengambil nilai y . Dan itu akan selalu kembali benar .
sumber
Pada tes pertama yang Anda periksa apakah 1 == 3.
Pada tes kedua pemeriksaan Anda tidak 3 == 3.
(x = y) memberikan nilai dan nilai itu diuji. Dalam contoh sebelumnya x = 1 pertama maka x ditugaskan 3. Apakah 1 == 3?
Dalam yang terakhir, x ditugaskan 3, dan jelas itu masih 3. Apakah 3 == 3?
sumber
Pertimbangkan contoh lain yang mungkin lebih sederhana ini:
Di sini, operator pra-kenaikan
++x
harus diterapkan sebelum perbandingan dibuat - seperti(x = y)
pada contoh Anda harus dihitung sebelumnya perbandingan.Namun, evaluasi ekspresi masih terjadi kiri → ke → kanan , jadi perbandingan pertama sebenarnya
1 == 2
sedangkan yang kedua adalah2 == 2
.Hal yang sama terjadi pada contoh Anda.
sumber
Ekspresi dievaluasi dari kiri ke kanan. Pada kasus ini:
sumber
Pada dasarnya pernyataan pertama x memiliki nilai 1 Jadi Java membandingkan 1 == ke variabel x baru yang tidak akan sama
Di yang kedua Anda mengatakan x = y yang berarti nilai x berubah dan jadi ketika Anda menyebutnya lagi itu akan menjadi nilai yang sama maka mengapa itu benar dan x == x
sumber
== adalah operator kesetaraan perbandingan dan berfungsi dari kiri ke kanan.
di sini nilai x yang diberikan yang lama dibandingkan dengan nilai assign x yang baru, (1 == 3) // false
Sedangkan, di sini nilai penetapan x yang baru dibandingkan dengan nilai holding yang baru dari x yang diberikan tepat sebelum perbandingan, (3 == 3) // benar
Sekarang pertimbangkan ini
Dengan demikian, kurung memainkan peran utama dalam ekspresi aritmatika tidak hanya dalam ekspresi perbandingan.
sumber
x + (x = y)
dan(x = y) + x
akan menunjukkan perilaku yang sama seperti aslinya dengan operator pembanding.Masalahnya di sini adalah operator aritmatik / operator prioritas urutan keluar dari dua operator
=
vs==
yang dominan adalah==
(Operator Relasional mendominasi) karena mendahului=
operator penugasan. Meskipun didahulukan, urutan evaluasi adalah prioritas LTR (LEFT TO RIGHT) muncul setelah urutan evaluasi. Jadi, terlepas dari evaluasi kendala apa pun adalah LTR.sumber
Sangat mudah dalam perbandingan kedua di sebelah kiri adalah penugasan setelah menugaskan y ke x (di sebelah kiri) Anda kemudian membandingkan 3 == 3. Pada contoh pertama Anda membandingkan x = 1 dengan penugasan baru x = 3. Tampaknya bahwa selalu ada pernyataan pembacaan keadaan sekarang dari kiri ke kanan x.
sumber
Jenis pertanyaan yang Anda ajukan adalah pertanyaan yang sangat bagus jika Anda ingin menulis kompiler Java, atau menguji program untuk memverifikasi bahwa kompiler Java berfungsi dengan benar. Di Jawa, kedua ekspresi ini harus menghasilkan hasil yang Anda lihat. Dalam C ++, misalnya, mereka tidak harus - jadi jika seseorang menggunakan kembali bagian dari kompiler C ++ di kompiler Java mereka, Anda mungkin secara teoritis menemukan bahwa kompiler tidak berperilaku sebagaimana mestinya.
Sebagai pengembang perangkat lunak, menulis kode yang dapat dibaca, dimengerti dan dipelihara, kedua versi kode Anda akan dianggap mengerikan. Untuk memahami apa yang dikerjakan kode, kita harus tahu persis bagaimana bahasa Jawa didefinisikan. Seseorang yang menulis kode Java dan C ++ akan ngeri melihat kode. Jika Anda harus bertanya mengapa satu baris kode melakukan apa yang dilakukannya, maka Anda harus menghindari kode itu. (Saya kira dan berharap bahwa orang-orang yang menjawab pertanyaan "mengapa" Anda dengan benar akan sendiri menghindari kode itu juga).
sumber