Saya selalu tahu itu goto
adalah sesuatu yang buruk, terkunci di ruang bawah tanah di suatu tempat yang tidak pernah terlihat untuk selamanya tetapi saya mengalami contoh kode hari ini yang masuk akal untuk digunakan goto
.
Saya memiliki IP di mana saya perlu memeriksa apakah ada dalam daftar IP dan kemudian melanjutkan dengan kode, jika tidak, melemparkan pengecualian.
<?php
$ip = '192.168.1.5';
$ips = [
'192.168.1.3',
'192.168.1.4',
'192.168.1.5',
];
foreach ($ips as $i) {
if ($ip === $i) {
goto allowed;
}
}
throw new Exception('Not allowed');
allowed:
...
Jika saya tidak menggunakan goto
maka saya harus menggunakan beberapa variabel seperti
$allowed = false;
foreach ($ips as $i) {
if ($ip === $i) {
$allowed = true;
break;
}
}
if (!$allowed) {
throw new Exception('Not allowed');
}
Pertanyaan saya adalah apa yang sangat buruk dengan goto
ketika digunakan untuk kasus-kasus yang jelas dan relevan seperti itu?
Jawaban:
GOTO sendiri bukan masalah langsung, itu adalah mesin negara implisit yang cenderung diterapkan oleh orang-orang dengan itu. Dalam kasus Anda, Anda ingin kode yang memeriksa apakah alamat IP ada dalam daftar alamat yang dibolehkan
jadi kode Anda ingin memeriksa suatu kondisi. Algoritme untuk mengimplementasikan pemeriksaan ini seharusnya tidak menjadi masalah di sini, dalam ruang mental program utama Anda cek tersebut bersifat atomik. Begitulah seharusnya.
Tetapi jika Anda memasukkan kode yang melakukan pemeriksaan ke program utama Anda, Anda kehilangan itu. Anda memperkenalkan keadaan yang bisa berubah, baik secara eksplisit:
di mana
$list_contains_ip
satu-satunya variabel status Anda, atau secara implisit:Seperti yang Anda lihat, ada variabel status yang tidak dideklarasikan dalam konstruk GOTO. Itu bukan masalah semata, tetapi variabel-variabel keadaan ini seperti kerikil: membawa satu tidak sulit, membawa tas yang penuh dengannya akan membuat Anda berkeringat. Kode Anda tidak akan tetap sama: bulan depan Anda akan diminta untuk membedakan antara alamat pribadi dan publik. Bulan setelah itu, kode Anda harus mendukung rentang IP. Tahun depan, seseorang akan meminta Anda untuk mendukung alamat IPv6. Dalam waktu singkat, kode Anda akan terlihat seperti ini:
Dan siapa pun yang harus men-debug kode itu akan mengutuk Anda dan anak-anak Anda.
Dijkstra mengatakannya seperti ini:
Dan itu sebabnya GOTO dianggap berbahaya.
sumber
$list_contains_ip = false;
pernyataan tampaknya salah tempatAda beberapa kasus penggunaan yang sah untuk
GOTO
. Misalnya untuk penanganan kesalahan dan pembersihan di C atau untuk menerapkan beberapa bentuk mesin negara. Tetapi ini bukan salah satu dari kasus-kasus ini. Contoh kedua adalah IMHO yang lebih mudah dibaca, tetapi bahkan lebih mudah dibaca adalah mengekstrak loop ke fungsi yang terpisah dan kemudian kembali ketika Anda menemukan kecocokan. Bahkan lebih baik (dalam pseudocode, saya tidak tahu persis sintaks):Jadi apa yang buruk tentang
GOTO
itu? Pemrograman terstruktur menggunakan fungsi dan struktur kontrol untuk mengatur kode sehingga struktur sintaksis mencerminkan struktur logis. Jika sesuatu hanya dieksekusi secara kondisional, itu akan muncul di blok pernyataan bersyarat. Jika sesuatu dieksekusi dalam satu lingkaran, itu akan muncul dalam satu blok lingkaran.GOTO
memungkinkan Anda untuk menghindari struktur sintaksis dengan melompat-lompat dengan sewenang-wenang, sehingga membuat kode lebih sulit untuk diikuti.Tentu saja jika Anda tidak memiliki pilihan lain yang Anda gunakan
GOTO
, tetapi jika efek yang sama dapat dicapai dengan fungsi dan struktur kontrol, itu lebih disukai.sumber
it's easier to write good optimizing compilers for "structured" languages.
Peduli untuk menguraikan? Sebagai contoh balasan, orang-orang Rust memperkenalkan MIR , representasi perantara dalam kompiler yang secara khusus menggantikan loop, terus, istirahat, dan semacamnya dengan gotos, karena lebih mudah untuk memeriksa dan mengoptimalkan.goto
dengan sarang dari flag dan kondisional (seperti pada contoh kedua OP) jauh lebih mudah dibaca daripada juts menggunakan agoto
.Seperti yang orang lain katakan, masalahnya bukan pada
goto
dirinya sendiri; masalahnya adalah bagaimana orang menggunakannyagoto
, dan bagaimana kode itu membuat lebih sulit untuk dipahami dan dipelihara.Asumsikan cuplikan kode berikut:
Untuk nilai apa dicetak
i
? Kapan itu dicetak? Sampai Anda memperhitungkan setiap contohgoto label
dalam fungsi Anda, Anda tidak bisa tahu. Kehadiran sederhana label itu menghancurkan kemampuan Anda untuk men-debug kode dengan inspeksi sederhana. Untuk fungsi kecil dengan satu atau dua cabang, tidak banyak masalah. Untuk fungsi yang tidak kecil ...Jauh di awal tahun 90-an kami diberi setumpuk kode C yang menggerakkan tampilan grafis 3d dan disuruh membuatnya berjalan lebih cepat. Itu hanya sekitar 5.000 baris kode, tetapi semuanya ada di
main
, dan penulis menggunakan sekitar 15 atau lebihgoto
bercabang di kedua arah. Ini adalah kode yang buruk untuk memulai, tetapi kehadiran merekagoto
membuatnya jauh lebih buruk. Butuh rekan kerja saya sekitar 2 minggu untuk memecahkan aliran kontrol. Lebih baik lagi,goto
itu menghasilkan kode yang begitu erat dengan dirinya sendiri sehingga kami tidak dapat membuat perubahan tanpa merusak sesuatu.Kami mencoba mengkompilasi dengan optimasi level 1, dan kompiler memakan semua RAM yang tersedia, lalu semua swap yang tersedia, dan kemudian panik sistem (yang mungkin tidak ada hubungannya dengan
goto
s sendiri, tapi saya suka membuang anekdot di luar sana).Pada akhirnya, kami memberi pelanggan dua opsi - mari kita menulis ulang semuanya dari awal, atau membeli perangkat keras yang lebih cepat.
Mereka membeli perangkat keras yang lebih cepat.
Aturan Bode untuk menggunakan
goto
:if
ataufor
atauwhile
pernyataan);goto
menggantikan struktur kontrolAda kasus di mana a
goto
adalah jawaban yang tepat, tetapi jarang terjadi (putus dari lingkaran yang bersarang adalah satu-satunya tempat saya menggunakannya).EDIT
Memperluas pernyataan terakhir itu, inilah salah satu dari beberapa kasus penggunaan yang valid untuk
goto
. Asumsikan kita memiliki fungsi berikut:Sekarang, kami memiliki masalah - bagaimana jika salah satu
malloc
panggilan gagal di tengah jalan? Tidak seperti peristiwa yang mungkin terjadi, kami tidak ingin mengembalikan array yang dialokasikan sebagian, kami juga tidak ingin keluar dari fungsi dengan kesalahan; kami ingin membersihkan setelah diri kita sendiri dan menghapus semua memori yang dialokasikan sebagian. Dalam bahasa yang melempar pengecualian pada alokasi yang buruk, itu cukup mudah - Anda hanya menulis penangan pengecualian untuk membebaskan apa yang sudah dialokasikan.Di C, Anda tidak memiliki penanganan pengecualian terstruktur; Anda harus memeriksa nilai balik dari setiap
malloc
panggilan dan mengambil tindakan yang sesuai.Bisakah kita melakukan ini tanpa menggunakan
goto
? Tentu saja kita bisa - itu hanya memerlukan sedikit pembukuan tambahan (dan, dalam praktiknya, itulah jalan yang akan saya ambil). Tetapi, jika Anda mencari tempat di mana menggunakan tandagoto
tidak langsung merupakan praktik atau desain yang buruk, ini adalah salah satu dari sedikit.sumber
goto
bahkan sesuai dengan aturan-aturan ini — saya tidak akan menggunakannya sama sekali kecuali itu satu-satunya bentuk percabangan yang tersedia dalam bahasa tersebut — tetapi saya dapat melihat bahwa tidak akan terlalu banyak mimpi buruk untuk di-debuggoto
Jika ditulis sesuai dengan aturan ini.GOTO
, termasuk Fortran's (dalam) goto yang ditugaskan dan aritmatika yang terkenal, jika saya ingat dengan benar.return
,break
,continue
Danthrow
/catch
semua pada dasarnya gotos - mereka semua mentransfer kontrol untuk sepotong kode dan semua bisa dilaksanakan dengan gotos - sebenarnya saya melakukannya sekali dalam proyek sekolah, seorang instruktur PASCAL mengatakan berapa banyak lebih baik Pascal adalah dari dasar karena struktur ... jadi saya harus menjadi berlawanan ...Hal terpenting tentang Rekayasa Perangkat Lunak (Saya akan menggunakan istilah ini di atas Pengodean untuk merujuk pada situasi di mana Anda dibayar oleh seseorang untuk membuat basis kode bersama-sama dengan insinyur lain yang memerlukan perbaikan dan pemeliharaan berkelanjutan) membuat kode dapat dibaca- -mendapatkannya untuk melakukan sesuatu hampir sekunder. Kode Anda akan ditulis hanya sekali tetapi, dalam banyak kasus, orang akan menghabiskan berhari-hari dan minggu untuk meninjau kembali / mempelajari kembali, memperbaiki dan memperbaikinya - dan setiap kali mereka (atau Anda) harus mulai dari awal dan mencoba mengingat / mencari tahu kode Anda.
Sebagian besar fitur yang telah ditambahkan ke bahasa selama bertahun-tahun adalah untuk membuat perangkat lunak lebih mudah dikelola, tidak mudah untuk ditulis (meskipun beberapa bahasa mengarah ke sana - mereka sering menyebabkan masalah jangka panjang ...).
Dibandingkan dengan pernyataan kontrol aliran yang serupa, GOTO dapat dengan mudah mengikuti yang terbaik (Goto tunggal yang digunakan dalam kasus seperti yang Anda sarankan), dan mimpi buruk ketika disalahgunakan - dan sangat mudah disalahgunakan ...
Jadi setelah berurusan dengan mimpi buruk spaghetti selama beberapa tahun, kami hanya berkata "Tidak", sebagai komunitas kami tidak akan menerima ini - terlalu banyak orang mengacaukannya jika diberi sedikit waktu luang - itu benar-benar satu-satunya masalah dengan mereka. Anda bisa menggunakannya ... tetapi bahkan jika itu adalah kasus yang sempurna, orang berikutnya akan menganggap Anda seorang programmer yang mengerikan karena Anda tidak mengerti sejarah komunitas.
Banyak struktur lain telah dikembangkan hanya untuk membuat kode Anda lebih mudah dipahami: Fungsi, Objek, Pelingkupan, Enkapsulasi, Komentar (!) ... serta pola / proses yang lebih penting seperti "KERING" (mencegah duplikasi) dan "YAGNI" (Mengurangi generalisasi berlebihan / komplikasi kode) - semua benar-benar hanya mengimpor agar orang BERIKUTNYA untuk membaca kode Anda (Siapa yang mungkin akan menjadi Anda - setelah Anda lupa sebagian besar dari apa yang Anda lakukan di tempat pertama!)
sumber
return
break
continue
throw
catch
GOTO
adalah alat. Itu bisa digunakan untuk kebaikan atau kejahatan.Di masa lalu yang buruk, dengan FORTRAN dan BASIC, itu adalah satu - satunya alat.
Ketika melihat kode dari hari-hari itu, ketika Anda melihat
GOTO
Anda harus mencari tahu mengapa itu ada. Ini bisa menjadi bagian dari idiom standar yang dapat Anda pahami dengan cepat ... atau bisa menjadi bagian dari struktur kontrol mimpi buruk yang seharusnya tidak pernah terjadi. Anda tidak tahu sampai Anda melihat, dan mudah untuk keliru.Orang menginginkan sesuatu yang lebih baik, dan struktur kontrol yang lebih maju diciptakan. Ini mencakup sebagian besar kasus penggunaan, dan orang-orang yang dibakar oleh orang jahat
GOTO
ingin sepenuhnya melarang mereka.Ironisnya,
GOTO
tidak begitu buruk ketika jarang. Ketika Anda melihatnya, Anda tahu ada sesuatu yang istimewa terjadi, dan mudah untuk menemukan label yang sesuai karena itu adalah satu-satunya label di dekatnya.Maju cepat ke hari ini. Anda adalah dosen yang mengajar pemrograman. Anda bisa mengatakan "Dalam kebanyakan kasus, Anda harus menggunakan konstruksi baru lanjutan, tetapi dalam beberapa kasus sederhana
GOTO
dapat lebih mudah dibaca." Siswa tidak akan mengerti itu. Mereka akan menyalahgunakanGOTO
untuk membuat kode yang tidak dapat dibaca.Sebaliknya Anda mengatakan "
GOTO
buruk.GOTO
Jahat.GOTO
Gagal ujian." Siswa akan mengerti itu !sumber
Dengan pengecualian
goto
, semua konstruk aliran dalam PHP (dan sebagian besar bahasa) dicakup secara hierarkis.Bayangkan beberapa kode diperiksa melalui mata menyipit:
Terlepas dari apa kontrol membangun
foo
adalah (if
,while
, dll), hanya ada perintah diperbolehkan tertentu untuka
,b
, danc
.Anda dapat memiliki
a
-b
-c
, ataua
-c
, atau bahkana
-b
-b
-b
-c
. Tapi Anda bisa tidak pernah memilikib
-c
ataua
-b
-a
-c
.... kecuali kamu punya
goto
.goto
(khususnya mundurgoto
) dapat cukup merepotkan sehingga yang terbaik adalah membiarkannya saja, dan menggunakan konstruksi aliran yang hierarkis dan tersumbat.goto
Ada tempat, tetapi sebagian besar sebagai optimasi mikro dalam bahasa tingkat rendah. IMO, tidak ada tempat yang baik untuk itu di PHP.FYI, kode contoh dapat ditulis lebih baik daripada salah satu saran Anda.
sumber
Dalam bahasa tingkat rendah, GOTO tidak bisa dihindari. Tetapi pada tingkat tinggi harus dihindari (dalam hal bahasa mendukungnya) karena membuat program lebih sulit dibaca .
Semuanya bermuara untuk membuat kode lebih sulit dibaca. Bahasa tingkat tinggi ditinggikan untuk membuat kode lebih mudah dibaca daripada bahasa tingkat rendah seperti, katakanlah, assembler atau C.
GOTO tidak menyebabkan pemanasan global atau menyebabkan kemiskinan di dunia ketiga. Itu hanya membuat kode lebih sulit dibaca.
Sebagian besar bahasa modern memiliki struktur kontrol yang membuat GOTO tidak perlu. Beberapa orang seperti Java bahkan tidak memilikinya.
Bahkan, istilah kode spaguetti berasal dari kode yang berbelit-belit, sulit diikuti karena struktur percabangan yang tidak terstruktur.
sumber
goto
dapat membuat kode lebih mudah dibaca. Ketika Anda membuat variabel tambahan dan cabang bersarang versus satu lompatan kode jauh lebih sulit untuk dibaca dan dipahami.goto
muncul bukan contoh kode spaghetti yang berbelit-belit, tetapi hal-hal yang cukup mudah, sering meniru pola aliran kontrol dalam bahasa yang tidak dapat memiliki sintaks untuk itu: dua contoh paling umum adalah untuk keluar dari banyak loop dan contoh di OP di managoto
digunakan untuk mengimplementasikanfor
-else
.goto
juga bagus untuk hal-hal seperti coba lagi pengecualian, kembalikan, mesin negara.Tidak ada yang salah dengan
goto
pernyataan itu sendiri. Kesalahannya adalah dengan beberapa orang yang secara tidak tepat menggunakan pernyataan itu.Selain apa yang dikatakan JacquesB (penanganan kesalahan dalam C), Anda menggunakan
goto
untuk keluar dari loop non-bersarang, sesuatu yang dapat Anda lakukan dengan menggunakanbreak
. Dalam hal ini sebaiknya Anda gunakanbreak
.Tetapi jika Anda memiliki skenario loop bersarang, maka menggunakan
goto
akan lebih elegan / sederhana. Sebagai contoh:Contoh di atas tidak masuk akal dalam masalah spesifik Anda, karena Anda tidak perlu loop bersarang. Tapi saya harap Anda hanya melihat bagian loop bersarang dari itu.
Bonous point: jika daftar IP Anda kecil, metode Anda baik-baik saja. Tetapi jika daftar bertambah, ketahuilah bahwa pendekatan Anda memiliki kompleksitas run-time terburuk O (n) asimptotik . Ketika daftar Anda bertambah, Anda mungkin ingin menggunakan metode berbeda yang mencapai O (log n) (seperti struktur pohon) atau O (1) (tabel hash tanpa tabrakan).
sumber
break
dancontinue
dengan angka (untuk memecah / melanjutkan banyak lapisan loop) atau label (untuk memecah / melanjutkan loop dengan label itu), salah satu dari yang umumnya harus lebih baik menggunakangoto
untuk loop bersarang.Benar. Tidak peduli
Benar. Tidak peduli
Benar. Tidak peduli
Benar. Namun, saya telah menjadi programmer yang disiplin. Saya telah melihat apa yang terjadi pada kode dari waktu ke waktu.
Goto
dimulai dengan baik. Kemudian keinginan untuk menggunakan kembali kode masuk. Cukup segera saya menemukan diri saya di breakpoint tidak memiliki petunjuk apa pun yang terjadi bahkan setelah melihat keadaan program.Goto
membuatnya sulit untuk alasan tentang kode. Kami telah bekerja menciptakan benar-benar keraswhile
,do while
,for
,for each
switch
,subroutines
,functions
, dan banyak lagi semua karena melakukan hal-hal ini denganif
dangoto
sulit pada otak.Jadi tidak. Kami tidak ingin melihatnya
goto
. Tentu itu hidup dan baik dalam biner tetapi kita tidak perlu melihatnya dalam sumber. Bahkan,if
mulai terlihat sedikit goyah.sumber
Goto
memungkinkan Anda mengabstraksi masalah dengan membuatnya sedikit masalah kode lainnya.If
memungkinkan Anda memilih abstraksi itu. Ada cara yang lebih baik untuk abstrak dan cara yang lebih baik untuk memilih abstraksi. Polimorfisme untuk satu.Bahasa assembly biasanya hanya memiliki lompatan bersyarat / tanpa syarat (setara dengan GOTO. Implementasi FORTRAN dan BASIC yang lebih lama tidak memiliki pernyataan blok kontrol di luar iterasi yang dihitung (loop DO), meninggalkan semua aliran kontrol lainnya ke IFs dan GOTO. DO loop di bahasa-bahasa ini diakhiri dengan pernyataan berlabel numerik. Akibatnya, kode yang ditulis untuk bahasa-bahasa ini bisa, dan sering kali, sulit untuk diikuti dan rawan kesalahan.
Untuk menggarisbawahi intinya, ada pernyataan " DATANG DARI ".
Praktis tidak perlu menggunakan GOTO dalam bahasa seperti C, C ++, C #, PASCAL, Java, dll .; konstruksi alternatif dapat digunakan yang hampir pasti akan sama efisien dan jauh lebih mudah dirawat. Memang benar bahwa satu GOTO dalam file sumber tidak akan menjadi masalah. Masalahnya adalah bahwa tidak perlu banyak untuk membuat unit kode sulit untuk diikuti dan rawan kesalahan pemeliharaan. Itu sebabnya kebijaksanaan yang diterima adalah untuk menghindari GOTO jika memungkinkan.
Ini artikel wikipedia pada pernyataan goto mungkin bisa membantu
sumber