Keluar dari loop bersarang


216

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.


Saya hanya ingin memastikan: mengapa Anda ingin melakukan ini?
Jon Limjap

3
Mengapa Anda tidak ingin menggunakan boolean? Apa yang salah dengan melakukan itu?
Anthony

Di VB.net Anda dapat membungkus pernyataan try / akhirnya (tidak ada tangkapan) di sekitar jumlah loop acak, lalu "exit try" akan keluar dari semuanya pada titik mana pun.
Brain2000

Jawaban:


206

Ya, gototapi itu jelek, dan tidak selalu mungkin. Anda juga dapat menempatkan loop ke dalam metode (atau metode anon) dan gunakan returnuntuk keluar kembali ke kode utama.

    // goto
    for (int i = 0; i < 100; i++)
    {
        for (int j = 0; j < 100; j++)
        {
            goto Foo; // yeuck!
        }
    }
Foo:
    Console.WriteLine("Hi");

vs:

// anon-method
Action work = delegate
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits anon-method
        }
    }
};
work(); // execute anon-method
Console.WriteLine("Hi");

Perhatikan bahwa dalam C # 7 kita harus mendapatkan "fungsi lokal", yang (sintaks tbd dll) artinya harus berfungsi seperti:

// local function (declared **inside** another method)
void Work()
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits local function
        }
    }
};
Work(); // execute local function
Console.WriteLine("Hi");

49
Dalam situasi seperti ini saya tidak berpikir menggunakan goto lebih buruk daripada penggunaan normal seperti break (toh keduanya sama-sama cabang tanpa syarat ke label, hanya saja dengan break label implisit).
Greg Beech

37
kadang-kadang goto lebih tidak jahat daripada alternatifnya
seanb

7
@BeowulfOF - break hanya akan keluar dari loop dalam, bukan loop dalam dan luar.
Greg Beech

36
Goto itu sendiri tidak jelek. Apa yang jelek adalah menyalahgunakan goto yang menghasilkan kode spaghetti. Menggunakan goto untuk keluar dari nested loop sangat bagus. Selain itu, perhatikan bahwa semua break, continue, dan return, dari sudut pandang pemrograman struktural, hampir tidak lebih baik daripada kebersamaan - pada dasarnya mereka adalah hal yang sama, hanya dalam kemasan yang lebih bagus. Itu sebabnya bahasa struktural murni (seperti Pascal asli) tidak memiliki ketiganya.
el.pescado

11
@ el.pescado benar-benar tepat: banyak coders telah menyesatkan untuk percaya gotoberbahaya per-se , sementara itu hanya adalah benar instrumen untuk melakukan beberapa hal, dan seperti banyak instrumen dapat disalahgunakan. Keengganan religius gotoini terus terang sangat bodoh dan jelas tidak ilmiah.
o0 '.

96

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 -1sering merupakan pilihan yang baik):

for (int i = 0; i < 100; i++)
{
    for (int j = 0; j < 100; j++)
    {
        if (exit_condition)
        {
            // cause the outer loop to break:
            // use i = INT_MAX - 1; otherwise i++ == INT_MIN < 100 and loop will continue 
            i = int.MaxValue - 1;
            Console.WriteLine("Hi");
            // break the inner loop
            break;
        }
    }
    // if you have code in outer loop it will execute after break from inner loop    
}

Seperti dicatat dalam kode, kata breaktidak 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 fordan whileloop tetapi tidak berhasil foreach. Jika foreachAnda tidak memiliki akses kode ke enumerator tersembunyi sehingga Anda tidak dapat mengubahnya (dan bahkan jika Anda IEnumeratortidak memiliki metode "MoveToEnd").

Ucapan terima kasih kepada penulis komentar inline:
i = INT_MAX - 1saran oleh Meta
for / foreachkomentar oleh ygoe . Komentar
tepat IntMaxoleh jmbpiano
tentang kode setelah loop dalam oleh blizpasta


@ DR: Tidak akan bekerja di c # sebagai "Pernyataan istirahat mengakhiri loop terlampir terdekat atau pernyataan beralih di mana ia muncul." (
msdn

1
@blizpasta Jadi? Jika dia membuat kondisi pada loop luar salah (seperti yang dia lakukan), itu akan keluar keduanya.
Patrick

51
Anda harus menggunakan i = INT_MAX - 1; jika tidak i ++ == INT_MIN <100 dan loop akan terus berlanjut
Meta

6
ada ide di muka?
ktutnik

4
@ktutnik Ini tidak akan berfungsi dengan foreach karena Anda tidak akan memiliki akses kode ke enumerator tersembunyi. Juga IEnumerator tidak memiliki beberapa metode "MoveToEnd".
ygoe

39

Solusi ini tidak berlaku untuk C #

Untuk orang yang menemukan pertanyaan ini melalui bahasa lain, Javascript, Java, dan D memungkinkan jeda berlabel dan melanjutkan :

outer: while(fn1())
{
   while(fn2())
   {
     if(fn3()) continue outer;
     if(fn4()) break outer;
   }
}

40
yang menyedihkan ini tidak dapat dilakukan dengan c #, itu akan berkali-kali menghasilkan kode yang lebih bersih.
Rickard

17
Saya benar-benar menjadi bersemangat sejenak sampai saya menyadari ini BUKAN untuk c #. :(
Arvo Bowen

1
Konsep ini juga untuk PowerShell jika seseorang menemukan masalah di sana. (Mereka hanya meletakkan titik dua di depan nama label.) Sekarang saya tahu ini bukan PowerShell-spesifik ... Sintaks ini tidak akan sesuai dengan label goto yang tersedia di C #. PHP menggunakan sesuatu yang lain: break 3; Masukkan jumlah level setelah pernyataan break.
ygoe

1
Ini bukan java c #
Luca Ziegler

Bagi mereka yang ingin melihat fitur ini dalam C #, letakkan pujian Anda di sini, silakan 😉 github.com/dotnet/csharplang/issues/869#issuecomment-326606003
m93a

28

Gunakan pelindung yang cocok di lingkaran luar. Atur pelindung di loop dalam sebelum Anda istirahat.

bool exitedInner = false;

for (int i = 0; i < N && !exitedInner; ++i) {

    .... some outer loop stuff

    for (int j = 0; j < M; ++j) {

        if (sometest) {
            exitedInner = true;
            break;
        }
    }
    if (!exitedInner) {
       ... more outer loop stuff
    }
}

Atau lebih baik lagi, abstraksi loop dalam menjadi metode dan keluar dari loop luar ketika itu kembali palsu.

for (int i = 0; i < N; ++i) {

    .... some outer loop stuff

    if (!doInner(i, N, M)) {
       break;
    }

    ... more outer loop stuff
}

4
Kecuali OP yang mengatakan "Saya tidak ingin harus menggunakan boolean".
LeopardSkinPillBoxHat

Sangat Pascal-ish ... Saya mungkin lebih suka menggunakan goto, meskipun saya biasanya menghindari mereka seperti wabah.
Jonathan Leffler

21

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:

for ( int i = 0; i < 10; ++i ) {
   for ( int j = 0; j < 10; ++j ) {
      // code
      if ( break_condition ) goto End;
      // more code
   }
}
End: ;

Kondisi:

bool exit = false;
for ( int i = 0; i < 10 && !exit; ++i ) {
   for ( int j = 0; j < 10 && !exit; ++j ) {
      // code
      if ( break_condition ) {
         exit = true;
         break; // or continue
      }
      // more code
   }
}

Pengecualian:

try {
    for ( int i = 0; i < 10 && !exit; ++i ) {
       for ( int j = 0; j < 10 && !exit; ++j ) {
          // code
          if ( break_condition ) {
             throw new Exception()
          }
          // more code
       }
    }
catch ( Exception e ) {}

2
ini semua adalah solusi yang rapi di mana akan sangat bersih untuk hanya memasukkan faktor ke dalam metode dan menggunakan pengembalian awal
Dustin Getz

3
:) benar, itu adalah solusi sederhana, tetapi Anda harus memasukkan semua data lokal yang diperlukan ke dalam metode sebagai argumen ... Ini adalah salah satu dari sedikit tempat di mana goto mungkin menjadi solusi yang tepat
David Rodríguez - dribeas

Metode Kondisi bahkan tidak berfungsi karena "lebih banyak kode" akan dieksekusi satu kali setelah keluar dari loop dalam sebelum keluar dari loop luar. Metode GOTO bekerja tetapi melakukan persis apa yang dikatakan poster itu tidak ingin mereka lakukan. Metode Exception berfungsi tetapi lebih jelek dan lebih lambat daripada GOTO.
Pemrogram Windows

Saya akan menyoroti titik koma itu setelah label. Dengan cara ini label tersebut bisa berada di ujung blok. +1
Tamas Hegedus

@Windowsprogrammer OP bertanya, "Apa cara cepat dan menyenangkan untuk melakukan ini?" Goto adalah solusi yang lebih disukai: bersih dan ringkas, tanpa melibatkan metode terpisah.
Suncat2000

16

Apakah mungkin untuk memperbaiki loop yang tersarang untuk menjadi metode pribadi? Dengan begitu Anda bisa 'kembali' keluar dari metode untuk keluar dari loop.


Dengan manfaat sampingan membuat metode asli Anda lebih pendek :-)
Martin Capodici

C ++ 11 lambdas membuat ini mudah untuk beberapa kasus:[&] { ... return; ... }();
BCS

14

Sepertinya saya suka orang sangat tidak menyukai gotopernyataan, jadi saya merasa perlu untuk meluruskan hal ini sedikit.

Saya percaya 'emosi' yang dimiliki orang pada gotoakhirnya 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:

foreach (var item in array)
{
    // ... 
    break;
    // ...
}

Saya akan melakukan langkah demi langkah untuk memberi Anda ide bagus tentang apa yang terjadi di bawah tenda.

Terjemahan pertama: dari foreachke forloop setara (Catatan: Saya menggunakan array di sini, karena saya tidak ingin masuk ke rincian IDisposable - dalam hal ini saya juga harus menggunakan IEnumerable):

for (int i=0; i<array.Length; ++i)
{
    var item = array[i];
    // ...
    break;
    // ...
}

Terjemahan kedua: fordan breakditerjemahkan ke dalam padanan yang lebih mudah:

int i=0;
while (i < array.Length)
{
    var item = array[i];
    // ...
    break;
    // ...
    ++i;
}

Dan terjemahan ketiga (ini setara dengan kode IL): kami mengubah breakdan whilemenjadi cabang:

    int i=0; // for initialization

startLoop:
    if (i >= array.Length) // for condition
    {
        goto exitLoop;
    }
    var item = array[i];
    // ...
    goto exitLoop; // break
    // ...
    ++i;           // for post-expression
    goto startLoop; 

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, whiledan breaksebenarnya pendek tangan untuk goto, 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:

    int i=0; // for initialization

    if (i >= array.Length) // for condition
    {
        goto endOfLoop;
    }

startLoop:
    var item = array[i];
    // ...
    goto endOfLoop; // break
    // ...
    ++i;           // for post-expression

    if (i >= array.Length) // for condition
    {
        goto startLoop;
    }

endOfLoop:
    // ...

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 breakpernyataan 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 foreachlebih dapat diprediksi daripada sekelompok gotopernyataan 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, gotoatau returndi 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:

// a is a variable.

for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j)
  {
     // ...

     if (i*j > a) 
     {
        // break everything
     }
  }
}

Cara termudah untuk membuat ini menjadi pola yang dapat diprediksi adalah dengan hanya menghilangkan ifsepenuhnya:

int i, j;
for (i=0; i<100 && i*j <= a; ++i) 
{
  for (j=0; j<100 && i*j <= a; ++j)
  {
     // ...
  }
}

Dalam kasus lain, Anda juga dapat membagi metode menjadi 2 metode:

// Outer loop in method 1:

for (i=0; i<100 && processInner(i); ++i) 
{
}

private bool processInner(int i)
{
  int j;
  for (j=0; j<100 && i*j <= a; ++j)
  {
     // ...
  }
  return i*j<=a;
}

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:

bool more = true;
for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j) 
  {
     // ...
     if (i*j > a) { more = false; break; } // yuck.
     // ...
  }
  if (!more) { break; } // yuck.
  // ...
}
// ...

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:

  • Kompiler akan menuliskan semuanya sebagai cabang.
  • Sebagai langkah optimasi, kompiler akan melakukan analisis aliran data dalam upaya untuk menghapus morevariabel aneh yang hanya digunakan dalam aliran kontrol.
  • Jika berhasil, variabel moreakan dihilangkan dari program, dan hanya cabang yang tersisa. Cabang-cabang ini akan dioptimalkan, sehingga Anda hanya akan mendapatkan satu cabang saja dari loop dalam.
  • Jika tidak berhasil, variabel morepasti 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 morehanya 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:

for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j)
  {
     // ...
     if (i*j > a) { goto exitLoop; } // perhaps add a comment
     // ...
  }
  // ...
}
exitLoop:

// ...

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:

  • Gunakan kondisi sederhana di loop for Anda jika memungkinkan. Tetap berpegang pada konstruksi tingkat tinggi bahasa yang Anda miliki sebanyak mungkin.
  • Jika semuanya gagal dan Anda salah gotoatau bool more, lebih suka yang pertama.

OTOH: Saya jarang memiliki keinginan untuk memecahkan loop luar atau berharap untuk goto-equiv (dan banyak kode yang bisa dibilang bisa ditulis lebih bersih), meskipun seperti itu mungkin lebih umum di domain lain .. hari ini adalah kasus seperti itu, tapi itu sebagian besar disebabkan oleh 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.
user2864740

1
@ Suncat2000 Hanya memperkenalkan lebih banyak kode seperti variabel dan cabang kondisional untuk menghindari penggunaan 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.
atlaste

@laste: Maaf, izinkan saya menyatakan komentar saya lebih jelas. Apa yang seharusnya saya katakan adalah: Penjelasan yang bagus. Tetapi orang-orang yang menghindari kebo bukanlah tentang kinerja; ini tentang menghindari penyalahgunaan yang menyebabkan kurangnya keterbacaan. Kita dapat berasumsi bahwa orang yang menghindarinya gototidak memiliki disiplin untuk tidak menyalahgunakannya.
Suncat2000

@atlaste, bahasa apa itu "ackomplish"?
Pacerier

11

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.

def do_until_equal():
  foreach a:
    foreach b:
      if a==b: return

10

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.


Setuju. Memperkenalkan metode baru hanya untuk menyingkirkan satu goto itu konyol. Jika kompiler tidak dapat menyejajarkan panggilan metode itu dengan alasan apa pun - kita akan berakhir dengan overhead yang sama sekali tidak perlu dari panggilan metode tambahan. Melontarkan dan menangkap pengecualian hanya untuk memutus perulangan adalah lebih bertele-tele dan lebih mahal.
Zar Shardan

@ Program Windows: OP tidak meminta "no use of goto". Dia tidak ingin "pergi ke metode lain". Pertanyaannya adalah jauh dari mengesampingkan semua cara yang mungkin, tetapi Anda benar untuk menyiratkan bahwa kebagian terbaik di sini.
Suncat2000

5

Kadang-kadang bagus untuk mengabstraksi kode ke dalam fungsinya sendiri dan daripada menggunakan pengembalian awal - pengembalian awal itu jahat:)

public void GetIndexOf(Transform transform, out int outX, out int outY)
{
    outX = -1;
    outY = -1;

    for (int x = 0; x < Columns.Length; x++)
    {
        var column = Columns[x];

        for (int y = 0; y < column.Transforms.Length; y++)
        {
            if(column.Transforms[y] == transform)
            {
                outX = x;
                outY = y;

                return;
            }
        }
    }
}

1
tetapi kemudian ini memungkinkan solusi tradisional yang diminta oleh OP
Surya Pratap

2

Saya telah melihat banyak contoh yang menggunakan "break" tetapi tidak ada yang menggunakan "continue".

Itu masih akan membutuhkan semacam flag di loop dalam:

while( some_condition )
{
    // outer loop stuff
    ...

    bool get_out = false;
    for(...)
    {
        // inner loop stuff
        ...

        get_out = true;
        break;
    }

    if( get_out )
    {
        some_condition=false;
        continue;
    }

    // more out loop stuff
    ...

}

1

Sejak saya pertama kali melihat breakdi 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:

break; // our trusty friend, breaks out of current looping construct.
break 2; // breaks out of the current and it's parent looping construct.
break 3; // breaks out of 3 looping constructs.
break all; // totally decimates any looping constructs in force.

3
Kemudian programmer pemeliharaan akan memasukkan level lain dari sarang, akan memperbaiki beberapa pernyataan istirahat, dan akan memecahkan beberapa pernyataan istirahat lainnya. Cara mengatasinya adalah dengan membuka label. Itu benar-benar telah diusulkan, tetapi para pragmatis menggunakan label goto sebagai gantinya.
Pemrogram Windows

Tunggu, siapa yang melakukan pemrograman pemeliharaan lagi? :)
Jesse C. Slicer

2
JavaScript bahkan telah mencantumkan blok / break statement. devguru.com/Technologies/ecmascript/quickref/break.html
David Grant

@ Chris Bartow: keren! membuat Natal saya :) @ David Grant: sepertinya JS break == C's goto?
Jesse C. Slicer

D telah memberi label break / continue
BCS

0

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:

var isDone = false;
for (var x in collectionX) {
    for (var y in collectionY) {
        for (var z in collectionZ) {
            if (conditionMet) {
                // some code
                isDone = true;
            }
            if (isDone)
                break;
        }
        if (isDone) 
            break;
    }
    if (isDone)
        break;
}

... saya harap itu membantu bagi mereka yang menyukai saya adalah "fanboys" anti-goto :)


Maaf untuk memberitahu Anda, tetapi dosen Anda yang harus disalahkan atas kondisi Anda. Jika dia repot-repot memaksa Anda untuk belajar berkumpul, maka Anda tahu bahwa 'goto' hanya lompatan (tentu saja saya mengabaikan fakta bahwa ini adalah pertanyaan # ac).
cmroanirgo

0

Begitulah cara saya melakukannya. Masih ada solusi.

foreach (var substring in substrings) {
  //To be used to break from 1st loop.
  int breaker=1;
  foreach (char c in substring) {
    if (char.IsLetter(c)) {
      Console.WriteLine(line.IndexOf(c));
      \\setting condition to break from 1st loop.
      breaker=9;
      break;
    }
  }
  if (breaker==9) {
    break;
  }
}


0

Cara termudah untuk mengakhiri loop ganda adalah langsung mengakhiri loop pertama

string TestStr = "The frog jumped over the hill";
char[] KillChar = {'w', 'l'};

for(int i = 0; i < TestStr.Length; i++)
{
    for(int E = 0; E < KillChar.Length; E++)
    {
        if(KillChar[E] == TestStr[i])
        {
            i = TestStr.Length; //Ends First Loop
            break; //Ends Second Loop
        }
    }
}

Ini tidak berfungsi karena dengan menggunakan istirahat Anda akan mengakhiri lingkaran dalam. Lingkaran luar akan terus beralih.
Alex Leo

0

Loop dapat dipecah menggunakan kondisi khusus dalam loop, memungkinkan untuk memiliki kode bersih.

    static void Main(string[] args)
    {
        bool isBreak = false;
        for (int i = 0; ConditionLoop(isBreak, i, 500); i++)
        {
            Console.WriteLine($"External loop iteration {i}");
            for (int j = 0; ConditionLoop(isBreak, j, 500); j++)
            {
                Console.WriteLine($"Inner loop iteration {j}");

                // This code is only to produce the break.
                if (j > 3)
                {
                    isBreak = true;
                }                  
            }

            Console.WriteLine("The code after the inner loop will be executed when breaks");
        }

        Console.ReadKey();
    }

    private static bool ConditionLoop(bool isBreak, int i, int maxIterations) => i < maxIterations && !isBreak;   

Dengan kode ini kami mendapatkan output berikut:

  • Iterasi loop eksternal 0
  • Iterasi loop dalam 0
  • Iterasi loop dalam 1
  • Iterasi loop dalam 2
  • Iterasi loop dalam 3
  • Iterasi loop dalam 4
  • Kode setelah loop dalam akan dieksekusi ketika istirahat

0

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.

// self-invoked-anonymous-function
new Action(() =>
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits self invoked lambda expression
        }
    }
})();
Console.WriteLine("Hi");

-3

Lempar pengecualian khusus yang keluar dari loop outter.

Ini berfungsi untuk for, foreachatau whilejenis loop apa saja dan bahasa apa pun yang menggunakan try catch exceptionblok

try 
{
   foreach (object o in list)
   {
      foreach (object another in otherList)
      {
         // ... some stuff here
         if (condition)
         {
            throw new CustomExcpetion();
         }
      }
   }
}
catch (CustomException)
{
   // log 
}

-4
         bool breakInnerLoop=false
        for(int i=0;i<=10;i++)
        {
          for(int J=0;i<=10;i++)
          {
              if(i<=j)
                {
                    breakInnerLoop=true;
                    break;
                }
          }
            if(breakInnerLoop)
            {
            continue
            }
        }

Apa perbedaan mendasar dengan jawaban dviljoen?
Gert Arnold

Itu tidak akan berhasil, karena Anda tidak memeriksa kondisi "breakInnerLoop" di luar untuk loop, sehingga Anda hanya beralih ke loop berikutnya, Anda juga menulis j dan J, dan melewatkan titik koma, yang tidak akan dikompilasi.
Sebastian

-4

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:

            for (; j < 10; j++)
            {
                //solution
                bool breakme = false;
                for (int k = 1; k < 10; k++)
                {
                   //place the condition where you want to stop it
                    if ()
                    {
                        breakme = true;
                        break;
                    }
                }

                if(breakme)
                    break;
               }

sederhana dan polos. :)


baca pertanyaannya sebelum menyarankan istirahat. Karena itu downvotes.
cmroanirgo

1
baca sekarang, tetapi ketika saya memposting jawaban ini versi yang diedit dari tidak menggunakan boolean tidak ditambahkan ini adalah mengapa saya memposting jawaban ini .. tapi terima kasih untuk downvote!
Steve

1
Itu bukan masalah pribadi. Sayangnya, pemungutan suara sekarang terkunci;) Ini adalah bagian penting dari SO untuk mendapatkan jawaban terbaik ke atas (yang tidak selalu terjadi)
cmroanirgo

-6

Apakah Anda bahkan melihat breakkata kunci? Oo

Ini hanya kode semu, tetapi Anda harus dapat melihat apa yang saya maksud:

<?php
for(...) {
    while(...) {
        foreach(...) {
            break 3;
        }
    }
}

Jika Anda berpikir tentang breakmenjadi 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


13
Bukan pertanyaan php.
Zerga

-10

Saya pikir kecuali Anda ingin melakukan "hal boolean" satu-satunya solusi sebenarnya adalah melempar. Yang jelas tidak seharusnya kamu lakukan ..!

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.