Jika saya memiliki for for yang bersarang di dalam yang lain, bagaimana saya bisa secara efisien keluar dari kedua loop (dalam dan luar) dengan cara tercepat yang mungkin?
Saya tidak ingin harus menggunakan boolean dan kemudian harus mengatakan pergi ke metode lain, tetapi hanya untuk mengeksekusi baris kode pertama setelah loop luar.
Apa cara cepat dan menyenangkan untuk melakukan ini?
Saya berpikir bahwa pengecualian tidak murah / hanya boleh dilemparkan dalam kondisi yang benar-benar luar biasa dll. Oleh karena itu saya tidak berpikir solusi ini akan baik dari perspektif kinerja.
Saya tidak merasa itu benar untuk mengambil keuntungan dari fitur-fitur baru di .NET (metode anon) untuk melakukan sesuatu yang sangat mendasar.
c#
for-loop
nested-loops
GurdeepS
sumber
sumber
Jawaban:
Ya,
goto
tapi itu jelek, dan tidak selalu mungkin. Anda juga dapat menempatkan loop ke dalam metode (atau metode anon) dan gunakanreturn
untuk keluar kembali ke kode utama.vs:
Perhatikan bahwa dalam C # 7 kita harus mendapatkan "fungsi lokal", yang (sintaks tbd dll) artinya harus berfungsi seperti:
sumber
goto
berbahaya per-se , sementara itu hanya adalah benar instrumen untuk melakukan beberapa hal, dan seperti banyak instrumen dapat disalahgunakan. Keengganan religiusgoto
ini terus terang sangat bodoh dan jelas tidak ilmiah.C # adaptasi pendekatan yang sering digunakan dalam C - set nilai variabel luar loop di luar kondisi loop (yaitu untuk loop menggunakan variabel int
INT_MAX -1
sering merupakan pilihan yang baik):Seperti dicatat dalam kode, kata
break
tidak akan secara ajaib melompat ke iterasi berikutnya dari loop luar - jadi jika Anda memiliki kode di luar loop dalam pendekatan ini memerlukan pemeriksaan lebih lanjut. Pertimbangkan solusi lain dalam kasus tersebut.Pendekatan ini bekerja dengan
for
danwhile
loop tetapi tidak berhasilforeach
. Jikaforeach
Anda tidak memiliki akses kode ke enumerator tersembunyi sehingga Anda tidak dapat mengubahnya (dan bahkan jika AndaIEnumerator
tidak memiliki metode "MoveToEnd").Ucapan terima kasih kepada penulis komentar inline:
i = INT_MAX - 1
saran oleh Metafor
/foreach
komentar oleh ygoe . Komentartepat
IntMax
oleh jmbpianotentang kode setelah loop dalam oleh blizpasta
sumber
Solusi ini tidak berlaku untuk C #
Untuk orang yang menemukan pertanyaan ini melalui bahasa lain, Javascript, Java, dan D memungkinkan jeda berlabel dan melanjutkan :
sumber
Gunakan pelindung yang cocok di lingkaran luar. Atur pelindung di loop dalam sebelum Anda istirahat.
Atau lebih baik lagi, abstraksi loop dalam menjadi metode dan keluar dari loop luar ketika itu kembali palsu.
sumber
Jangan mengutip saya tentang ini, tetapi Anda bisa menggunakan goto seperti yang disarankan dalam MSDN. Ada solusi lain, seperti termasuk bendera yang diperiksa di setiap iterasi dari kedua loop. Akhirnya Anda bisa menggunakan pengecualian sebagai solusi kelas berat untuk masalah Anda.
PERGI KE:
Kondisi:
Pengecualian:
sumber
Apakah mungkin untuk memperbaiki loop yang tersarang untuk menjadi metode pribadi? Dengan begitu Anda bisa 'kembali' keluar dari metode untuk keluar dari loop.
sumber
[&] { ... return; ... }();
Sepertinya saya suka orang sangat tidak menyukai
goto
pernyataan, jadi saya merasa perlu untuk meluruskan hal ini sedikit.Saya percaya 'emosi' yang dimiliki orang pada
goto
akhirnya bermuara pada pemahaman kode dan (kesalahpahaman) tentang kemungkinan implikasi kinerja. Sebelum menjawab pertanyaan, karena itu saya akan terlebih dahulu masuk ke beberapa detail tentang bagaimana itu dikompilasi.Seperti yang kita semua tahu, C # dikompilasi ke IL, yang kemudian dikompilasi ke assembler menggunakan kompiler SSA. Saya akan memberikan sedikit wawasan tentang bagaimana semua ini bekerja, dan kemudian mencoba menjawab pertanyaan itu sendiri.
Dari C # ke IL
Pertama kita membutuhkan sepotong kode C #. Mari kita mulai dari yang sederhana:
Saya akan melakukan langkah demi langkah untuk memberi Anda ide bagus tentang apa yang terjadi di bawah tenda.
Terjemahan pertama: dari
foreach
kefor
loop setara (Catatan: Saya menggunakan array di sini, karena saya tidak ingin masuk ke rincian IDisposable - dalam hal ini saya juga harus menggunakan IEnumerable):Terjemahan kedua:
for
danbreak
diterjemahkan ke dalam padanan yang lebih mudah:Dan terjemahan ketiga (ini setara dengan kode IL): kami mengubah
break
danwhile
menjadi cabang:Sementara kompiler melakukan hal-hal ini dalam satu langkah, itu memberi Anda wawasan tentang proses. Kode IL yang berevolusi dari program C # adalah terjemahan literal dari kode C # terakhir. Anda dapat melihat sendiri di sini: https://dotnetfiddle.net/QaiLRz (klik 'lihat IL')
Sekarang, satu hal yang Anda amati di sini adalah bahwa selama proses, kode menjadi lebih kompleks. Cara termudah untuk mengamati ini adalah dengan fakta bahwa kami membutuhkan lebih banyak kode untuk menyelesaikan hal yang sama. Anda juga mungkin berpendapat bahwa
foreach
,for
,while
danbreak
sebenarnya pendek tangan untukgoto
, yang sebagian benar.Dari IL ke Assembler
Kompiler. NET JIT adalah kompiler SSA. Saya tidak akan membahas semua detail formulir SSA di sini dan cara membuat kompiler yang mengoptimalkan, terlalu banyak, tetapi dapat memberikan pemahaman dasar tentang apa yang akan terjadi. Untuk pemahaman yang lebih dalam, yang terbaik adalah mulai membaca tentang mengoptimalkan kompiler (saya suka buku ini untuk pengantar singkat: http://ssabook.gforge.inria.fr/latest/book.pdf ) dan LLVM (llvm.org) .
Setiap kompiler yang mengoptimalkan bergantung pada fakta bahwa kode itu mudah dan mengikuti pola yang dapat diprediksi . Dalam kasus loop FOR, kami menggunakan teori grafik untuk menganalisis cabang, dan kemudian mengoptimalkan hal-hal seperti cycli di cabang kami (mis. Cabang mundur).
Namun, kami sekarang memiliki cabang ke depan untuk mengimplementasikan loop kami. Seperti yang mungkin sudah Anda duga, ini sebenarnya salah satu langkah pertama yang akan diperbaiki JIT, seperti ini:
Seperti yang Anda lihat, kita sekarang memiliki cabang terbelakang, yang merupakan lingkaran kecil kami. Satu-satunya hal yang masih jahat di sini adalah cabang yang kami dapatkan karena
break
pernyataan kami . Dalam beberapa kasus, kita dapat memindahkan ini dengan cara yang sama, tetapi dalam kasus lain tetap ada.Jadi mengapa kompiler melakukan ini? Nah, jika kita bisa membuka gulungannya, kita mungkin bisa mengubahnya. Kita bahkan mungkin dapat membuktikan bahwa hanya ada konstanta yang ditambahkan, yang berarti seluruh loop kita bisa menghilang ke udara tipis. Untuk meringkas: dengan membuat pola-pola yang dapat diprediksi (dengan membuat cabang-cabang dapat diprediksi), kita dapat membuktikan bahwa kondisi-kondisi tertentu bertahan dalam loop kita, yang berarti kita dapat melakukan sihir selama optimasi JIT.
Namun, cabang-cabang cenderung mematahkan pola-pola bagus yang dapat diprediksi itu, yang oleh karenanya merupakan sesuatu yang optimis. Hancurkan, lanjutkan, kebagian - mereka semua berniat untuk menghancurkan pola-pola yang dapat diprediksi ini - dan karenanya tidak benar-benar 'baik'.
Anda juga harus menyadari pada titik ini bahwa yang sederhana
foreach
lebih dapat diprediksi daripada sekelompokgoto
pernyataan yang tersebar di semua tempat. Dalam hal (1) keterbacaan dan (2) dari perspektif pengoptimal, keduanya merupakan solusi yang lebih baik.Hal lain yang patut disebutkan adalah sangat relevan untuk mengoptimalkan kompiler untuk menetapkan register ke variabel (proses yang disebut alokasi register ). Seperti yang mungkin Anda ketahui, hanya ada sejumlah register yang terbatas di CPU Anda dan mereka adalah bagian memori tercepat di perangkat keras Anda. Variabel yang digunakan dalam kode yang berada di loop paling dalam, lebih mungkin untuk mendapatkan register yang ditugaskan, sedangkan variabel di luar loop Anda kurang penting (karena kode ini mungkin lebih sedikit hit).
Bantuan, terlalu banyak kerumitan ... apa yang harus saya lakukan?
Intinya adalah bahwa Anda harus selalu menggunakan konstruksi bahasa yang Anda miliki, yang biasanya (secara implisit) membangun pola yang dapat diprediksi untuk kompiler Anda. Cobalah untuk menghindari cabang aneh jika mungkin (khusus:
break
,continue
,goto
ataureturn
di tengah-tengah tidak ada).Kabar baiknya di sini adalah bahwa pola yang dapat diprediksi ini mudah dibaca (untuk manusia) dan mudah dikenali (untuk penyusun).
Salah satu pola itu disebut SESE, yang merupakan kependekan dari Single Entry Single Exit.
Dan sekarang kita sampai pada pertanyaan sebenarnya.
Bayangkan Anda memiliki sesuatu seperti ini:
Cara termudah untuk membuat ini menjadi pola yang dapat diprediksi adalah dengan hanya menghilangkan
if
sepenuhnya:Dalam kasus lain, Anda juga dapat membagi metode menjadi 2 metode:
Variabel sementara? Baik, buruk, atau jelek?
Anda bahkan mungkin memutuskan untuk mengembalikan boolean dari dalam loop (tapi saya pribadi lebih suka bentuk SESE karena itulah bagaimana kompiler akan melihatnya dan saya pikir lebih bersih untuk membaca).
Beberapa orang berpikir itu lebih bersih untuk menggunakan variabel sementara, dan mengusulkan solusi seperti ini:
Saya pribadi menentang pendekatan ini. Lihat lagi bagaimana kode dikompilasi. Sekarang pikirkan apa yang akan dilakukan dengan pola yang bagus dan dapat diprediksi ini. Dapatkan fotonya?
Benar, izinkan saya mengejanya. Apa yang akan terjadi adalah:
more
variabel aneh yang hanya digunakan dalam aliran kontrol.more
akan dihilangkan dari program, dan hanya cabang yang tersisa. Cabang-cabang ini akan dioptimalkan, sehingga Anda hanya akan mendapatkan satu cabang saja dari loop dalam.more
pasti digunakan dalam loop paling dalam, jadi jika kompiler tidak akan mengoptimalkannya, ia memiliki peluang besar untuk dialokasikan ke register (yang memakan memori register yang berharga).Jadi, untuk meringkas: pengoptimal dalam kompiler Anda akan mengalami banyak kesulitan untuk mencari tahu yang
more
hanya digunakan untuk aliran kontrol, dan dalam skenario kasus terbaik akan menerjemahkannya ke cabang tunggal di luar luar untuk lingkaran.Dengan kata lain, skenario kasus terbaik adalah bahwa skenario itu akan berakhir dengan yang setara dengan ini:
Pendapat pribadi saya tentang ini cukup sederhana: jika ini yang kami maksudkan selama ini, mari kita buat dunia lebih mudah untuk kompiler dan keterbacaan, dan segera tulis itu.
tl; dr:
Intinya:
goto
ataubool more
, lebih suka yang pertama.sumber
yield return
. Yaitu, walaupun mudah untuk menunjukkan ada beberapa kasus yang berguna, untuk sebagian besar kode "tingkat aplikasi" itu mungkin merupakan kejadian frustrasi yang sangat rendah dengan C #, tidak termasuk yang "baru datang dari" C / C ++ ;-) Saya juga kadang-kadang kehilangan "Lempar" Ruby (non-Exception unwinding) kadang-kadang juga cocok dengan domain ini.goto
, tidak membuat kode Anda lebih mudah dibaca - saya berpendapat apa yang terjadi adalah sebaliknya: sebenarnya aliran kontrol Anda akan mengandung lebih banyak node - yang cukup banyak definisi kompleksitas. Hanya menghindarinya untuk membantu kedisiplinan kedengarannya kontra produktif dalam hal keterbacaan.goto
tidak memiliki disiplin untuk tidak menyalahgunakannya.faktor menjadi fungsi / metode dan menggunakan pengembalian awal, atau mengatur ulang loop Anda menjadi klausa sementara. goto / pengecualian / apa pun tentu tidak pantas di sini.
sumber
Anda meminta kombinasi cepat, bagus, tidak menggunakan boolean, tidak menggunakan goto, dan C #. Anda telah mengesampingkan semua cara yang mungkin untuk melakukan apa yang Anda inginkan.
Cara paling cepat dan paling jelek adalah menggunakan goto.
sumber
Kadang-kadang bagus untuk mengabstraksi kode ke dalam fungsinya sendiri dan daripada menggunakan pengembalian awal - pengembalian awal itu jahat:)
sumber
Saya telah melihat banyak contoh yang menggunakan "break" tetapi tidak ada yang menggunakan "continue".
Itu masih akan membutuhkan semacam flag di loop dalam:
sumber
Sejak saya pertama kali melihat
break
di C beberapa dekade lalu, masalah ini telah membuat saya jengkel. Saya berharap beberapa peningkatan bahasa akan memiliki perpanjangan untuk istirahat yang akan berfungsi sebagai berikut:sumber
Saya ingat dari masa mahasiswa saya bahwa dikatakan secara matematis dapat dibuktikan bahwa Anda dapat melakukan apa pun dalam kode tanpa goto (yaitu tidak ada situasi di mana goto adalah satu-satunya jawaban). Jadi, saya tidak pernah menggunakan goto (hanya preferensi pribadi saya, tidak menyarankan bahwa saya benar atau salah)
Bagaimanapun, untuk keluar dari loop bersarang saya melakukan sesuatu seperti ini:
... saya harap itu membantu bagi mereka yang menyukai saya adalah "fanboys" anti-goto :)
sumber
Begitulah cara saya melakukannya. Masih ada solusi.
sumber
Cara termudah untuk mengakhiri loop ganda adalah langsung mengakhiri loop pertama
sumber
Loop dapat dipecah menggunakan kondisi khusus dalam loop, memungkinkan untuk memiliki kode bersih.
Dengan kode ini kami mendapatkan output berikut:
sumber
Pilihan lain adalah fungsi anonim yang dipanggil sendiri . Tidak ada goto, tidak ada label, tidak ada variabel baru, tidak ada fungsi-nama baru yang digunakan dan satu baris lebih pendek dari contoh metode anonim di bagian atas.
sumber
Lempar pengecualian khusus yang keluar dari loop outter.
Ini berfungsi untuk
for
,foreach
atauwhile
jenis loop apa saja dan bahasa apa pun yang menggunakantry catch exception
bloksumber
sumber
Seperti yang saya lihat Anda menerima jawaban di mana orang merujuk Anda pernyataan goto, di mana dalam pemrograman modern dan dalam pendapat ahli goto adalah pembunuh, kami menyebutnya pembunuh dalam pemrograman yang memiliki beberapa alasan tertentu, yang saya tidak akan membahasnya di sini pada titik ini, tetapi solusi dari pertanyaan Anda sangat sederhana, Anda dapat menggunakan bendera Boolean dalam skenario semacam ini seperti saya akan menunjukkannya dalam contoh saya:
sederhana dan polos. :)
sumber
Apakah Anda bahkan melihat
break
kata kunci? OoIni hanya kode semu, tetapi Anda harus dapat melihat apa yang saya maksud:
Jika Anda berpikir tentang
break
menjadi seperti fungsibreak()
, maka parameternya adalah jumlah loop yang harus dilepaskan. Karena kita berada di loop ketiga dalam kode di sini, kita dapat keluar dari ketiganya.Manual: http://php.net/break
sumber
Saya pikir kecuali Anda ingin melakukan "hal boolean" satu-satunya solusi sebenarnya adalah melempar. Yang jelas tidak seharusnya kamu lakukan ..!
sumber