Apakah `break` dan` continue` praktik pemrograman yang buruk?

191

Bos saya terus menyebutkan dengan acuh tak acuh bahwa programmer buruk menggunakan breakdan continuedalam loop.

Saya menggunakannya sepanjang waktu karena mereka masuk akal; izinkan saya menunjukkan inspirasi kepada Anda:

function verify(object) {
    if (object->value < 0) return false;
    if (object->value > object->max_value) return false;
    if (object->name == "") return false;
    ...
}

Intinya di sini adalah bahwa pertama fungsi memeriksa bahwa kondisinya benar, kemudian menjalankan fungsionalitas yang sebenarnya. IMO yang sama berlaku dengan loop:

while (primary_condition) {
    if (loop_count > 1000) break;
    if (time_exect > 3600) break;
    if (this->data == "undefined") continue;
    if (this->skip == true) continue;
    ...
}

Saya pikir ini membuatnya lebih mudah untuk dibaca & debug; tapi saya juga tidak melihat sisi negatifnya.

Mikhail
sumber
2
Tidak butuh banyak untuk melupakan yang mana melakukan apa.
57
Tidak. Tidak juga. Mengetahui kapan menggunakannya adalah kuncinya. Mereka adalah alat di kotak alat. Anda menggunakannya ketika mereka memberikan kode yang jelas & ringkas.
orj
67
Saya tidak dapat menyuarakan dukungan saya untuk gaya pengkodean ini cukup kuat. Beberapa tingkat kondisi bersarang jauh lebih buruk daripada pendekatan ini. Saya biasanya tidak militan tentang gaya pengkodean, tetapi ini hampir merupakan pemecahan bagi saya.
Emil H
9
Jelas atasan Anda tidak menulis kode (cukup). Jika dia melakukannya, dia akan tahu bahwa semua kata kunci (ya, genap goto) berguna dalam beberapa kasus.
sakisk
57
Pemrogram yang buruk menggunakan break dan continue tidak berarti pemrogram yang baik tidak. Pemrogram yang buruk menggunakan jika dan sementara juga.
mouviciel

Jawaban:

240

Ketika digunakan pada awal blok, seperti cek pertama kali dibuat, mereka bertindak seperti prasyarat, jadi itu bagus.

Ketika digunakan di tengah blok, dengan beberapa kode di sekitarnya, mereka bertindak seperti perangkap tersembunyi, jadi itu buruk.

Klaim
sumber
6
@Klaim: Dapat dikatakan bahwa rutin apa pun yang memiliki beberapa titik keluar adalah rutin dengan faktor buruk. Rutin yang diperhitungkan dengan benar harus melakukan satu hal dan satu hal saja.
bit-twiddler
79
@ bit-twiddler: ini adalah pola pikir C-ish yang sangat. Variabel sementara dapat dimodifikasi nanti, jadi satu kesalahan ketik 20 baris dari sini dapat menghapus hasil yang dibuat dengan hati-hati. Namun, pengembalian yang cepat (atau rusak, atau berlanjut) sangat jelas: Saya bisa berhenti membaca sekarang karena saya tahu itu tidak mungkin dimodifikasi lebih jauh ke bawah . Ini bagus untuk otak kecil saya, benar-benar membuat menelusuri kode dengan lebih mudah.
Matthieu M.
15
@Matthieu saya setuju. Keluar dari blok ketika Anda menerima hasil yang memenuhi tujuan blok itu.
Evan Plaice
8
@ bit-twiddler - prinsip titik keluar tunggal terpisah dari prinsip tanggung jawab tunggal. Saya tidak setuju bahwa pengecekan selalu terpisah dari tindakan WRT, tanggung jawab tunggal. Sebenarnya "tanggung jawab tunggal" selalu menganggap saya sebagai istilah subyektif. Misalnya, dalam pemecah kuadrat, haruskah menghitung diskriminan menjadi tanggung jawab terpisah? Atau bisakah seluruh rumus kuadratik menjadi tanggung jawab tunggal? Saya berpendapat bahwa itu tergantung pada apakah Anda memiliki penggunaan terpisah untuk diskriminan - jika tidak, memperlakukannya sebagai tanggung jawab terpisah mungkin berlebihan.
Steve314
10
Jawaban itu adalah aturan praktis, bukan aturan keras. Ini berfungsi dalam banyak kasus, jangan ragu untuk memecahkannya jika itu masuk akal dalam konteks Anda.
Klaim
87

Anda dapat membaca makalah Donald Knuth's 1974 Program Structured dengan pergi ke Pernyataan , di mana ia membahas berbagai penggunaan go toyang secara struktural diinginkan. Mereka termasuk yang setara breakdan continuepernyataan (banyak penggunaan go todi sana telah dikembangkan menjadi konstruksi yang lebih terbatas). Apakah bos Anda tipe yang menyebut Knuth seorang programmer yang buruk?

(Contoh-contoh yang diberikan menarik minat saya. Biasanya, breakdan continuetidak disukai oleh orang-orang yang menyukai satu entri dan satu keluar dari setiap kode, dan orang semacam itu juga mengerutkan dahi pada banyak returnpernyataan.)

David Thornley
sumber
8
Sebagian besar orang yang menyukai fungsi dan prosedur memiliki titik masuk dan keluar tunggal tumbuh di Pascal. Pascal bukanlah bahasa pertama yang saya pelajari, tetapi memiliki dampak mendalam pada bagaimana saya menyusun kode sampai hari ini. Orang-orang selalu mengomentari betapa mudahnya membaca kode Java saya. Itu karena saya menghindari beberapa titik keluar serta mencampur deklarasi dengan kode. Saya mencoba yang terbaik untuk mendeklarasikan setiap variabel lokal yang digunakan dalam suatu metode di bagian atas metode. Praktek ini menghindari kode yang mengoceh dengan memaksa saya untuk menjaga metode singkat.
bit-twiddler
5
Pascal juga memiliki fungsi bersarang. Katakan saja ...
Shog9
6
Dari apa yang saya ingat, kembali pada hari itu, alasan utama bahwa orang tidak suka beberapa pernyataan pengembalian fungsi adalah karena pengadu tidak menanganinya dengan benar. Sungguh menyakitkan untuk menetapkan breakpoint di akhir fungsi tetapi Anda tidak pernah memukulnya karena pernyataan pengembalian sebelumnya. Untuk setiap kompiler yang saya gunakan saat ini tidak lagi menjadi masalah.
Dunk
28
@ bit-twiddler: Saya tidak begitu yakin tentang itu. Saya masih menggunakan Pascal hari ini, dan saya biasanya menganggap "single entry single exit," atau setidaknya bagian single-exit, sebagai pemrograman pemujaan kargo. Saya hanya mempertimbangkan Break, Continuedan Exitsebagai alat di kotak alat saya; Saya menggunakannya di mana itu membuat kode lebih mudah diikuti, dan jangan menggunakannya di mana hal itu akan membuat lebih sulit untuk dibaca.
Mason Wheeler
2
@ bit-twiddler: amin untuk itu. Saya juga akan menambahkan bahwa begitu Anda ke blok yang sesuai dengan mudah di layar, beberapa titik keluar menjadi jauh lebih sulit.
Shog9
44

Saya tidak percaya mereka jahat. Gagasan bahwa mereka buruk berasal dari masa pemrograman terstruktur. Ini terkait dengan gagasan bahwa suatu fungsi harus memiliki satu titik masuk dan satu titik keluar, yaitu hanya satu returnper fungsi.

Ini masuk akal jika fungsi Anda panjang, dan jika Anda memiliki beberapa loop bersarang. Namun, fungsi Anda harus pendek, dan Anda harus membungkus loop dan tubuhnya menjadi fungsi pendek sendiri. Secara umum, memaksa suatu fungsi untuk memiliki satu titik keluar dapat menghasilkan logika yang sangat berbelit-belit.

Jika fungsi Anda sangat pendek, jika Anda memiliki satu loop, atau paling buruk dua loop bersarang, dan jika tubuh loop sangat pendek, maka sangat jelas apa yang dilakukan oleh a breakatau a continue. Juga jelas apa yang dilakukan banyak returnpernyataan.

Masalah-masalah ini dibahas dalam "Kode Bersih" oleh Robert C. Martin dan "Refactoring" oleh Martin Fowler.

Dima
sumber
12
"Jadikan fungsi Anda kecil. Kemudian buat lebih kecil" -Robert C. Martin. Saya menemukan bahwa ini bekerja dengan sangat baik. Setiap kali Anda melihat blok kode dalam suatu fungsi yang membutuhkan komentar yang menjelaskan apa yang dilakukannya, bungkus menjadi fungsi yang terpisah dengan nama deskriptif. Bahkan jika itu hanya beberapa baris, dan bahkan jika itu hanya digunakan sekali. Praktik ini menghilangkan sebagian besar masalah dengan break / continue atau multiple return.
Dima
2
@Mikhail: Kompleksitas siklus umumnya sangat berkorelasi dengan SLOC, yang berarti bahwa saran tersebut dapat disederhanakan menjadi "jangan menulis fungsi yang panjang".
John R. Strohm
3
Ide titik keluar tunggal banyak disalahtafsirkan. Sekali waktu, fungsi tidak harus mengembalikan penelepon mereka. Mereka bisa kembali ke tempat lain. Ini biasa dilakukan dalam bahasa assembly. Fortran memiliki konstruksi khusus untuk ini; Anda dapat memasukkan nomor pernyataan yang didahului dengan ampersand CALL P(X, Y, &10), dan jika terjadi kesalahan, fungsi tersebut dapat memberikan kontrol ke pernyataan itu, alih-alih kembali ke titik panggilan.
kevin cline
@kevincline seperti yang terlihat dengan Lua, misalnya.
Qix
1
@ cjsimon Anda mendapatkannya. Tidak hanya fungsi yang harus kecil. Kelas juga harus kecil.
Dima
39

Programmer yang buruk berbicara secara absolut (seperti Sith). Pemrogram yang baik menggunakan solusi sejelas mungkin ( semua hal lain dianggap sama ).

Menggunakan break dan melanjutkan sering membuat kode sulit untuk diikuti. Tetapi jika menggantinya membuat kode lebih sulit untuk diikuti, maka itu adalah perubahan yang buruk.

Contoh yang Anda berikan jelas merupakan situasi di mana istirahat dan terus harus diganti dengan sesuatu yang lebih elegan.

Matthew Read
sumber
Ya, saya melebih-lebihkan sejumlah persyaratan untuk memberikan contoh kasus untuk keluar.
Mikhail
9
Apa contoh kode pengganti yang Anda sarankan? Saya pikir itu adalah contoh pernyataan penjaga yang agak masuk akal.
simgineer
Tampak bagi saya bahwa "solusi yang paling jelas mungkin" selalu ... mungkin? Bagaimana mungkin tidak ada solusi yang "paling jelas"? Tapi kemudian, saya bukan orang yang absolut, jadi mungkin Anda benar.
@nocomprende Saya tidak yakin apa yang Anda maksud. "Kemungkinan" di sini tidak menunjukkan bahwa solusi terbaik tidak ada - hanya yang sempurna, kejelasan tertinggi bukanlah apa-apa. Bagaimanapun, ini subyektif.
Matius Baca
22

Kebanyakan orang berpikir itu ide yang buruk karena perilakunya tidak mudah diprediksi. Jika Anda membaca kode dan Anda while(x < 1000){}menganggap Anda akan menjalankannya sampai x> = 1000 ... Tetapi jika ada jeda di tengah, maka itu tidak berlaku, sehingga Anda tidak dapat benar-benar mempercayai Anda perulangan ...

Itu alasan yang sama orang tidak suka GOTO: tentu saja, itu dapat digunakan dengan baik, tetapi juga dapat menyebabkan kode spaghetti yang luar biasa, di mana kode melompat secara acak dari bagian ke bagian.

Bagi saya sendiri, jika saya akan melakukan loop yang rusak pada lebih dari satu kondisi, saya akan lakukan while(x){}kemudian beralih X ke false ketika saya perlu untuk keluar. Hasil akhirnya akan sama, dan siapa pun yang membaca kode akan tahu untuk melihat lebih dekat pada hal-hal yang mengubah nilai X.

Satanicpuppy
sumber
2
+1 berkata dengan sangat baik, dan +1 (jika saya bisa melakukan yang lain) untuk while(notDone){ }pendekatan tersebut.
FrustratedWithFormsDesigner
5
Mikhail: Masalah dengan break adalah bahwa kondisi akhir untuk loop tidak pernah hanya dinyatakan di satu tempat. Itu membuatnya sulit untuk memprediksi kondisi pasca setelah loop. Dalam kasus sepele ini (> = 1000) tidak sulit. Tambahkan banyak jika-pernyataan dan berbagai tingkat bersarang itu bisa menjadi sangat, sangat sulit untuk menentukan kondisi pasca loop.
S.Lott
S.Lott memukul paku tepat di kepala. Ekspresi yang mengontrol iterasi harus mencakup setiap kondisi yang harus dipenuhi untuk melanjutkan iterasi.
bit-twiddler
6
Mengganti break;dengan x=false;tidak membuat kode Anda lebih jelas. Anda masih harus mencari isi pernyataan itu. Dan jika x=false;Anda harus memeriksa bahwa itu tidak menekan x=true;lebih jauh ke bawah.
Sjoerd
14
Ketika orang mengatakan "Saya melihat x dan saya berasumsi y, tetapi jika Anda z anggapan itu tidak berlaku" Saya cenderung berpikir "jadi jangan membuat asumsi bodoh itu". Banyak orang akan menyederhanakan itu menjadi "ketika saya melihat while (x < 1000)saya menganggap itu akan berjalan 1000 kali". Nah, ada banyak alasan mengapa itu salah, meskipun xawalnya nol. Misalnya, siapa bilang xbertambah tepat sekali selama loop, dan tidak pernah dimodifikasi dengan cara lain? Bahkan untuk asumsi Anda sendiri, hanya karena set sesuatu x >= 1000tidak berarti loop akan berakhir - itu dapat diatur kembali dalam kisaran sebelum kondisi diperiksa.
Steve314
14

Ya Anda dapat [kembali] menulis program tanpa pernyataan break (atau kembali dari tengah loop, yang melakukan hal yang sama). Tetapi Anda mungkin harus memperkenalkan variabel tambahan dan / atau duplikasi kode yang keduanya biasanya membuat program lebih sulit untuk dipahami. Pascal (bahasa pemrograman) sangat buruk terutama untuk programmer pemula karena alasan itu. Atasan Anda pada dasarnya ingin Anda memprogram dalam struktur kontrol Pascal. Jika Linus Torvalds ada di sepatu Anda, dia mungkin akan menunjukkan kepada bos Anda jari tengah!

Ada hasil ilmu komputer yang disebut hierarki struktur kontrol Kosaraju, yang berasal dari tahun 1973 dan yang disebutkan dalam makalah Knuth yang terkenal pada tahun 1974. (Makalah Knuth ini sudah direkomendasikan di atas oleh David Thornley, omong-omong .) Apa yang dibuktikan oleh S. Rao Kosaraju pada tahun 1973 adalah bahwa tidak mungkin untuk menulis ulang semua program yang memiliki multi level level kedalaman n ke dalam program-program dengan kedalaman break kurang dari n tanpa memperkenalkan variabel tambahan. Tetapi katakanlah itu hanya hasil teoretis semata. (Cukup tambahkan beberapa variabel tambahan ?! Tentunya Anda dapat melakukan itu untuk menyenangkan bos Anda ...)

Apa yang jauh lebih penting dari perspektif rekayasa perangkat lunak adalah makalah yang lebih baru, tahun 1995 oleh Eric S. Roberts berjudul Loop Exits and Structured Programming: Membuka Kembali Debat ( http://cs.stanford.edu/people/eroberts/papers/SIGCSE- 1995 / LoopExits.pdf ). Roberts merangkum beberapa studi empiris yang dilakukan oleh orang lain sebelum dia. Sebagai contoh, ketika sekelompok siswa tipe CS101 diminta untuk menulis kode untuk fungsi yang mengimplementasikan pencarian sekuensial dalam array, penulis penelitian mengatakan hal berikut tentang siswa yang menggunakan break / return / goto untuk keluar dari dari loop pencarian berurutan ketika elemen ditemukan:

Saya belum menemukan satu orang yang mencoba program menggunakan [gaya ini] yang menghasilkan solusi yang salah.

Roberts juga mengatakan bahwa:

Siswa yang berusaha memecahkan masalah tanpa menggunakan pengembalian eksplisit dari for loop bernasib kurang baik: hanya tujuh dari 42 siswa yang mencoba strategi ini berhasil menghasilkan solusi yang benar. Angka itu mewakili tingkat keberhasilan kurang dari 20%.

Ya, Anda mungkin lebih berpengalaman daripada siswa CS101, tetapi tanpa menggunakan pernyataan break (atau ekuivalen kembali / kebohongan dari tengah-tengah loop), pada akhirnya Anda akan menulis kode bahwa sementara yang terstruktur dengan baik cukup berbulu dalam hal logika tambahan variabel dan duplikasi kode yang seseorang, mungkin diri Anda sendiri, akan memasukkan bug logika di dalamnya ketika mencoba mengikuti gaya pengkodean bos Anda.

Saya juga akan mengatakan di sini bahwa makalah Roberts jauh lebih mudah diakses oleh programmer rata-rata, jadi bacaan pertama yang lebih baik daripada karya Knuth. Ini juga lebih pendek dan mencakup topik yang lebih sempit. Anda mungkin bahkan dapat merekomendasikannya kepada atasan Anda, bahkan jika dia adalah manajemen dan bukan tipe CS.

user3588161
sumber
2
Meskipun pemrograman terstruktur adalah pendekatan saya selama bertahun-tahun, dalam beberapa tahun terakhir telah beralih untuk sepenuhnya menggunakan pintu keluar eksplisit pada kesempatan pertama. Ini membuat eksekusi lebih cepat dan hampir menghilangkan kesalahan logika yang digunakan untuk menghasilkan loop tak berujung (menggantung).
DocSalvager
9

Saya tidak mempertimbangkan menggunakan salah satu dari praktik buruk ini, tetapi menggunakannya terlalu banyak di dalam loop yang sama harus menjamin memikirkan kembali logika yang digunakan dalam loop. Gunakan dengan hemat.

Bernard
sumber
:) ya, contoh yang saya berikan adalah konseptual
Mikhail
7

Contoh yang Anda berikan tidak perlu istirahat atau dilanjutkan:

while (primary-condition AND
       loop-count <= 1000 AND
       time-exec <= 3600) {
   when (data != "undefined" AND
           NOT skip)
      do-something-useful;
   }

'Masalah' saya dengan 4 baris dalam contoh Anda adalah bahwa mereka semua berada pada level yang sama tetapi mereka melakukan hal-hal yang berbeda: beberapa istirahat, beberapa melanjutkan ... Anda harus membaca setiap baris.

Dalam pendekatan bertingkat saya, semakin dalam Anda melangkah, semakin 'bermanfaat' kode tersebut.

Tetapi, jika jauh di lubuk hati Anda akan menemukan alasan untuk menghentikan loop (selain kondisi primer), istirahat atau kembali akan menggunakannya. Saya lebih suka menggunakan bendera ekstra yang akan diuji dalam kondisi tingkat atas. Break / return lebih langsung; itu lebih baik menyatakan maksud daripada menetapkan variabel lain.

Peter Frings
sumber
+1 Namun sebenarnya <perbandingan Anda harus <=sesuai dengan solusi OP
El Ronnoco
3
Dalam kebanyakan kasus, jika seseorang sangat dalam sehingga seseorang perlu menggunakan break / return untuk mengelola kontrol aliran, fungsi / metode seseorang terlalu kompleks.
bit-twiddler
6

"Kejahatan" tergantung pada bagaimana Anda menggunakannya. Saya biasanya menggunakan istirahat dalam looping constructs ONLY ketika itu akan menyelamatkan saya siklus yang tidak dapat disimpan melalui refactoring suatu algoritma. Misalnya, bersepeda melalui koleksi mencari item dengan nilai di properti tertentu yang disetel ke true. Jika yang perlu Anda ketahui adalah bahwa salah satu item memiliki properti ini disetel ke true, setelah Anda mencapai hasil itu, istirahat adalah baik untuk mengakhiri loop dengan tepat.

Jika menggunakan istirahat tidak akan membuat kode secara khusus lebih mudah dibaca, lebih pendek untuk menjalankan atau menyimpan siklus dalam pemrosesan secara signifikan, maka yang terbaik adalah tidak menggunakannya. Saya cenderung memberi kode ke "common denominator terendah" bila mungkin untuk memastikan bahwa siapa pun yang mengikuti saya dapat dengan mudah melihat kode saya dan mencari tahu apa yang terjadi (saya tidak selalu berhasil dalam hal ini). Istirahat mengurangi itu karena mereka memperkenalkan titik masuk / keluar aneh. Disalahgunakan mereka bisa berperilaku sangat mirip dengan pernyataan "goto" yang mendera.

Joel Etherton
sumber
Saya setuju dengan kedua poin! Sejauh poin kedua Anda - saya pikir lebih mudah untuk mengikuti posting asli saya karena terbaca seperti bahasa Inggris. Jika Anda memiliki kombinasi kondisi dalam 1 jika pernyataan, maka hampir merupakan penguraian untuk mencari tahu apa yang harus terjadi untuk menjalankan jika benar.
Mikhail
@Mikhail: Sampel yang Anda berikan sedikit altruistik untuk realisme tertentu. Seperti yang saya lihat, sampel-sampel itu jelas, ringkas, lebih mudah dibaca. Kebanyakan loop tidak seperti itu. Kebanyakan loop memiliki semacam logika lain yang mereka lakukan dan berpotensi kondisional yang jauh lebih rumit. Ini dalam kasus-kasus ini di mana break / continue mungkin bukan penggunaan terbaik karena itu berlumpur logika ketika dibaca.
Joel Etherton
4

Sama sekali tidak ... Ya penggunaannya gotoburuk karena memperburuk struktur program Anda dan juga sangat sulit untuk memahami aliran kontrol.

Tetapi penggunaan pernyataan seperti breakdan continuesangat diperlukan hari ini dan tidak dianggap sebagai praktik pemrograman yang buruk sama sekali.

Dan juga tidak sulit untuk memahami aliran kontrol dalam penggunaan breakdan continue. Dalam konstruksi seperti switchyang breakpernyataan mutlak diperlukan.

Radheshyam Nayak
sumber
2
Saya belum menggunakan "terus" sejak saya pertama kali belajar C pada tahun 1981. Ini adalah fitur bahasa yang tidak perlu, karena kode yang dilewati oleh pernyataan terus dapat dibungkus oleh pernyataan kontrol bersyarat.
bit-twiddler
12
Saya lebih suka menggunakan continue dalam kasus-kasus itu karena memastikan bahwa kode saya tidak menjadi kode panah. Saya benci kode panah lebih dari pernyataan jenis goto. Saya juga membacanya sebagai, "jika pernyataan ini benar, lewati sisa lingkaran ini dan lanjutkan dengan iterasi berikutnya." Sangat berguna saat berada di awal for for loop (kurang berguna saat loop).
jsternberg
@jsternberg Untuk kemenangan! :-)
Notinlist
3

Gagasan penting berasal dari kemampuan menganalisis program Anda secara semantik. Jika Anda memiliki satu entri dan satu keluar, matematika yang diperlukan untuk menunjukkan kemungkinan keadaan jauh lebih mudah daripada jika Anda harus mengelola jalur forking.

Sebagian, kesulitan ini mencerminkan kemampuan berpikir secara konseptual tentang kode Anda.

Terus terang, kode kedua Anda tidak jelas. Apa yang sedang dilakukannya? Apakah melanjutkan 'melanjutkan', atau apakah 'selanjutnya' loop? Saya tidak punya ide. Setidaknya contoh pertama Anda jelas.

Paul Nathan
sumber
Ketika saya bekerja di proyek yang manajer wajib menggunakannya, saya harus menggunakan diagram alur untuk mengingat penggunaannya, dan beberapa jalur keluar, membuat kode th lebih membingungkan ...
umlcat
komentar "Frankly" Anda adalah sintaksis - "selanjutnya" adalah hal yang penting; bahasa normal menggunakan "terus" yang berarti "lewati loop ini"
Mikhail
@Mik, mungkin "lewati iterasi ini" akan menjadi deskripsi yang lebih baik. Hormat saya, Dr. IM Pedantic
Pete Wilson
@Mikhail: Tentu. Tetapi ketika seseorang bekerja dalam banyak bahasa, itu dapat menyebabkan kesalahan ketika sintaks tidak umum di antara bahasa.
Paul Nathan
Tapi matematika bukan pemrograman. Kode lebih ekspresif daripada matematika. Saya mendapatkan bahwa single-entry / single-exit dapat membuat flowchart terlihat lebih bagus tetapi dengan biaya apa (mis. Dimana break / return dapat membuat kode bekerja lebih baik)?
Evan Plaice
3

Saya akan mengganti potongan kode kedua Anda dengan

while (primary_condition && (loop_count <= 1000 && time_exect <= 3600)) {
    if (this->data != "undefined" && this->skip != true) {
        ..
    }
}

bukan karena alasan kesederhanaan - saya benar-benar berpikir ini lebih mudah dibaca dan bagi seseorang untuk memahami apa yang sedang terjadi. Secara umum kondisi loop Anda harus terkandung murni dalam kondisi loop yang tidak berserakan di seluruh tubuh. Namun ada beberapa situasi di mana breakdan continuedapat membantu keterbacaan. breaklebih banyak daripada yang continuebisa saya tambahkan: D

El Ronnoco
sumber
Sementara saya tidak setuju dengan, "Saya benar-benar berpikir ini lebih mudah dibaca" ini mencapai tujuan yang sama persis seperti kode di atas. Saya tidak pernah benar-benar berpikir 'break' sebagai operator hubung singkat sampai sekarang tetapi itu masuk akal. Adapun, "Secara umum kondisi untuk loop Anda harus terkandung murni dalam kondisi loop", apa yang Anda lakukan ketika Anda memproses loop foreach karena benar-benar tidak ada cara untuk memasukkan logika kondisional dalam definisi loop?
Evan Plaice
@Van Kondisi ini tidak berlaku untuk foreachloop karena ini hanya akan mengulangi setiap item dalam koleksi. Sebuah forloop serupa dalam hal itu seharusnya tidak memiliki titik akhir bersyarat. Jika Anda memang membutuhkan titik akhir bersyarat maka Anda perlu menggunakan whileloop.
El Ronnoco
1
@ Evan saya memang mengerti maksud Anda - yaitu 'bagaimana jika Anda perlu keluar dari loop foreach?' - Yah, seharusnya hanya ada satu breakmaksimum menurut pendapat saya dari loop.
El Ronnoco
2

Saya tidak setuju dengan atasan Anda. Ada tempat yang tepat untuk breakdan continueuntuk digunakan. Faktanya, alasan eksekusi dan penanganan perkecualian diperkenalkan ke bahasa pemrograman modern adalah Anda tidak bisa menyelesaikan setiap masalah hanya dengan menggunakan structured techniques.

Sebagai tambahan saya tidak ingin memulai diskusi keagamaan di sini tetapi Anda dapat menyusun kembali kode Anda menjadi lebih mudah dibaca seperti ini:

while (primary_condition) {
    if (loop_count > 1000) || (time_exect > 3600) {
        break;
    } else if ( ( this->data != "undefined") && ( !this->skip ) ) {
       ... // where the real work of the loop happens
    }
}

Di sisi lain catatan

Saya pribadi tidak suka penggunaan ( flag == true )dalam kondisi karena jika variabel sudah menjadi boolean, maka Anda memperkenalkan perbandingan tambahan yang perlu terjadi ketika nilai boolean memiliki jawaban yang Anda inginkan - kecuali tentu saja Anda yakin bahwa kompiler Anda akan optimalkan perbandingan ekstra itu.

Zeke Hansell
sumber
Saya telah bertanya-tanya tentang pertanyaan ini selama bertahun-tahun. Cara Anda ('jika (bendera) {...}') jauh lebih singkat dan mungkin terlihat lebih 'profesional' atau 'ahli'. Tapi, ya 'tahu, itu sering berarti bahwa seorang pembaca / pengelola harus menyela dirinya sebentar untuk mengingat apa arti konstruk; dan untuk memastikan rasa ujian. Saat ini, saya menggunakan 'if (flag == true) {...}' hanya karena sepertinya dokumentasi yang lebih baik. Bulan depan? Sabe tenang?
Pete Wilson
2
@Pete - Istilah ini tidak singkat tetapi elegan . Anda dapat membatalkan semua yang Anda inginkan, tetapi jika Anda khawatir bahwa pembaca / pengelola tidak mengerti apa booleanitu atau apa arti terminologi singkat / elegan itu maka mungkin Anda lebih baik menyewa beberapa pengelola yang lebih pintar ;-)
Zeke Hansell
@ Pete, saya juga mendukung pernyataan saya tentang kode yang dihasilkan. Anda melakukan satu lagi perbandingan dengan membandingkan bendera ke nilai konstan sebelum mengevaluasi nilai boolean dari ekspresi. Mengapa membuatnya lebih sulit daripada yang seharusnya, variabel flag sudah memiliki nilai yang Anda inginkan!
Zeke Hansell
+1 pekerjaan bagus. Jelas jauh lebih elegan dari contoh sebelumnya.
Evan Plaice
2

Saya setuju dengan bos Anda. Mereka buruk karena mereka menghasilkan metode dengan kompleksitas siklomatik yang tinggi. Metode seperti itu sulit dibaca dan sulit untuk diuji. Untungnya ada solusi mudah. Ekstrak loop body menjadi metode yang terpisah, di mana "melanjutkan" menjadi "kembali". "Kembali" lebih baik karena setelah "kembali" selesai - tidak ada kekhawatiran tentang keadaan setempat.

Untuk "break" ekstrak loop itu sendiri ke metode yang terpisah, ganti "break" dengan "return".

Jika metode yang diekstraksi membutuhkan sejumlah besar argumen, itu indikasi untuk mengekstraksi kelas - baik mengumpulkannya ke objek konteks.

kevin cline
sumber
0

Saya pikir itu hanya masalah ketika bersarang jauh di dalam banyak loop. Sulit untuk mengetahui loop mana yang Anda hentikan. Mungkin sulit untuk melanjutkan, tetapi saya pikir rasa sakit yang sebenarnya berasal dari istirahat - logika bisa sulit untuk diikuti.

davidhaskins
sumber
2
Sebenarnya, mudah untuk melihat apakah Anda indentasi dengan benar, semantik pernyataan-pernyataan itu diinternalisasi dan tidak terlalu mengantuk.
@delnan - itu banyak asumsi;)
davidhaskins
2
Ya, terutama yang terakhir.
Michael K
Yah, toh # 1 diperlukan untuk pemrograman yang serius, # 2 diharapkan dari semua orang yang disebut programmer, dan # 3 cukup berguna secara umum;)
Beberapa bahasa (hanya skrip yang saya tahu) mendukung break 2; untuk yang lain, saya kira flag bool sementara digunakan
Mikhail
0

Selama mereka tidak digunakan sebagai goto tersamar seperti pada contoh berikut:

do
{
      if (foo)
      {
             /*** code ***/
             break;
      }

      if (bar)
      {
             /*** code ***/
             break;
      }
} while (0);

Saya baik-baik saja dengan mereka. (Contoh terlihat dalam kode produksi, meh)

SuperBloup
sumber
Apakah Anda merekomendasikan untuk kasus seperti ini untuk membuat fungsi?
Mikhail
Ya dan jika tidak memungkinkan, goto polos. Ketika saya melihat do, saya pikir pengulangan, bukan konteks untuk mensimulasikan lompatan.
SuperBloup
oh saya setuju, saya hanya bertanya bagaimana Anda akan melakukannya :) ilmuwan Komputer melakukannya berulang-ulang
Mikhail
0

Saya tidak suka gaya ini. Inilah yang saya inginkan:

function verify(object)
{
    if not (object->value < 0) 
       and not(object->value > object->max_value)
       and not(object->name == "") 
       {
         do somethign important
       }
    else return false; //probably not necessary since this function doesn't even seem to be defined to return anything...?
}

Saya benar-benar tidak suka menggunakan returnuntuk membatalkan fungsi. Rasanya seperti penyalahgunaan return.

Menggunakan breakjuga tidak selalu jelas untuk dibaca.

Mungkin lebih baik:

notdone := primarycondition    
while (notDone)
{
    if (loop_count > 1000) or (time_exect > 3600)
    {
       notDone := false; 
    }
    else
    { 
        skipCurrentIteration := (this->data == "undefined") or (this->skip == true) 

        if not skipCurrentIteration
        {
           do something
        } 
        ...
    }
}

lebih sedikit bersarang dan kondisi kompleks dire-refored menjadi variabel (dalam program nyata Anda harus memiliki nama yang lebih baik, jelas ...)

(Semua kode di atas adalah kode semu)

FrustratedWithFormsDesigner
sumber
11
Anda benar-benar lebih suka 3 tingkat bersarang daripada yang saya ketik di atas?
Mikhail
@Mikhail: Ya, atau saya akan menetapkan hasil kondisi ke variabel. Saya merasa jauh lebih mudah dipahami daripada logika breakdan continue. Mengakhiri loop secara tidak normal hanya terasa aneh dan saya tidak menyukainya.
FrustratedWithFormsDesigner
1
Ironisnya, Anda salah membaca kondisi saya. continueberarti lewati fungsionalitas, lanjutkan ke loop berikutnya; tidak "lanjutkan dengan eksekusi"
Mikhail
@Mikhail: Ah. Saya tidak sering menggunakannya dan ketika saya membacanya, saya menjadi bingung dengan artinya. Alasan lain saya tidak suka itu. : P Beri saya waktu sebentar untuk memperbarui ...
FrustratedWithFormsDesigner
7
Terlalu banyak bersarang merusak keterbacaan. Dan kadang-kadang, menghindari jeda / lanjutkan memperkenalkan perlunya membalikkan logika Anda pada tes bersyarat Anda, yang dapat menyebabkan salah tafsir tentang apa yang dilakukan kode Anda - Saya hanya bilang '
Zeke Hansell
0

Tidak. Ini cara untuk memecahkan masalah, dan ada cara lain untuk menyelesaikannya.

Banyak bahasa arus utama (Java, .NET (C # + VB), PHP, tulis sendiri) menggunakan "break" dan "continue" untuk melewati loop. Keduanya "kalimat terstruktur".

Tanpa mereka:

String myKey = "mars";

int i = 0; bool found = false;
while ((i < MyList.Count) && (not found)) {
  found = (MyList[i].key == myKey);
  i++;   
}
if (found)
  ShowMessage("Key is " + i.toString());
else
  ShowMessage("Not found.");

Dengan mereka:

String myKey = "mars";

for (i = 0; i < MyList.Count; i++) {
  if (MyList[i].key == myKey)
    break;
}
ShowMessage("Key is " + i.toString());

Perhatikan bahwa kode "break" dan "continue" lebih pendek, dan biasanya mengubah "While" kalimat menjadi "for" atau "foreach".

Kedua kasus adalah masalah gaya pengkodean. Saya lebih suka tidak menggunakannya , karena gaya verbose memungkinkan saya untuk memiliki lebih banyak kontrol kode.

Saya sebenarnya, bekerja di beberapa proyek, di mana wajib menggunakan kalimat-kalimat itu.

Beberapa pengembang mungkin berpikir mereka tidak perlu, tetapi hipotetis, jika kita harus menghapusnya, kita harus menghapus "while" dan "do while" ("ulangi sampai", kalian pascal guys) juga ;-)

Kesimpulan, bahkan jika saya lebih suka untuk tidak menggunakannya, saya pikir ini pilihan, bukan praktik pemrograman yang buruk.

umlcat
sumber
Maaf untuk pilih-pilih, tetapi contoh kedua Anda kehilangan output ketika kunci tidak ditemukan (jadi tentu saja itu terlihat jauh lebih pendek).
FrustratedWithFormsDesigner
2
belum lagi contoh pertama hanya berfungsi jika kuncinya adalah yang terakhir dalam daftar.
Mikhail
@FrustratedWithFormsDesigner PERSIS. Saya memakai purpouse untuk menunjukkan mengapa metode itu lebih disukai ;-)
umlcat
Namun, Anda memiliki dua rutinitas dengan semantik yang berbeda; oleh karena itu, mereka tidak setara secara logis.
bit-twiddler
2
contoh kedua Anda memiliki dua bug, satu sintaksis dan satu logis. 1. Ini tidak akan dikompilasi karena iterator tidak dideklarasikan di luar lingkup for for loop (oleh karena itu tidak tersedia dalam output string). 2. Bahkan jika iterator dideklarasikan di luar loop, jika kunci tidak ditemukan dalam koleksi, output string akan mencetak kunci item terakhir dalam daftar.
Evan Plaice
0

Saya tidak menentang continuedan breakpada prinsipnya, tetapi saya pikir itu adalah konstruksi tingkat sangat rendah yang seringkali dapat diganti dengan sesuatu yang lebih baik.

Saya menggunakan C # sebagai contoh di sini, pertimbangkan kasus keinginan untuk beralih pada koleksi, tetapi kami hanya ingin elemen yang memenuhi beberapa predikat, dan kami tidak ingin melakukan lebih dari maksimal 100 iterasi.

for (var i = 0; i < collection.Count; i++)
{
    if (!Predicate(item)) continue;
    if (i >= 100) break; // at first I used a > here which is a bug. another good thing about the more declarative style!

    DoStuff(item);
}

Ini terlihat sangat bersih. Tidak terlalu sulit untuk dimengerti. Saya pikir itu akan berdiri untuk mendapatkan banyak dari menjadi lebih deklaratif. Bandingkan dengan yang berikut ini:

foreach (var item in collection.Where(Predicate).Take(100))
    DoStuff(item);

Mungkin di mana dan menerima panggilan bahkan tidak boleh dalam metode ini. Mungkin pemfilteran ini harus dilakukan SEBELUM koleksi dikirimkan ke metode ini. Bagaimanapun, dengan beralih dari hal-hal tingkat rendah dan lebih fokus pada logika bisnis yang sebenarnya, menjadi lebih jelas apa yang benar-benar kami minati. Menjadi lebih mudah untuk memisahkan kode kami menjadi modul kohesif yang lebih berpegang pada praktik desain yang baik dan karenanya di.

Barang-barang tingkat rendah masih akan ada di beberapa bagian kode, tetapi kami ingin menyembunyikan ini sebanyak mungkin, karena dibutuhkan energi mental yang bisa kami gunakan untuk alasan tentang masalah bisnis.

kai
sumber
-1

Kode Lengkap memiliki bagian yang bagus tentang penggunaan gotodan beberapa pengembalian dari rutin atau loop.

Secara umum itu bukan praktik yang buruk. breakatau continuekatakan apa yang terjadi selanjutnya. Dan saya setuju dengan ini.

Steve McConnell (penulis Code Complete) menggunakan contoh yang hampir sama dengan Anda untuk menunjukkan keuntungan menggunakan berbagai gotopernyataan.

Namun terlalu sering digunakan breakatau continuedapat menyebabkan perangkat lunak yang kompleks dan tidak terawat.

Grzegorz Gierlik
sumber