Saya tidak tahu apa sebutannya ini, tetapi saya selalu melihatnya. Implementasi Python adalah sesuatu seperti:
x += 5
sebagai notasi steno untuk x = x + 5
.
Tetapi mengapa ini dianggap praktik yang baik? Saya telah menjalankannya di hampir setiap buku atau tutorial pemrograman yang pernah saya baca untuk Python, C, R , dan sebagainya. Saya merasa nyaman, menghemat tiga kali penekanan tombol termasuk spasi. Tetapi mereka selalu membuat saya tersandung ketika saya membaca kode, dan setidaknya dalam pikiran saya, membuatnya kurang mudah dibaca, tidak lebih.
Apakah saya kehilangan alasan yang jelas dan jelas mengapa ini digunakan di semua tempat?
programming-practices
Fomite
sumber
sumber
x += 5
dari CLRx = x + 5
? Atau itu benar-benar hanya gula sintaksis seperti yang Anda sarankan?Jawaban:
Itu bukan steno.
The
+=
simbol muncul dalam bahasa C pada 1970-an, dan - dengan C gagasan "assembler pintar" sesuai dengan mode instruksi mesin dan adressing jelas berbeda:Hal-hal seperti "
i=i+1
","i+=1
"dan"++i
", meskipun pada level abstrak menghasilkan efek yang sama, berhubungan pada level rendah dengan cara kerja prosesor yang berbeda.Secara khusus ketiga ekspresi tersebut, dengan asumsi
i
variabel berada di alamat memori yang disimpan dalam register CPU (sebut sajaD
- anggap sebagai "penunjuk ke int") dan ALU prosesor mengambil parameter dan mengembalikan hasil dalam "Akumulator" (sebut saja A - pikirkan sebagai int).Dengan kendala-kendala ini (sangat umum di semua mikroprosesor dari periode itu), kemungkinan besar terjemahannya
Cara pertama untuk melakukannya adalah disoptimal, tetapi lebih umum ketika beroperasi dengan variabel daripada konstan (
ADD A, B
atauADD A, (D+x)
) atau ketika menerjemahkan ekspresi yang lebih kompleks (mereka semua mendidih dalam mendorong operasi prioritas rendah dalam tumpukan, sebut prioritas tinggi, pop dan ulangi sampai semua argumen dihilangkan).Yang kedua lebih khas dari "mesin negara": kita tidak lagi "mengevaluasi ekspresi", tetapi "mengoperasikan nilai": kita masih menggunakan ALU, tetapi menghindari memindahkan nilai sekitar karena hasilnya diperbolehkan untuk mengganti parameter. Instruksi semacam ini tidak dapat digunakan di mana ekspresi yang lebih rumit diperlukan:
i = 3*i + i-2
tidak dapat dioperasikan di tempat, karenai
diperlukan lebih banyak kali.Yang ketiga sederhana bahkan tidak mempertimbangkan gagasan "penambahan", tetapi menggunakan sirkuit yang lebih "primitif" (dalam arti komputasi) untuk penghitung. Instruksi ini disingkat, memuat lebih cepat dan dieksekusi segera, karena jaringan kombinatorial diperlukan untuk retrofit register untuk menjadikannya penghitung yang lebih kecil, dan karenanya lebih cepat daripada yang dari penambah penuh.
Dengan kompiler kontemporer (lihat C, sekarang), memungkinkan optimisasi kompiler, korespondensi dapat ditukar berdasarkan kenyamanan, tetapi masih ada perbedaan konseptual dalam semantik.
x += 5
caraTetapi
x = x + 5
berarti:Tentu saja, optimasi bisa
&x
sebagai ganti ke akumulatorsehingga membuat kode yang dioptimalkan bertepatan
x += 5
satu.Tetapi ini dapat dilakukan hanya jika "menemukan x" tidak memiliki efek samping, sebaliknya
dan
secara semantik berbeda, karena
x()
efek samping (mengakuix()
adalah fungsi melakukan hal-hal aneh di sekitar dan mengembalikanint*
) akan diproduksi dua kali atau sekali.Kesetaraan antara
x = x + y
danx += y
karenanya karena kasus khusus di mana+=
dan=
diterapkan pada nilai-l langsung.Untuk pindah ke Python, itu mewarisi sintaksis dari C, tetapi karena tidak ada terjemahan / optimasi SEBELUM eksekusi dalam bahasa yang ditafsirkan, hal-hal tidak harus begitu terkait erat (karena ada satu langkah penguraian yang kurang). Namun, seorang juru bahasa dapat merujuk ke rutinitas eksekusi yang berbeda untuk tiga jenis ekspresi, mengambil keuntungan dari kode mesin yang berbeda tergantung pada bagaimana ekspresi dibentuk dan pada konteks evaluasi.
Bagi yang suka lebih detail ...
Setiap CPU memiliki ALU (unit aritmatika-logis) yaitu, pada intinya, jaringan kombinatorial yang input dan outputnya "dicolokkan" ke register dan / atau memori tergantung pada opcode instruksi.
Operasi biner biasanya diimplementasikan sebagai "pengubah register akumulator dengan input yang diambil" di suatu tempat ", di mana suatu tempat dapat berada - di dalam aliran instruksi itu sendiri (khas untuk manifes kontan: ADD A 5) - di dalam registri lain (tipikal untuk ekspresi ekspresi dengan temporaries: eg ADD AB) - di dalam memori, pada alamat yang diberikan oleh register (tipikal pengambilan data misalnya: ADD A (H)) - H, dalam hal ini, berfungsi seperti penunjuk dereferencing.
Dengan kodesemu ini,
x += 5
adalahsementara
x = x+5
iniArtinya, x + 5 memberikan sementara yang kemudian ditugaskan.
x += 5
beroperasi langsung pada x.Implementasi aktual tergantung pada set instruksi nyata dari prosesor: Jika tidak ada
ADD (.) c
opcode, kode pertama menjadi yang kedua: tidak mungkin.Jika ada opcode seperti itu, dan optimisasi diaktifkan, ekspresi kedua, setelah menghilangkan gerakan mundur dan menyesuaikan opcode register, menjadi yang pertama.
sumber
Tergantung pada bagaimana Anda berpikir tentang hal itu, sebenarnya lebih mudah dipahami karena lebih mudah. Ambil, misalnya:
x = x + 5
meminta pemrosesan mental "ambil x, tambahkan lima untuk itu, dan kemudian tetapkan nilai baru itu kembali ke x"x += 5
dapat dianggap sebagai "naikkan x 5"Jadi, ini bukan hanya istilah singkat, itu sebenarnya menggambarkan fungsi jauh lebih langsung. Saat membaca sekumpulan kode, lebih mudah dipahami.
sumber
x = x + 5
masih mengganggu saya. Ketika saya masuk matematika di usia yang lebih tua, itu lebih mengganggu saya. Menggunakanx += 5
secara signifikan lebih deskriptif dan jauh lebih masuk akal sebagai ungkapan.reallyreallyreallylongvariablename = reallyreallyreallylongvariablename + 1
... oh noes !!! a typoSetidaknya dengan Python ,
x += y
danx = x + y
dapat melakukan hal-hal yang sama sekali berbeda.Misalnya, jika kita lakukan
kemudian
a += [3]
akan menghasilkana == b == [3]
, sedangkana = a + [3]
akan menghasilkana == [3]
danb == []
. Yaitu,+=
memodifikasi objek di tempat (well, mungkin bisa, Anda dapat mendefinisikan__iadd__
metode untuk melakukan hampir semua hal yang Anda suka), sambil=
membuat objek baru dan mengikat variabel ke sana.Ini sangat penting ketika melakukan pekerjaan numerik dengan NumPy , karena Anda sering berakhir dengan beberapa referensi ke berbagai bagian array, dan penting untuk memastikan Anda tidak secara tidak sengaja memodifikasi bagian dari array yang ada referensi lain, atau menyalin array yang tidak perlu (yang bisa sangat mahal).
sumber
__iadd__
: ada bahasa di mana Anda dapat membuat referensi berubah ke datastructure bisa berubah, di manaoperator +=
didefinisikan, misalnya scala:val sb = StringBuffer(); lb += "mutable structure"
vsvar s = ""; s += "mutable variable"
. lebih lanjut memodifikasi konten struktur data sementara yang terakhir membuat titik variabel ke yang baru.Itu disebut idiom . Pemrograman idiom berguna karena mereka adalah cara yang konsisten untuk menulis konstruksi pemrograman tertentu.
Setiap kali seseorang menulis,
x += y
Anda tahu bahwax
itu bertambah olehy
dan bukan operasi yang lebih kompleks (sebagai praktik terbaik, biasanya saya tidak akan mencampur operasi yang lebih rumit dan steno sintaksis ini). Ini paling masuk akal saat ditambah 1.sumber
++x
danx+=1
setara dalam C dan Java (mungkin juga C #) meskipun tidak harus begitu dalam C ++ karena semantik operator yang kompleks di sana. Kuncinya adalah bahwa mereka berdua mengevaluasix
satu kali, menambah variabel dengan satu, dan memiliki hasil yang merupakan isi dari variabel setelah evaluasi.++x + 3
tidak sama denganx += 1 + 3
. Kurungx += 1
dan identik. Sebagai pernyataan sendiri, mereka identik.++x + 3
,x += 1 + 3
atau(x += 1) + 3
memiliki perilaku yang tidak terdefinisi (dengan asumsi nilai yang dihasilkan "cocok").Untuk menempatkan poin @ Pubby sedikit lebih jelas, pertimbangkan
someObj.foo.bar.func(x, y, z).baz += 5
Tanpa
+=
operator, ada dua cara:someObj.foo.bar.func(x, y, z).baz = someObj.foo.bar.func(x, y, z).baz + 5
. Ini tidak hanya sangat berlebihan dan panjang, ini juga lebih lambat. Karena itu orang harustmp := someObj.foo.bar.func(x, y, z); tmp.baz = tmp.bar + 5
. Ini baik-baik saja, tetapi banyak kebisingan untuk hal sederhana. Ini sebenarnya sangat dekat dengan apa yang terjadi pada saat runtime, tetapi membosankan untuk menulis dan hanya menggunakan+=
akan menggeser pekerjaan ke kompiler / juru bahasa.Keuntungan dari
+=
dan operator lainnya tidak dapat disangkal, sementara membiasakan diri dengan mereka hanyalah masalah waktu.sumber
Memang benar bahwa itu lebih pendek dan lebih mudah, dan memang benar bahwa itu mungkin terinspirasi oleh bahasa assembly yang mendasarinya, tetapi alasan itu praktik terbaik adalah bahwa itu mencegah seluruh kelas kesalahan, dan itu membuatnya lebih mudah untuk meninjau kode dan memastikan apa fungsinya.
Dengan
Karena hanya ada satu nama variabel yang terlibat, Anda yakin apa yang dilakukan pernyataan itu.
Dengan
RidiculouslyComplexName = RidiculosulyComplexName + 1;
Selalu ada keraguan bahwa kedua belah pihak persis sama. Apakah Anda melihat bug? Itu menjadi lebih buruk ketika langganan dan kualifikasi hadir.
sumber
Walaupun notasi + = idiomatik dan lebih pendek, ini bukan alasan mengapa lebih mudah dibaca. Bagian terpenting dari membaca kode adalah memetakan sintaks terhadap makna, dan semakin dekat sintaksinya dengan proses berpikir programmer, semakin mudah dibaca (ini juga alasan mengapa kode boilerplate buruk: bukan bagian dari pemikiran proses, tetapi masih perlu untuk membuat fungsi kode). Dalam hal ini, pemikirannya adalah "variabel kenaikan x sebesar 5", bukan "biarkan x menjadi nilai x ditambah 5".
Ada kasus-kasus lain di mana notasi yang lebih pendek buruk untuk keterbacaan, misalnya ketika Anda menggunakan operator ternary di mana
if
pernyataan akan lebih sesuai.sumber
Untuk mengetahui mengapa operator-operator ini menggunakan bahasa 'C-style', ada kutipan dari K&R 1st Edition (1978), 34 tahun yang lalu:
Saya pikir jelas dari bagian ini bahwa Brian Kernighan dan Dennis Ritchie (K&R), percaya bahwa operator penugasan majemuk membantu keterbacaan kode.
Sudah lama sejak K&R menulis itu, dan banyak 'praktik terbaik' tentang bagaimana orang harus menulis kode telah berubah atau berkembang sejak saat itu. Tetapi pertanyaan programmer ini .stackexchange adalah pertama kali saya dapat mengingat seseorang menyuarakan keluhan tentang keterbacaan tugas majemuk, jadi saya bertanya-tanya apakah banyak programmer menemukan mereka menjadi masalah? Kemudian lagi, saat saya mengetik ini pertanyaannya memiliki 95 upvotes, jadi mungkin orang menemukan mereka menggelegar ketika membaca kode.
sumber
Selain keterbacaan, mereka sebenarnya melakukan hal yang berbeda:
+=
tidak perlu mengevaluasi operan kirinya dua kali.Misalnya,
expr = expr + 5
akan evalauteexpr
dua kali (dengan asumsiexpr
tidak murni).sumber
expr = expr + 5
danexpr += 5
expr
memiliki efek samping.expr
memiliki efek samping, makaexpr = expr + 5
harus menjalankan efek samping tersebut dua kali.volatile
efek samping, danx=x+5
danx+=5
memiliki efek samping yang sama ketikax
adalahvolatile
Selain manfaat jelas yang dijelaskan orang lain dengan sangat baik, ketika Anda memiliki nama yang sangat panjang itu lebih kompak.
atau
sumber
Ini singkat.
Ini jauh lebih pendek untuk diketik. Ini melibatkan lebih sedikit operator. Ini memiliki area permukaan lebih sedikit dan lebih sedikit kesempatan untuk kebingungan.
Ini menggunakan operator yang lebih spesifik.
Ini adalah contoh yang dibuat-buat, dan saya tidak yakin apakah kompiler yang sebenarnya mengimplementasikan ini. x + = y sebenarnya menggunakan satu argumen dan satu operator dan memodifikasi x pada tempatnya. x = x + y dapat memiliki representasi menengah x = z di mana z adalah x + y. Yang terakhir menggunakan dua operator, penjumlahan dan penugasan, dan variabel sementara. Operator tunggal membuatnya sangat jelas bahwa sisi nilai tidak bisa apa pun selain y dan tidak perlu ditafsirkan. Dan secara teoritis bisa ada beberapa CPU mewah yang memiliki operator plus-sama yang berjalan lebih cepat daripada operator plus dan operator penugasan secara seri.
sumber
ADD
petunjuk pada banyak CPU memiliki varian yang beroperasi langsung di register atau memori menggunakan register lainnya, memori atau konstanta sebagai ditambakan kedua. Tidak semua kombinasi tersedia (mis. Tambahkan memori ke memori), tetapi ada cukup banyak untuk berguna. Bagaimanapun, setiap kompiler dengan pengoptimal yang baik akan tahu untuk menghasilkan kode yang samax = x + y
seperti untukx += y
.Itu adalah ungkapan yang bagus. Apakah lebih cepat atau tidak tergantung pada bahasa. Dalam C, itu lebih cepat karena itu diterjemahkan ke instruksi untuk meningkatkan variabel di sisi kanan. Bahasa modern, termasuk Python, Ruby, C, C ++ dan Java semuanya mendukung op = sintaks. Ini kompak, dan Anda terbiasa dengan cepat. Karena Anda akan melihatnya secara keseluruhan dalam kode orang lain (OPC), Anda mungkin juga akan terbiasa dan menggunakannya. Inilah yang terjadi dalam beberapa bahasa lain.
Dalam Python, pengetikan
x += 5
masih menyebabkan pembuatan objek integer 1 (meskipun dapat diambil dari kumpulan) dan yatim piatu dari objek integer yang berisi 5.Di Jawa, itu menyebabkan tacit cast terjadi. Coba ketikkan
sumber
x+=5
memiliki makna sesedikitx=x+5
; misalnya di Haskell, yang terakhir tidak menyebabkanx
penambahan sebesar 5 - malah menyebabkan loop rekursi tak terbatas. Siapa yang mau tulisan singkat untuk itu?+=
tidak terlalu tergantung pada bahasa seperti tergantung pada prosesor . Misalnya, X86 adalah arsitektur dua alamat, hanya mendukung+=
secara asli. Pernyataan sepertia = b + c;
harus dikompilasia = b; a += c;
karena tidak ada instruksi yang dapat menempatkan hasil penambahan di tempat lain selain di tempat salah satu puncak. Arsitektur Power, sebaliknya, adalah arsitektur tiga alamat yang tidak memiliki perintah khusus untuk+=
. Pada arsitektur ini, pernyataana += b;
dana = a + b;
selalu dikompilasi dengan kode yang sama.Operator seperti
+=
sangat berguna ketika Anda menggunakan variabel sebagai akumulator , yaitu total berjalan:Jauh lebih mudah dibaca daripada:
Dalam kasus pertama, secara konseptual, Anda mengubah nilai dalam
x
. Dalam kasus kedua, Anda menghitung nilai baru dan menetapkannyax
setiap waktu. Dan walaupun Anda mungkin tidak akan pernah menulis kode yang sesederhana itu, idenya tetap sama ... fokusnya adalah pada apa yang Anda lakukan pada nilai yang ada alih-alih menciptakan beberapa nilai baru.sumber
Pertimbangkan ini
D0 Anda benar-benar ingin menulis
sumber
Jawaban yang lain menargetkan kasus yang lebih umum, tetapi ada alasan lain: Dalam beberapa bahasa pemrograman, ini bisa kelebihan beban; misalnya Scala .
Pelajaran Scala kecil:
Jika suatu kelas hanya mendefinisikan
+
operator,x+=y
memang merupakan jalan pintas darix=x+y
.+=
Namun, jika kelas overload , mereka tidak:Selain itu, operator
+
dan+=
dua operator terpisah (dan bukan hanya ini: + a, ++ a, a ++, a + ba + = b juga merupakan operator yang berbeda); dalam bahasa di mana operator kelebihan muatan tersedia, ini dapat menciptakan situasi yang menarik. Sama seperti yang dijelaskan di atas - jika Anda akan membebani+
operator untuk melakukan penambahan, ingatlah bahwa+=
itu juga harus kelebihan beban.sumber
+=
dan kemudian menentukana+b
sebagai "membuat salinana
, menempatkanb
di atasnya menggunakan+=
, mengembalikan salinana
".Katakan sekali dan hanya sekali: di
x = x + 1
, saya katakan 'x' dua kali.Tetapi jangan pernah menulis, 'a = b + = 1' atau kita harus membunuh 10 anak kucing, 27 tikus, seekor anjing dan seekor hamster.
Anda seharusnya tidak pernah mengubah nilai suatu variabel, karena membuatnya lebih mudah untuk membuktikan kode itu benar - lihat pemrograman fungsional. Namun jika Anda melakukannya, maka lebih baik tidak mengatakan hal-hal hanya sekali.
sumber