Di dalam for-loop, haruskah saya memindahkan kondisi istirahat ke bidang kondisi jika memungkinkan? [Tutup]

48

Terkadang saya membutuhkan loop yang membutuhkan istirahat seperti ini:

for(int i=0;i<array.length;i++){
    //some other code
    if(condition){
        break;
    }
}

Saya merasa tidak nyaman dengan tulisan

if(condition){
    break;
}

karena mengkonsumsi 3 baris kode. Dan saya menemukan loop dapat ditulis ulang sebagai:

                                   ↓
for(int i=0;i<array.length && !condition;i++){
    //some other code
}

Jadi pertanyaan saya adalah, apakah praktik yang baik untuk memindahkan kondisi ke bidang kondisi untuk mengurangi garis kode jika mungkin?

ocomfd
sumber
7
Tergantung pada apa yang conditionkhususnya.
Bergi
4
Ada alasan mengapa misra melarang penggunaan "break" dan "continue" dalam loop. Lihat juga ini: stackoverflow.com/q/3922599/476681
BЈовић
35
Saya pikir jumlah baris itu sendiri bukan masalah sebenarnya. Itu bisa ditulis dalam satu baris. jika (kondisi) rusak; Masalahnya menurut saya adalah keterbacaan. Saya pribadi tidak suka melanggar loop selain menggunakan kondisi loop.
Joe
97
karena mengkonsumsi 3 baris kode A alasan yang sangat, sangat, sangat buruk untuk tidak menyukai gaya apa pun. IMO "mengonsumsi baris kode" adalah suatu tempat antara yang tidak relevan dan hal yang baik. Mencoba memasukkan terlalu banyak logika ke dalam beberapa baris akan menghasilkan kode yang tidak dapat dibaca dan bug yang sulit ditemukan.
Andrew Henle
3
Pendapat yang mungkin tidak populer: for(int i=0;i<array.length && !condition;i++)relatif tidak umum dan dapat diabaikan oleh seseorang yang hanya membaca kode; ini mungkin kasus penggunaan yang baik untuk a whileatau do whileloop, yang lebih sering memiliki beberapa kondisi break dalam definisi loop.
Jules

Jawaban:

154

Dua contoh yang Anda berikan tidak setara secara fungsional. Dalam aslinya, pengujian kondisi dilakukan setelah bagian "kode lain", sedangkan dalam versi yang dimodifikasi, dilakukan terlebih dahulu, pada awal tubuh loop.

Kode tidak boleh ditulis ulang dengan tujuan mengurangi jumlah baris. Jelas itu adalah bonus yang bagus ketika berhasil seperti itu, tetapi itu tidak boleh dilakukan dengan mengorbankan keterbacaan atau kebenaran.

Pete
sumber
5
Saya mengerti sentimen Anda, tetapi saya telah mempertahankan kode legacy yang cukup di mana loop panjangnya beberapa ratus baris dan ada beberapa break bersyarat. Itu membuat debugging sangat sulit ketika Anda mengharapkan kode untuk masuk ke istirahat bersyarat yang Anda harapkan tetapi kode sebelum dilakukan itu istirahat bersyarat terlebih dahulu. Bagaimana Anda menangani kode itu? Dalam contoh singkat seperti di atas, mudah untuk melupakan skenario yang terlalu umum semacam itu.
Berin Loritsch
84
@BerinLoritsch: Cara yang benar untuk mengatasinya adalah tidak memiliki loop yang panjangnya beberapa ratus baris!
Chris
27
@ Walfrat: Jika menulis ulang itu bukan pilihan maka sebenarnya tidak ada pertanyaan lagi. Setelah mengatakan bahwa Anda biasanya dapat cukup aman menggunakan tipe refactor "Extract Method" untuk memindahkan blok kode di sekitar yang seharusnya membuat loop metode utama Anda lebih pendek dan mudah-mudahan membuat aliran logika lebih jelas. Ini benar-benar kasus per kasus. Saya akan mendukung aturan umum saya untuk menjaga metode tetap pendek dan pasti tetap pendek, tetapi saya tidak ingin mencoba mengatakan lebih dari itu dalam pengertian umum ...
Chris
4
@AndrewHenle singkatnya keterbacaan, setidaknya dalam arti bahwa meningkatkan verbositas selalu mengurangi keterbacaan dan pemeliharaan. Mengurangi LOC dicapai dengan meningkatkan kepadatan dan meningkatkan level abstraksi, dan berkorelasi sangat erat dengan rawatan seperti yang dibuktikan pada pasangan Q / A lain di lokasi ini.
Leushenko
5
@ Jo do Do-While konstruksi sangat jarang sehingga cenderung untuk pengembang perjalanan yang perlu mempertahankan kode itu nanti. Beberapa devs tidak pernah benar-benar melihat mereka! Saya tidak yakin apakah do-sementara akan meningkatkan keterbacaan sama sekali - jika ada, itu akan meningkatkan kesulitan untuk memahami kode. Sebagai contoh - basis kode saya saat ini berusia sekitar 15 tahun dan memiliki sekitar 2 juta LOC. Sekitar lima puluh pengembang sudah menyentuhnya. Ini benar-benar nol loop do-while!
T. Sar - Pasang kembali Monica
51

Saya tidak membeli argumen bahwa "ia mengkonsumsi 3 baris kode" dan karenanya buruk. Bagaimanapun, Anda bisa menuliskannya sebagai:

if (condition) break;

dan hanya mengkonsumsi satu baris.

Jika ifmuncul setengah jalan melalui loop, tentu saja harus ada sebagai tes terpisah. Namun, jika tes ini muncul di akhir blok, Anda telah membuat loop yang memiliki tes melanjutkan di kedua ujung loop, mungkin menambah kompleksitas kode. Berhati-hatilah, bahwa terkadang if (condition)tes hanya masuk akal setelah blok dijalankan.

Tetapi dengan asumsi itu tidak terjadi, dengan mengadopsi pendekatan lain:

for(int i=0;i<array.length && !condition;i++){
    //some other code
}

kondisi keluar disimpan bersama, yang dapat menyederhanakan banyak hal (terutama jika " beberapa kode lain " panjang).

Tidak ada jawaban yang tepat di sini, tentu saja. Jadi berhati-hatilah dengan idiom bahasa tersebut, seperti apa konvensi pengkodean tim Anda, dll. Tetapi secara seimbang, saya akan mengadopsi pendekatan uji tunggal ketika masuk akal secara fungsional untuk melakukannya.

David Arno
sumber
14
@ jpmc26, Lebih Baik? Tidak. Gaya alternatif yang valid? Tentu saja.
David Arno
6
Lebih baik seperti lebih sulit untuk mengacaukan ketika kode diedit nanti, dan tidak menderita kerugian yang menonjol.
jpmc26
4
Menyatukan keduanya karena "konten lingkaran mungkin panjang" tampaknya menjadi argumen yang lemah. Ketika konten Anda sulit dibaca, maka Anda memiliki masalah lain selain menyimpan dua baris kode.
BlueWizard
12
@ jpmc26 Tidak setuju. Kawat gigi adalah kekacauan berlebihan dalam kondisi terpendek, satu-liner sesuai jawaban ini. Tanpa itu lebih elegan dan lebih mudah di mata. Mereka seperti operator kondisional ternary. Saya tidak pernah lupa menambahkan kawat gigi sambil memecahnya menjadi blok pernyataan yang lebih besar; Saya sudah menambahkan baris baru.
benxyzzy
3
Itu semua adalah preferensi gaya, tetapi jika seseorang membuat argumen bahwa itu lebih aman, saya akan tidak setuju dengan alasan itu. Saya sendiri lebih suka tanpa kawat gigi dan pada baris berikutnya, tetapi itu tidak berarti itu lebih atau kurang valid daripada gaya lainnya
phflack
49

Pertanyaan semacam ini telah memicu perdebatan hampir selama pemrograman berjalan. Untuk melemparkan topi saya ke atas ring, saya akan memilih versi dengan breaksyarat dan inilah alasannya (untuk kasus khusus ini):

The forloop hanya ada untuk menentukan nilai-nilai iterasi dalam lingkaran. Pengkodean kondisi melanggar dalam yang hanya mengacaukan logika IMO.

Memiliki terpisah breakmembuatnya sangat jelas bahwa selama pemrosesan normal, nilai-nilai seperti yang ditentukan dalam forloop dan pemrosesan harus dilanjutkan kecuali di mana kondisi tersebut masuk.

Sebagai sedikit latar belakang, saya mulai coding a break, kemudian meletakkan kondisi dalam forloop selama bertahun-tahun (di bawah arahan seorang programmer luar biasa) dan kemudian kembali ke breakgaya karena hanya terasa jauh lebih bersih (dan merupakan gaya yang berlaku di berbagai buku tebal kode yang saya konsumsi).

Ini adalah salah satu dari perang suci tersebut pada akhir hari - beberapa orang mengatakan Anda tidak boleh keluar dari loop secara artifisial, jika tidak itu bukan loop nyata. Lakukan apa pun yang menurut Anda dapat dibaca (baik untuk diri sendiri dan orang lain). Jika mengurangi penekanan tombol benar-benar sesuai keinginan Anda, main golf kode di akhir pekan - bir opsional.

Robbie Dee
sumber
3
Jika kondisi memiliki nama yang bagus, seperti "ditemukan" (mis. Anda sedang mencari sesuatu di array), maka itu adalah ide yang baik untuk meletakkannya di kepala for loop.
user949300
1
Saya diberitahu bahwa forloop digunakan ketika jumlah iterasi diketahui (misalnya ketika tidak ada kondisi kerusakan kecuali akhir array / daftar) dan whilekapan tidak. Ini tampaknya menjadi kasus untuk whileloop. Jika keterbacaan menjadi perhatian, bagian idx+=1dalam loop jauh lebih bersih untuk dibaca daripada satu if/elseblok
Laiv
Anda tidak bisa begitu saja meletakkan kondisi break di loop jika ada kode yang mengikutinya, jadi setidaknya Anda harus menulis "jika (kondisi) {found = true; lanjut;}
gnasher729
Kami sangat terbiasa dengan for(int i=0; i<something;i++)loop sehingga saya kira separuh pengembang tidak begitu ingat bahwa forloop dapat digunakan secara berbeda, dan karenanya sulit memahami &&conditionbagian tersebut.
Ralf Kleberhoff
@RalfKleberhoff Memang. Saya telah melihat banyak pengembang baru yang terkejut bahwa ekspresi pernyataan tripart semuanya opsional. Saya bahkan tidak ingat kapan terakhir kali saya melihat for(;;)...
Robbie Dee
43

forloop adalah untuk iterasi lebih dari sesuatu 1 - mereka tidak hanya lol-mari-tarik-beberapa-hal-acak-dari-tubuh-dan-menempatkan-mereka-di-loop-header - tiga ekspresi memiliki sangat arti khusus:

  • Yang pertama adalah untuk menginisialisasi iterator . Ini bisa berupa indeks, pointer, objek iterasi atau apa pun - selama itu digunakan untuk iterasi .
  • Yang kedua adalah untuk memeriksa apakah kita mencapai akhir.
  • Yang ketiga adalah untuk memajukan iterator.

Idenya adalah untuk memisahkan penanganan iterasi ( cara mengulang) dari logika di dalam loop ( apa yang harus dilakukan di setiap iterasi). Banyak bahasa biasanya memiliki untuk-setiap loop yang membebaskan Anda dari rincian iterasi, tetapi bahkan jika bahasa Anda tidak memiliki konstruksi itu, atau jika itu tidak dapat digunakan dalam skenario Anda - Anda masih harus membatasi header loop untuk penanganan iterasi.

Jadi, Anda perlu bertanya pada diri sendiri - apakah kondisi Anda tentang penanganan iterasi atau tentang logika di dalam loop? Kemungkinannya, ini tentang logika di dalam loop - jadi harus diperiksa di dalam loop daripada di header.

1 Berbeda dengan loop lain, itu "menunggu" sesuatu terjadi. A for/ foreachloop harus memiliki konsep "daftar" item untuk diulangi - bahkan jika daftar itu malas atau tidak terbatas atau abstrak.

Idan Arye
sumber
5
Ini adalah pikiran pertama saya ketika saya membaca pertanyaan itu. Kecewa saya harus menelusuri setengah lusin jawaban untuk melihat seseorang mengatakannya
Nemo
4
Umm ... semua loop untuk iterasi - itulah arti kata "iterasi" :-). Saya berasumsi Anda bermaksud sesuatu seperti "iterasi di atas koleksi", atau "iterasi menggunakan iterator"?
psmears
3
@psmears OK, mungkin saya memilih kata yang salah - Bahasa Inggris bukan bahasa ibu saya, dan ketika saya memikirkan iterasi saya memikirkan "iteration over something". Saya akan memperbaiki jawabannya.
Idan Arye
Kasus menengah adalah di mana kondisinya seperti apa someFunction(array[i]). Sekarang kedua bagian dari kondisi tersebut adalah tentang hal yang Anda iterasi, dan masuk akal untuk menggabungkannya dengan &&header loop.
Barmar
Saya menghormati posisi Anda, tetapi saya tidak setuju. Saya pikir forloop pada hati adalah counter, bukan loop atas daftar, dan ini didukung oleh sintaks fordalam bahasa sebelumnya (bahasa ini sering memiliki for i = 1 to 10 step 2sintaks, dan stepperintah - bahkan memungkinkan nilai awal yang bukan indeks dari yang pertama elemen - petunjuk pada konteks numerik, bukan konteks daftar). Satu- satunya kasus penggunaan yang saya pikir mendukung forloop sebagai hanya iterasi pada daftar adalah paralelisasi, dan yang memerlukan sintaks eksplisit sekarang pula agar tidak melanggar kode melakukan, misalnya, semacam.
Logan Pickup
8

Saya sarankan menggunakan versi dengan if (condition). Ini lebih mudah dibaca dan lebih mudah untuk di-debug. Menulis 3 baris kode tambahan tidak akan merusak jari Anda, tetapi akan memudahkan orang berikutnya untuk memahami kode Anda.

Aleksander
sumber
1
Hanya jika (seperti telah dikomentari) blok loop tidak memiliki ratusan baris kode dan logika di dalamnya bukan ilmu roket. Saya pikir argumen Anda baik-baik saja, tetapi saya kehilangan sesuatu seperti naming thingsdengan benar untuk memahami apa yang terjadi dan ketika kondisi terputus terpenuhi.
Laiv
2
@ Longv Jika blok loop memiliki ratusan baris kode di dalamnya, maka masalah lebih besar daripada pertanyaan di mana menulis kondisi break.
Aleksander
Itu sebabnya saya menyarankan untuk mengontekkan jawabannya, karena itu tidak selalu benar.
Laiv
8

Jika Anda mengulangi koleksi di mana elemen-elemen tertentu harus dilewati, maka saya merekomendasikan opsi baru:

  • Saring koleksi Anda sebelum iterasi

Baik Java dan C # membuat ini relatif sepele untuk dilakukan. Saya tidak akan terkejut jika C ++ memiliki cara membuat iterator yang bagus untuk melewati elemen tertentu yang tidak berlaku. Tetapi ini hanya benar-benar pilihan jika bahasa pilihan Anda mendukungnya, dan alasan Anda menggunakannya breakadalah karena ada kondisi apakah Anda memproses suatu elemen atau tidak.

Kalau tidak, ada alasan yang sangat bagus untuk menjadikan kondisi Anda bagian dari for loop - dengan anggapan evaluasi awal itu benar.

  • Lebih mudah untuk melihat kapan loop berakhir lebih awal

Saya telah bekerja pada kode di mana for for loop Anda mengambil beberapa ratus baris dengan beberapa persyaratan dan beberapa matematika kompleks terjadi di sana. Masalah yang Anda hadapi adalah:

  • break perintah yang terkubur dalam logika sulit ditemukan dan terkadang mengejutkan
  • Debugging membutuhkan langkah melalui setiap iterasi dari loop untuk benar-benar memahami apa yang terjadi
  • Banyak kode tidak memiliki refactoring yang dapat dengan mudah dibalik tersedia atau unit test untuk membantu dengan kesetaraan fungsional setelah penulisan ulang

Dalam situasi itu saya merekomendasikan pedoman berikut dalam urutan pilihan:

  • Saring koleksi untuk menghilangkan kebutuhan breakjika mungkin
  • Buatlah bagian kondisional untuk kondisi loop
  • Tempatkan conditional dengan breakdi atas loop jika memungkinkan
  • Tambahkan komentar multi-baris yang menarik perhatian untuk istirahat, dan mengapa itu ada

Pedoman ini selalu tunduk pada kebenaran kode Anda. Pilih opsi yang paling mewakili niat dan tingkatkan kejelasan kode Anda.

Berin Loritsch
sumber
1
Sebagian besar jawaban ini baik-baik saja. Tapi ... Saya bisa melihat bagaimana memfilter koleksi dapat menghilangkan kebutuhan akan continuepernyataan, tapi saya tidak melihat bagaimana mereka banyak membantu break.
user949300
1
@ user949300 takeWhileFilter dapat menggantikan breakpernyataan. Jika Anda memiliki satu - Java misalnya hanya membuat mereka di Jave 9 (tidak bahwa itu yang sulit untuk mengimplementasikan sendiri)
Idan Arye
1
Tetapi di Jawa Anda masih bisa memfilter sebelum iterasi. Menggunakan stream (1,8 atau lebih baru) atau menggunakan Koleksi Apache (1,7 atau lebih lama).
Laiv
@IdanArye - ada add-on libraries yang menambahkan fasilitas seperti itu ke Java stream stream (mis. JOO-lambda )
Jules
@IdanArye tidak pernah mendengar takeWhile sebelum komentar Anda. Menautkan filter secara eksplisit ke pemesanan menganggap saya tidak biasa dan berkabut, karena dalam filter "normal" setiap elemen dapat mengembalikan benar atau salah, terlepas dari apa yang terjadi sebelum atau sesudahnya. Dengan cara ini Anda dapat menjalankan berbagai hal secara paralel.
user949300
8

Saya sangat menyarankan untuk mengambil pendekatan yang paling tidak mengejutkan kecuali Anda mendapatkan manfaat yang signifikan dengan melakukan sebaliknya.

Orang tidak membaca setiap huruf ketika membaca sebuah kata, dan mereka tidak membaca setiap kata saat membaca buku - jika mereka mahir membaca, mereka melihat garis besar kata dan kalimat dan membiarkan otak mereka mengisi sisanya. .

Jadi kemungkinan pengembang sesekali akan menganggap ini hanya standar untuk loop dan bahkan tidak melihatnya:

for(int i=0;i<array.length&&!condition;i++)

Jika Anda ingin menggunakan gaya itu, saya sarankan mengubah bagian-bagiannya for(int i=0;i<dan ;i++)memberi tahu otak pembaca bahwa ini adalah standar untuk loop.


Alasan lain untuk ikut if- breakadalah bahwa Anda tidak selalu dapat menggunakan pendekatan Anda dengan for-condition. Anda harus menggunakan if- breakketika kondisi break terlalu rumit untuk disembunyikan dalam forpernyataan, atau bergantung pada variabel yang hanya dapat diakses di dalam forloop.

for(int i=0;i<array.length&&!((someWidth.X==17 && y < someWidth.x/2) || (y == someWidth.x/2 && someWidth.x == 18);i++)

Jadi, jika Anda memutuskan untuk pergi bersama if- break, Anda terlindungi. Tetapi jika Anda memutuskan untuk menggunakan for-conditions, Anda harus menggunakan campuran if- breakdan for-conditions. Agar konsisten, Anda harus memindahkan kondisi bolak-balik antara for-kondisi dan if- breaksetiap kali kondisi berubah.

Peter
sumber
4

Transformasi Anda mengasumsikan bahwa apa pun conditionyang dievaluasi truesaat Anda memasuki loop. Kami tidak dapat mengomentari kebenaran dalam hal ini karena ini tidak didefinisikan.

Setelah berhasil "mengurangi baris kode" di sini, Anda dapat melanjutkan untuk melihat

for(int i=0;i<array.length;i++){
    // some other code
    if(condition){
        break;
    }
    // further code
}

dan menerapkan transformasi yang sama, tiba di

for(int i=0;i<array.length && !condition;i++){
    // some other code
    // further code
}

Yang merupakan transformasi tidak valid, karena Anda sekarang tanpa syarat melakukan di further codemana Anda sebelumnya tidak.

Skema transformasi yang jauh lebih aman adalah mengekstraksi some other codedan mengevaluasi conditionke fungsi terpisah

bool evaluate_condition(int i) // This is an horrible name, but I have no context to improve it
{
     // some other code
     return condition;
}

for(int i=0;i<array.length && !evaluate_condition(i);i++){
    // further code
}
Caleth
sumber
4

TL; DR Lakukan apa pun yang paling baik dibaca dalam keadaan khusus Anda

Mari kita ambil contoh ini:

for ( int i = 1; i < array.length; i++ ) 
{
    if(something) break;
    // perform operations
}

Dalam hal ini kita tidak ingin salah satu kode di dalam for loop dijalankan jika somethingitu benar, jadi memindahkan tes somethingke bidang kondisi adalah wajar.

for ( int i = 1; i < array.length && !something; i++ )

Kemudian lagi, tergantung pada apakah somethingdapat diatur truesebelum loop, tetapi tidak di dalamnya, ini bisa menawarkan lebih banyak kejelasan:

if(!something)
{
    for ( int i = 1; i < array.length; i++ )
    {...}
}

Sekarang bayangkan ini:

for ( int i = 1; i < array.length; i++ ) 
{
    // perform operations
    if(something) break;
    // perform more operations
}

Kami mengirim pesan yang sangat jelas di sana. Jika somethingmenjadi true saat memproses array maka abaikan seluruh operasi pada titik ini. Untuk memindahkan cek ke bidang kondisi yang perlu Anda lakukan:

for ( int i = 1; i < array.length && !something; i++ ) 
{
    // perform operations
    if(!something)
    {
        // perform more operations
    }
}

Bisa dibilang, "pesan" telah menjadi keruh, dan kami sedang memeriksa kondisi kegagalan di dua tempat - bagaimana jika kondisi kegagalan berubah dan kami lupa salah satu tempat?

Tentu saja ada banyak bentuk berbeda untuk loop dan kondisi, semua dengan nuansa mereka sendiri.

Tulis kode sehingga terbaca dengan baik (ini jelas agak subyektif) dan ringkas. Di luar seperangkat pedoman pengkodean yang kejam, semua "aturan" memiliki pengecualian. Tulis apa yang Anda rasa paling baik mengekspresikan pengambilan keputusan, dan meminimalkan peluang kesalahan di masa depan.

Grimm The Opiner
sumber
2

Meskipun mungkin menarik karena Anda melihat semua kondisi di header loop dan breakmungkin membingungkan di dalam blok besar, forloop adalah pola di mana sebagian besar programmer mengharapkan perulangan pada rentang tertentu.

Terutama karena Anda melakukannya, menambahkan kondisi istirahat tambahan dapat menyebabkan kebingungan dan lebih sulit untuk melihat apakah itu setara secara fungsional.

Seringkali alternatif yang baik adalah dengan menggunakan a whileatau do {} whileloop yang memeriksa kedua kondisi, dengan asumsi array Anda memiliki setidaknya satu elemen.

int i=0
do {
    // some other code
    i++; 
} while(i < array.length && condition);

Menggunakan loop yang hanya memeriksa kondisi dan tidak menjalankan kode dari header loop, Anda membuat sangat jelas ketika kondisi diperiksa dan apa kondisi aktual dan apa kode dengan efek samping.

allo
sumber
Meskipun pertanyaan ini sudah memiliki beberapa jawaban yang baik, saya ingin secara khusus mengungguli ini karena ini membuat poin penting: bagi saya, memiliki sesuatu selain dari pemeriksaan ikatan sederhana di for-loop ( for(...; i <= n; ...)) sebenarnya membuat kode lebih sulit untuk dibaca karena saya punya untuk berhenti dan berpikir tentang apa yang terjadi di sini dan mungkin melewatkan kondisi sama sekali pada pass pertama karena saya tidak berharap itu ada di sana. Bagi saya, sebuah forloop menyiratkan bahwa Anda melakukan looping pada beberapa rentang, jika ada kondisi keluar khusus itu harus dibuat eksplisit dengan if, atau Anda dapat menggunakan a while.
CompuChip
1

Ini buruk, karena kode dimaksudkan untuk dibaca oleh manusia - forloop non-idiomatis sering membingungkan untuk dibaca, yang berarti bug lebih mungkin bersembunyi di sini, tetapi kode asli mungkin dapat ditingkatkan sambil tetap mempertahankannya. pendek dan mudah dibaca.

Jika yang Anda inginkan adalah menemukan nilai dalam array (contoh kode yang disediakan agak umum, tetapi mungkin juga merupakan pola ini), Anda bisa secara eksplisit mencoba menggunakan fungsi yang disediakan oleh bahasa pemrograman khusus untuk tujuan itu. Misalnya (pseudo-code, karena bahasa pemrograman tidak ditentukan, solusinya bervariasi tergantung pada bahasa tertentu).

array.find(item -> item.valid)

Ini seharusnya mempersingkat solusi sambil menyederhanakannya ketika kode Anda mengatakan secara spesifik apa yang Anda butuhkan.

Konrad Borowski
sumber
1

Beberapa alasan menurut saya yang mengatakan, Anda tidak boleh.

  1. Ini mengurangi keterbacaan.
  2. Outputnya mungkin tidak sama. Namun, itu dapat dilakukan dengan do...whileloop (tidak while) karena kondisinya diperiksa setelah beberapa eksekusi kode.

Tapi di atas itu, pertimbangkan ini,

for(int i=0;i<array.length;i++){

    // Some other code
    if(condition1){
        break;
    }

    // Some more code
    if(condition1){
        break;
    }

    // Some even more code
}

Sekarang, dapatkah Anda benar-benar mencapainya dengan menambahkan kondisi ini ke dalam forkondisi itu?

Keras
sumber
Apa itu " keterbukaan "? Apakah maksud Anda " opini "?
Peter Mortensen
Apa yang Anda maksud dengan "Beberapa alasan dalam keterbukaan yang mengatakan, Anda tidak boleh" ? Bisakah Anda menguraikan?
Peter Mortensen
0

Saya pikir menghapus breakbisa menjadi ide yang bagus, tetapi tidak untuk menyimpan baris kode.

Keuntungan dari menulis tanpa itu breakadalah bahwa post-kondisi loop jelas dan eksplisit. Ketika Anda menyelesaikan loop dan menjalankan baris berikutnya dari program, kondisi loop Anda i < array.length && !conditionpasti salah. Karena itu, ilebih besar dari atau sama dengan panjang array Anda, atau conditionbenar.

Dalam versi dengan break, ada gotcha yang tersembunyi. Kondisi loop mengatakan bahwa loop berakhir ketika Anda telah menjalankan beberapa kode lain untuk setiap indeks array yang valid, tetapi sebenarnya ada breakpernyataan yang dapat menghentikan loop sebelum itu, dan Anda tidak akan melihatnya kecuali Anda meninjau kode loop dengan sangat hati-hati. Dan penganalisa statis otomatis, termasuk pengoptimal pengoptimal, dapat mengalami kesulitan mendeduksi seperti apa kondisi post-loop juga.

Ini adalah alasan asli Edgar Dijkstra menulis "Goto Dianggap Berbahaya." Itu bukan masalah gaya: keluar dari lingkaran membuatnya sulit untuk secara formal beralasan tentang keadaan program. Saya telah menulis banyak break;pernyataan dalam hidup saya, dan sekali sebagai orang dewasa, saya bahkan menulis a goto(untuk keluar dari berbagai tingkatan dalam lingkaran kinerja-kritis dan kemudian melanjutkan dengan cabang lain dari pohon pencarian). Namun, saya jauh lebih mungkin untuk menulis returnpernyataan kondisional dari loop (yang tidak pernah menjalankan kode setelah loop dan oleh karena itu tidak dapat membersihkan post-kondisi) daripada a break.

Nota bene

Bahkan, refactoring kode untuk memberikan perilaku yang sama mungkin sebenarnya membutuhkan lebih banyak baris kode. Refactoring Anda dapat valid untuk beberapa kode lain tertentu atau jika kami dapat membuktikan bahwa kode tersebutcondition akan mulai salah. Tetapi contoh Anda setara dalam kasus umum untuk:

if ( array.length > 0 ) {
  // Some other code.
}
for ( int i = 1; i < array.length && !condition; i++ ) {
  // Some other code.
}

Jika beberapa kode lain bukan satu-liner, Anda mungkin kemudian memindahkannya ke suatu fungsi agar tidak mengulangi diri Anda sendiri, dan kemudian Anda hampir refactored loop Anda menjadi fungsi rekursif ekor.

Saya tahu ini bukan poin utama dari pertanyaan Anda, dan Anda menjaganya tetap sederhana.

Davislor
sumber
Masalahnya bukan bahwa istirahat membuatnya sulit untuk berdebat tentang kode, masalahnya adalah bahwa istirahat di tempat acak membuat kode menjadi rumit. Jika itu benar-benar diperlukan, maka tidak sulit untuk berdebat karena istirahat, tetapi karena kode perlu melakukan hal-hal rumit.
gnasher729
Inilah mengapa saya tidak setuju: jika Anda tahu tidak ada breakpernyataan, maka Anda tahu apa kondisi loop dan bahwa loop berhenti ketika kondisi itu salah. Jika ada break? Lalu ada beberapa cara loop bisa berakhir, dan dalam kasus umum, mencari tahu kapan salah satu dari mereka bisa menghentikan loop secara harfiah memecahkan Masalah Berhenti. Dalam praktiknya, kekhawatiran saya yang lebih besar adalah bahwa loop itu terlihat seperti melakukan satu hal, tetapi sebenarnya, siapa pun yang menulis kode setelah loop mengabaikan kasus tepi yang terkubur dalam logika kompleksnya. Hal-hal dalam kondisi loop sulit untuk dilewatkan.
Davislor
0

Ya, tetapi sintaksis Anda tidak konvensional dan karenanya buruk (tm)

Semoga bahasa Anda mendukung sesuatu seperti

while(condition) //condition being a condition which if true you want to continue looping
{
    do thing; //your conditionally executed code goes here
    i++; //you can use a counter if you want to reference array items in order of index
}
Ewan
sumber
2
mungkin saya seharusnya menggunakan kata yang berbeda untuk 'kondisi'. Saya tidak bermaksud secara harfiah berarti kondisi yang sama persis dalam contoh
Ewan
1
Tidak hanya untuk loop bahkan tidak jauh tidak konvensional, sama sekali tidak ada alasan sementara loop dari bentuk ini lebih baik daripada setara untuk loop. Bahkan kebanyakan orang akan mengatakan itu lebih buruk, seperti penulis Pedoman Inti CPP
Sean Burton
2
Beberapa orang hanya membenci alternatif kreatif. Atau melihat masalah dengan cara yang berbeda dari yang ada di kepala mereka. Atau lebih umum, penilaian cerdas. Aku merasakan sakit kesakitanmu!
Martin Maat
1
@sean maaf kawan, saya tahu orang-orang benci diberi tahu bahwa mereka salah, tetapi saya merujuk Anda ke es.77
Ewan
2
Ini whilemenekankan monster dalam kode - pengecualian yang tersembunyi di kode rutin / biasa. Logika bisnis ada di depan dan di tengah, mekanika perulangan dimasukkan. Ini menghindari "tunggu sebentar ..." reaksi terhadap foralternatif loop. Jawaban ini memperbaiki masalah. suara sepenuhnya.
radarbob
0

Jika Anda khawatir tentang forpernyataan yang terlalu rumit yang sulit dibaca, itu jelas merupakan keprihatinan yang valid. Membuang-buang ruang vertikal juga merupakan masalah (meskipun tidak terlalu kritis).

(Secara pribadi saya tidak menyukai aturan bahwa ifpernyataan harus selalu memiliki kawat gigi, yang sebagian besar merupakan penyebab ruang vertikal yang terbuang di sini. Perhatikan bahwa masalahnya adalah membuang - buang ruang vertikal. Menggunakan ruang vertikal dengan garis-garis yang berarti tidak boleh menjadi masalah.)

Salah satu opsi adalah untuk membaginya menjadi beberapa baris. Ini jelas yang harus Anda lakukan jika Anda menggunakan forpernyataan yang sangat kompleks , dengan banyak variabel dan ketentuan. (Ini bagi saya penggunaan ruang vertikal yang lebih baik, memberikan sebanyak mungkin garis sesuatu yang bermakna.)

for (
   int i = 0, j = 0;
   i < array.length && !condition;
   i++, j++
) {
   // stuff
}

Pendekatan yang lebih baik mungkin dengan mencoba menggunakan bentuk yang lebih deklaratif dan kurang imperatif. Ini akan bervariasi tergantung pada bahasanya, atau Anda mungkin perlu menulis fungsi Anda sendiri untuk mengaktifkan hal semacam ini. Itu juga tergantung apa yang conditionsebenarnya. Agaknya itu harus dapat berubah entah bagaimana, tergantung pada apa yang ada dalam array.

array
.takeWhile(condition)  // condition can be item => bool or (item, index) => bool
.forEach(doSomething); // doSomething can be item => void or (item, index) => void
Dave Cousineau
sumber
Memisahkan pernyataan yang rumit atau tidak biasa formenjadi beberapa baris adalah ide terbaik dalam seluruh diskusi ini
user949300
0

Satu hal yang dilupakan: Ketika saya putus dari satu loop (tidak melakukan semua iterasi yang mungkin, tidak peduli bagaimana diterapkan), itu biasanya terjadi bahwa saya tidak hanya ingin keluar dari loop, saya juga ingin tahu mengapa ini terjadi . Misalnya, jika saya mencari item X, saya tidak hanya berhenti dari loop, saya juga ingin tahu bahwa X ditemukan dan di mana itu.

Dalam situasi itu, memiliki kondisi di luar loop cukup alami. Jadi saya mulai dengan

bool found = false;
size_t foundIndex;

dan dalam loop saya akan menulis

if (condition) {
    found = true;
    foundIndex = i;
    break;
}

dengan for loop

for (i = 0; i < something && ! found; ++i)

Sekarang memeriksa kondisi dalam loop cukup alami. Apakah ada pernyataan "break" tergantung pada apakah ada proses lebih lanjut dalam loop yang ingin Anda hindari. Jika beberapa pemrosesan diperlukan dan pemrosesan lainnya tidak, Anda mungkin memiliki sesuatu seperti

if (! found) { ... }

suatu tempat di loop Anda.

gnasher729
sumber