Apakah praktik yang buruk menggunakan break in a for loop? [Tutup]

123

Apakah praktik yang buruk menggunakan breakpernyataan di dalam forloop ?

Katakanlah, saya mencari nilai dalam array. Bandingkan di dalam perulangan for dan ketika nilai ditemukan, break;untuk keluar dari perulangan for.

Apakah ini praktik yang buruk? Saya telah melihat alternatif yang digunakan: definisikan variabel vFounddan setel ke true ketika nilainya ditemukan dan periksa vFounddalam forkondisi pernyataan. Tetapi apakah perlu membuat variabel baru hanya untuk tujuan ini?

Saya bertanya dalam konteks normal C atau C ++ for loop.

PS: Pedoman pengkodean MISRA menyarankan agar tidak menggunakan istirahat.

kiki
sumber
28
Jangan tempatkan breakdi liga yang sama dengan goto:)
BoltClock
2
Eh, saya belum menempatkan mereka di liga yang sama dengan pergi ke ... Peraturan MISRA menentukan istirahat, jika saya ingat dengan benar.
kiki
1
Satu-satunya alasan yang dapat saya pikirkan untuk tidak menggunakan break inside a loop adalah ketika Anda masih harus memproses lebih banyak item yang mungkin mengubah hasil dari apa yang Anda lakukan ...
Younes
4
Aturan MISRA telah dilonggarkan: misra.org.uk/forum/viewtopic.php?f=75&t=298
Johan Kotlinski

Jawaban:

122

Banyak jawaban di sini, tetapi saya belum melihat ini disebutkan:

Sebagian besar "bahaya" yang terkait dengan penggunaan breakatau continuepengulangan for dinegasikan jika Anda menulis pengulangan yang rapi dan mudah dibaca. Jika badan loop Anda mencakup beberapa panjang layar dan memiliki beberapa sub-blok bersarang, ya, Anda dapat dengan mudah lupa bahwa beberapa kode tidak akan dijalankan setelah jeda. Namun, jika loopnya pendek dan to the point, tujuan pernyataan break harus jelas.

Jika sebuah loop menjadi terlalu besar, gunakan satu atau beberapa pemanggilan fungsi yang dinamai baik di dalam loop sebagai gantinya. Satu-satunya alasan sebenarnya untuk menghindari melakukannya adalah untuk memproses kemacetan.

e. James
sumber
11
Benar sekali. Tentu saja jika lingkarannya begitu besar dan kompleks sehingga sulit untuk melihat apa yang terjadi di dalamnya, itu masalah apakah Anda sedang istirahat atau tidak.
Jay
1
Masalah lainnya adalah istirahat dan terus menyebabkan masalah dengan refactoring. Ini mungkin pertanda bahwa ini adalah praktik yang buruk. Maksud dari kode ini juga lebih jelas saat menggunakan pernyataan if.
Ed Greaves
"Pemanggilan fungsi yang dinamai dengan baik" tersebut dapat berupa fungsi lambda yang ditentukan secara lokal daripada fungsi pribadi yang ditentukan secara eksternal yang tidak akan digunakan di tempat lain.
DavidRR
135

Tidak, istirahat adalah solusi yang tepat.

Menambahkan variabel boolean membuat kode lebih sulit dibaca dan menambahkan potensi sumber kesalahan.

penyeringai
sumber
2
sepakat. Terutama jika Anda ingin keluar dari loop dengan lebih dari satu kondisi. Boolean untuk meninggalkan loop bisa menjadi sangat membingungkan.
Rontologist
2
Itulah mengapa saya biasa gotokeluar dari loop bersarang Level 2+ - karena tidak adabreak(level)
Петър Петров
3
Saya lebih suka meletakkan kode loop dalam sebuah fungsi, dan hanya kembali. Ini menghindari goto, dan memecah kode Anda menjadi potongan-potongan yang lebih kecil.
Dave Branton
53

Anda dapat menemukan semua jenis kode profesional dengan pernyataan 'break' di dalamnya. Sangat masuk akal untuk menggunakan ini kapan pun diperlukan. Dalam kasus Anda, opsi ini lebih baik daripada membuat variabel terpisah hanya untuk tujuan keluar dari loop.

Amit S
sumber
47

Baik-baik saja menggunakan breakserta continuedalam satu forloop.

Ini menyederhanakan kode dan meningkatkan keterbacaannya.


sumber
Ya .. Untuk beberapa waktu saya tidak menyukai ide dan mengkodekannya, tetapi tidak butuh waktu lama sebelum saya menyadari ... "solusi" sering kali merupakan mimpi buruk pemeliharaan. Nah, bukan mimpi buruk, tapi lebih rawan kesalahan.
MetalMikester
24

Jauh dari praktik buruk, Python (dan bahasa lain?) forMemperluas struktur perulangan sehingga sebagian darinya hanya akan dijalankan jika perulangan tidak break .

for n in range(5):
    for m in range(3):
        if m >= n:
            print('stop!')
            break
        print(m, end=' ')
    else:
        print('finished.')

Keluaran:

stop!
0 stop!
0 1 stop!
0 1 2 finished.
0 1 2 finished.

Kode yang setara tanpa breakdan itu berguna else:

for n in range(5):
    aborted = False
    for m in range(3):
        if not aborted:
            if m >= n:
                print('stop!')
                aborted = True
            else:            
                print(m, end=' ')
    if not aborted:
        print('finished.')
Nick T.
sumber
2
Oooh, saya suka itu dan terkadang mengharapkannya dalam C. Sintaks yang bagus juga, yang bisa diadaptasi ke dalam bahasa seperti C di masa depan tanpa ambiguitas.
supercat
"Bahasa seperti C": P
nirvanaswap
15

Tidak ada yang salah dengan menggunakan pernyataan break, tetapi loop bersarang bisa membingungkan. Untuk meningkatkan keterbacaan banyak bahasa ( setidaknya Java ) mendukung pemutusan label yang akan sangat meningkatkan keterbacaan.

int[] iArray = new int[]{0,1,2,3,4,5,6,7,8,9};
int[] jArray = new int[]{0,1,2,3,4,5,6,7,8,9};

// label for i loop
iLoop: for (int i = 0; i < iArray.length; i++) {

    // label for j loop
    jLoop: for (int j = 0; j < jArray.length; j++) {

        if(iArray[i] < jArray[j]){
            // break i and j loops
            break iLoop;
        } else if (iArray[i] > jArray[j]){  
            // breaks only j loop
            break jLoop;
        } else {
            // unclear which loop is ending
            // (breaks only the j loop)
            break;
        }
    }
}

Saya akan mengatakan bahwa pernyataan break (dan return) sering kali meningkatkan kompleksitas siklomatik yang membuatnya lebih sulit untuk membuktikan kode melakukan hal yang benar dalam semua kasus.

Jika Anda mempertimbangkan untuk menggunakan jeda saat mengulang urutan untuk beberapa item tertentu, Anda mungkin ingin mempertimbangkan kembali struktur data yang digunakan untuk menyimpan data Anda. Menggunakan sesuatu seperti Set atau Peta dapat memberikan hasil yang lebih baik.

biksu cyber
sumber
4
1 untuk kompleksitas siklomatik.
sp00m
Pemrogram .NET mungkin ingin menjelajahi penggunaan LINQ sebagai alternatif potensial untuk forloop kompleks .
DavidRR
14

break adalah pernyataan yang sepenuhnya dapat diterima untuk digunakan (begitu juga lanjutkan , btw). Ini semua tentang keterbacaan kode - selama Anda tidak memiliki loop yang terlalu rumit dan semacamnya, tidak masalah.

Ini tidak seperti liga yang sama dengan goto . :)

riviera.dll
sumber
Saya telah melihat hal itu berpendapat bahwa pernyataan break adalah efektif goto .
glenatron
3
Masalah dengan goto adalah Anda dapat mengarahkannya ke mana saja. Keduanya merusak dan terus mempertahankan modularitas kode. Argumen tersebut berada pada level yang sama dengan memiliki satu titik keluar untuk setiap fungsi.
Zachary Yates
1
Saya pernah mendengarnya berpendapat bahwa memiliki beberapa titik keluar untuk suatu fungsi secara efektif merupakan kebagian . ; D
glenatron
2
@glenatron Masalah dengan goto bukanlah pernyataan goto, itu adalah pengenalan label yang dilompati oleh goto itulah masalahnya. Saat Anda membaca pernyataan goto, tidak ada ketidakpastian tentang aliran kontrol. Saat Anda membaca label, ada banyak ketidakpastian tentang aliran kontrol (di mana semua gambar yang mentransfer kontrol ke label ini?). Pernyataan break tidak memperkenalkan label, jadi tidak menimbulkan komplikasi baru.
Stephen C. Steel
1
The "break" adalah goto khusus yang hanya bisa meninggalkan blok. Masalah historis dengan "goto" adalah (1) dapat, dan sering, digunakan untuk membangun blok loop yang tumpang tindih dengan cara yang tidak mungkin dilakukan dengan struktur kontrol yang tepat; (2) cara umum untuk melakukan konstruksi "jika-maka" yang biasanya "salah" adalah dengan memindahkan cabang kasus yang benar ke tempat yang tidak terkait dalam kode, dan kemudian melakukan cabang kembali. Ini akan menelan biaya dua cabang dalam kasus yang jarang terjadi, dan nol dalam kasus umum, tetapi itu membuat kode terlihat mengerikan.
supercat
14

Aturan umum: Jika mengikuti aturan mengharuskan Anda melakukan sesuatu yang lebih canggung dan sulit dibaca kemudian melanggar aturan, maka langgar aturan tersebut.

Dalam kasus perulangan sampai Anda menemukan sesuatu, Anda mengalami masalah membedakan ditemukan dan tidak ditemukan ketika Anda keluar. Itu adalah:

for (int x=0;x<fooCount;++x)
{
  Foo foo=getFooSomehow(x);
  if (foo.bar==42)
    break;
}
// So when we get here, did we find one, or did we fall out the bottom?

Jadi oke, Anda bisa menyetel bendera, atau menginisialisasi nilai "ditemukan" ke null. Tapi

Itulah mengapa secara umum saya lebih suka mendorong pencarian saya ke dalam fungsi:

Foo findFoo(int wantBar)
{
  for (int x=0;x<fooCount;++x)
  {
    Foo foo=getFooSomehow(x);
    if (foo.bar==wantBar)
      return foo;
  }
  // Not found
  return null;
}

Ini juga membantu mengurai kode. Di baris utama, "temukan" menjadi pernyataan tunggal, dan jika kondisinya rumit, hanya ditulis sekali.

Jay
sumber
1
Persis apa yang ingin saya katakan. Seringkali ketika saya menemukan diri saya menggunakan break, saya mencoba menemukan beberapa cara untuk merefaktor kode menjadi sebuah fungsi, sehingga saya dapat menggunakan returnsebagai gantinya.
Rene Saarsoo
Saya setuju 100% dengan Anda, tidak ada yang salah dengan menggunakan jeda. Namun, secara umum menunjukkan bahwa kode itu mungkin perlu diekstraksi menjadi fungsi di mana break akan diganti dengan return. Ini harus dianggap sebagai 'bau kode' daripada kesalahan langsung.
vascowhite
Jawaban Anda mungkin mendorong beberapa orang untuk mengeksplorasi kelayakan menggunakan beberapa pernyataan kembali .
DavidRR
13

Itu tergantung bahasanya. Meskipun Anda dapat memeriksa variabel boolean di sini:

for (int i = 0; i < 100 && stayInLoop; i++) { ... }

tidak mungkin melakukannya saat melakukan itering pada array:

for element in bigList: ...

Bagaimanapun, breakakan membuat kedua kode lebih mudah dibaca.

eumiro
sumber
7

Dalam contoh Anda, Anda tidak mengetahui jumlah iterasi untuk loop for . Mengapa tidak menggunakan while loop sebagai gantinya, yang memungkinkan jumlah iterasi menjadi tak tentu di awal?

Oleh karena itu, tidak perlu menggunakan statemement break secara umum, karena loop dapat lebih baik dinyatakan sebagai loop sementara .

Penjadwal
sumber
1
Loop "untuk" sering kali bagus jika terdapat jumlah iterasi maksimum yang diketahui. Yang telah dikatakan, sementara (1) loop terkadang lebih baik dalam skenario di mana seseorang mencari item dan berencana untuk membuatnya jika tidak ada. Dalam kasus tersebut, jika kode menemukan item, ia melakukan pemutusan langsung, dan jika mencapai akhir larik, ia membuat item baru dan kemudian melakukan jeda.
supercat
2
Dalam contoh yang diberikan - mencari kunci melalui array, loop for adalah konstruksi idiomatik yang tepat. Anda tidak menggunakan while loop untuk berjalan melalui sebuah array. Anda menggunakan for loop. (atau for-each loop jika tersedia). Anda melakukan ini sebagai rasa hormat kepada pemrogram lain, karena mereka mengenali konstruksi "for (int i = 0; i <ra.length; i ++) {}" segera sebagai "Berjalan melalui array" Menambahkan jeda, ke konstruksi ini adalah hanya pernyataan "Berhenti lebih awal jika Anda bisa".
Chris Cudmore
7

Saya setuju dengan orang lain yang merekomendasikan penggunaan break. Pertanyaan konsekuensial yang jelas adalah mengapa ada orang yang merekomendasikan sebaliknya? Nah ... ketika Anda menggunakan break, Anda melewatkan sisa kode di blok tersebut, dan iterasi yang tersisa. Terkadang hal ini menyebabkan bug, misalnya:

  • sumber daya yang diperoleh di bagian atas blok dapat dilepaskan di bagian bawah (ini berlaku bahkan untuk blok di dalam forloop), tetapi langkah rilis tersebut dapat dilewati secara tidak sengaja ketika keluar "prematur" disebabkan oleh breakpernyataan (dalam "modern" C ++, "RAII" digunakan untuk menangani ini dengan cara yang andal dan aman untuk pengecualian: pada dasarnya, resource bebas penghancur objek dengan andal, tidak peduli bagaimana suatu cakupan keluar)

  • seseorang dapat mengubah tes bersyarat dalam forpernyataan tanpa memperhatikan bahwa ada kondisi keluar yang terdelokalisasi lainnya

  • Jawaban ndim mengamati bahwa beberapa orang mungkin menghindari breaks untuk mempertahankan loop run-time yang relatif konsisten, tetapi Anda membandingkan breakdengan penggunaan variabel kontrol keluar awal boolean yang tidak berlaku.

Sesekali orang yang mengamati bug tersebut menyadari bahwa mereka dapat dicegah / dimitigasi oleh aturan "tanpa jeda" ini ... memang, ada strategi terkait keseluruhan untuk pemrograman "lebih aman" yang disebut "pemrograman terstruktur", di mana setiap fungsi seharusnya memiliki satu titik masuk dan keluar juga (yaitu tidak ada goto, tidak ada pengembalian awal). Ini mungkin menghilangkan beberapa bug, tapi pasti memperkenalkan yang lain. Mengapa mereka melakukannya?

  • mereka memiliki kerangka kerja pengembangan yang mendorong gaya pemrograman / kode tertentu, dan mereka memiliki bukti statistik bahwa ini menghasilkan keuntungan bersih dalam kerangka kerja terbatas itu, atau
  • mereka telah dipengaruhi oleh pedoman pemrograman atau pengalaman dalam kerangka seperti itu, atau
  • mereka hanya idiot diktator, atau
  • salah satu dari inersia + historis di atas (relevan karena pembenarannya lebih dapat diterapkan pada C daripada C ++ modern).
Tony Delroy
sumber
Ya, tapi sekali lagi saya telah melihat bug dalam kode oleh orang-orang yang secara religius mencoba menghindar break. Mereka memang menghindarinya tetapi gagal memikirkan kondisi yang menggantikan penggunaan break.
Tomáš Zato - Kembalikan Monica
5

Ini benar-benar valid untuk digunakan break- seperti yang ditunjukkan orang lain, itu tidak ada di liga yang sama seperti goto.

Meskipun Anda mungkin ingin menggunakan vFoundvariabel saat Anda ingin memeriksa di luar loop apakah nilainya ditemukan dalam array. Juga dari sudut pandang pemeliharaan, memiliki bendera umum yang menandakan kriteria keluar mungkin berguna.

Kartu as
sumber
5

Saya melakukan beberapa analisis pada basis kode yang sedang saya kerjakan (40.000 baris JavaScript).

Saya hanya menemukan 22 breakpernyataan, di antaranya:

  • 19 digunakan di dalam switchpernyataan (kami hanya memiliki total 3 pernyataan switch!).
  • 2 digunakan di dalam forloop - kode yang saya segera klasifikasikan untuk direfraktor menjadi fungsi terpisah dan diganti dengan returnpernyataan.
  • Adapun putaran breakdalam terakhir while... Aku berlari git blameuntuk melihat siapa yang menulis omong kosong ini!

Jadi menurut statistik saya: Jika breakdigunakan di luar switch, itu adalah kode bau.

Saya juga mencari continuepernyataan. Tidak ditemukan.

Rene Saarsoo
sumber
4

Saya tidak melihat alasan mengapa itu akan menjadi praktik yang buruk. DIBERIKAN bahwa Anda ingin menyelesaikan pemrosesan STOP pada saat itu.

Cornelius J. van Dyk
sumber
4

Di dunia tertanam, ada banyak kode di luar sana yang menggunakan konstruksi berikut:

    while(1)
    { 
         if (RCIF)
           gx();
         if (command_received == command_we_are_waiting_on)
           break;
         else if ((num_attempts > MAX_ATTEMPTS) || (TickGet() - BaseTick > MAX_TIMEOUT))
           return ERROR;
         num_attempts++;
    }
    if (call_some_bool_returning_function())
      return TRUE;
    else
      return FALSE;

Ini adalah contoh yang sangat umum, banyak hal yang terjadi di balik tirai, khususnya interupsi. Jangan gunakan ini sebagai kode boilerplate, saya hanya mencoba mengilustrasikan sebuah contoh.

Pendapat pribadi saya adalah tidak ada yang salah dengan menulis loop dengan cara ini selama perawatan yang tepat dilakukan untuk mencegah tetap dalam loop tanpa batas.

Nate
sumber
1
Bisakah Anda menjelaskannya? Bagi saya sepertinya loop tidak melakukan apa-apa ... kecuali ada continuepernyataan di dalam logika aplikasi. Ada?
Rene Saarsoo
1
pernyataan continue tidak diperlukan karena Anda sudah terikat oleh loop tak terbatas. Anda pada dasarnya melakukan logika yang diperlukan yang diperlukan untuk mencapai tujuan dan jika tujuan itu tidak terpenuhi dalam jumlah upaya n atau kondisi batas waktu berakhir, Anda keluar dari loop melalui konstruksi istirahat dan mengambil tindakan korektif atau menginformasikan kode panggilan kesalahan. Saya sering melihat jenis kode ini di UC yang berkomunikasi melalui bus umum (RS-485, dll.) Anda pada dasarnya mencoba merebut garis dan berkomunikasi tanpa tabrakan, jika tabrakan terjadi, Anda menunggu durasi yang ditentukan oleh nomor acak dan mencoba lagi.
Nate
1
Sebaliknya, jika semuanya berjalan dengan baik, Anda juga menggunakan pernyataan break untuk keluar setelah kondisi terpenuhi. Dalam kasus ini, kode yang mengikuti loop dijalankan dan semua orang senang.
Nate
"jika (call_some_bool_returning_function ()) mengembalikan TRUE; jika tidak mengembalikan FALSE;" bisa saja "return call_some_bool_returning_function ()"
frankster
3

Tergantung pada kasus penggunaan Anda. Ada aplikasi di mana runtime for loop harus konstan (misalnya untuk memenuhi beberapa batasan waktu, atau untuk menyembunyikan data internal Anda dari serangan berbasis waktu).

Dalam kasus tersebut bahkan akan masuk akal untuk menetapkan sebuah bendera dan hanya memeriksa nilai bendera SETELAH semua foriterasi loop benar-benar berjalan. Tentu saja, semua perulangan for perlu menjalankan kode yang masih membutuhkan waktu yang hampir bersamaan.

Jika Anda tidak peduli dengan run time ... gunakan break;dan continue;buat kode lebih mudah dibaca.

ndim
sumber
1
Jika pengaturan waktu penting, Anda mungkin lebih baik tidur dan menghemat sumber daya sistem daripada membiarkan program melakukan operasi yang diketahui tidak berguna.
Bgs
2

Pada aturan MISRA 98, yang digunakan di perusahaan saya di C dev, pernyataan break tidak boleh digunakan ...

Edit: Istirahat diperbolehkan di MISRA '04

Benoît
sumber
2
Persisnya mengapa saya menanyakan pertanyaan ini! Saya tidak menemukan alasan bagus mengapa aturan seperti itu ada sebagai pedoman pengkodean. Juga, seperti yang dikatakan semua orang di sini, istirahat; Terlihat lebih baik.
kiki
1
Saya tidak tahu persis mengapa itu dilarang (orang yang lebih pintar daripada saya memikirkannya ...) tetapi istirahat (dan pengembalian ganda) lebih baik untuk dibaca (menurut saya, tetapi pendapat saya bukan peraturan perusahaan ... )
Benoît
1
Uh, aturan MISRA mengizinkan penggunaan pernyataan istirahat: misra.org.uk/forum/viewtopic.php?f=75&t=298
luis.espinal
Kami menggunakan aturan MISRA 98 ... Tepatnya MISRA 2004 mengubah aturan
Benoît
0

Tentu, break;adalah solusi untuk menghentikan loop for atau foreach loop. Saya menggunakannya di php di foreach dan for loop dan ternyata berfungsi.

gautamlakum
sumber
0

Saya tidak setuju!

Mengapa Anda mengabaikan fungsionalitas bawaan dari perulangan for untuk membuatnya sendiri? Anda tidak perlu menemukan kembali roda.

Saya pikir lebih masuk akal untuk memiliki cek Anda di bagian atas loop for Anda seperti itu

for(int i = 0; i < myCollection.Length && myCollection[i].SomeValue != "Break Condition"; i++)
{
//loop body
}

atau jika Anda perlu memproses baris tersebut terlebih dahulu

for(int i = 0; i < myCollection.Length && (i == 0 ? true : myCollection[i-1].SomeValue != "Break Condition"); i++)
{
//loop body
}

Dengan cara ini Anda dapat menulis fungsi untuk melakukan semuanya dan membuat kode yang lebih bersih.

for(int i = 0; i < myCollection.Length && (i == 0 ? true : myCollection[i-1].SomeValue != "Break Condition"); i++)
{
    DoAllThatCrazyStuff(myCollection[i]);
}

Atau jika kondisi Anda rumit, Anda dapat memindahkan kode itu juga!

for(int i = 0; i < myCollection.Length && BreakFunctionCheck(i, myCollection); i++)
{
    DoAllThatCrazyStuff(myCollection[i]);
}

"Kode Profesional" yang penuh dengan jeda tidak terdengar seperti kode profesional bagi saya. Kedengarannya seperti pengkodean malas;)

Biff MaGriff
sumber
4
malas coding IS kode profesional :)
Jason
Hanya jika tidak sakit di pantat untuk mempertahankan :)
Biff MaGriff
2
Saya cenderung berlatih "GTFO ASAP". Artinya, jika saya dapat keluar dari suatu struktur dengan bersih maka saya harus melakukannya secepat mungkin, dan sedekat mungkin dengan kondisi yang menentukan keluar.
Chris Cudmore
Itu juga berhasil. Saya pikir argumen yang saya coba sampaikan adalah bahwa jika Anda menggunakan for loop, tidak ada salahnya menggunakan fungsionalitas bawaannya untuk membuat kode Anda dapat dibaca dan modular. Jika Anda benar-benar perlu memiliki pernyataan istirahat di dalam lingkaran Anda, saya akan duduk dan memikirkan mengapa tingkat kerumitan itu diperlukan.
Biff MaGriff
1
Ha ha. Pertimbangkan fakta bahwa kebanyakan orang yang berkecil hati breakberpendapat bahwa itu untuk keterbacaan kode. Sekarang lihat lagi kondisi gila sepanjang 5 mil di loop di atas ...
Tomáš Zato - Kembalikan Monica