Bisakah saya menggunakan istirahat untuk keluar dari banyak loop 'untuk' bersarang?

304

Apakah mungkin untuk menggunakan breakfungsi untuk keluar dari beberapa forloop bersarang ?

Jika demikian, bagaimana Anda akan melakukan ini? Bisakah Anda juga mengontrol berapa banyak loop breakkeluar?

Faken
sumber
1
Alih-alih menggunakan break atau goto untuk keluar dari beberapa loop bersarang, Anda dapat menyertakan logika tertentu dalam suatu fungsi dan menggunakan kembali untuk keluar dari beberapa loop bersarang. Ini akan mempertahankan estetika kode Anda dan akan mencegah Anda menggunakan goto yang merupakan praktik pemrograman yang buruk.
Rishab Shinghal

Jawaban:

239

AFAIK, C ++ tidak mendukung loop penamaan, seperti Java dan bahasa lainnya. Anda dapat menggunakan goto, atau membuat nilai bendera yang Anda gunakan. Pada akhir setiap loop periksa nilai flag. Jika disetel ke true, maka Anda dapat keluar dari iterasi itu.

Cullen Walsh
sumber
317
Jangan takut untuk menggunakan gotojika itu pilihan terbaik.
jkeys
18
Saya seorang programmer C ++ baru (dan satu tanpa pelatihan pemrograman formal) sehingga setelah membaca tentang kata-kata kasar orang pada goto. Saya ragu menggunakannya karena takut program saya tiba-tiba akan meledak dan membunuh saya. Selain itu, ketika saya biasa menulis program pada ti-83 saya (dalam kelas matematika yang membosankan tentu saja), fungsi-fungsi yang disediakan oleh editor dasar memerlukan penggunaan goto's.
Faken
26
@ Palsu: Dua jenis pemrogram gunakan goto: Pemrogram yang buruk, dan pemrogram yang pragmatis. Yang pertama cukup jelas. Yang terakhir, yang akan Anda cocokkan jika Anda memilih untuk menggunakannya dengan baik, menggunakan konsep yang disebut "jahat" ketika itu adalah yang lebih rendah dari (dua) kejahatan. Baca ini untuk pemahaman yang lebih baik tentang beberapa konsep C ++ yang mungkin perlu Anda gunakan dari waktu ke waktu (makro, goto, preprocessor, array): parashift.com/c++-faq-lite/big-picture.html#faq-6.15
jkeys
41
@ Palsu: Tidak ada yang salah dengan menggunakan goto. Ini menyalahgunakan goto yang merepotkan.
Semua orang
28
@ Hooked: Itu benar, kecuali bahwa gotojarang menggunakan adalah pilihan terbaik. Mengapa tidak menempatkan loop ke dalam fungsinya sendiri ( inline, jika Anda khawatir tentang kecepatan) dan returndari sini?
sbi
265

Tidak, jangan merusaknya dengan break. Ini adalah benteng terakhir yang tersisa untuk penggunaan goto.

Henk Holterman
sumber
19
Standar pengkodean MISRA C ++ memungkinkan penggunaan goto untuk mencakup situasi yang tepat ini.
Richard Corden
11
Pemrogram yang sebenarnya tidak takut menggunakan goto. Mengerjakannya selama 25 tahun - tanpa penyesalan - menghemat banyak waktu pengembangan.
Doug Null
1
Saya setuju. gotos adalah suatu keharusan jika Anda tidak memiliki 8K piksel, dan perlu memanggil urutan 30 panggilan OS Windows.
Michaël Roy
Atau "pengembalian" sederhana dengan meletakkan kode dalam suatu fungsi.
Tara
68

Hanya untuk menambahkan jawaban eksplisit menggunakan lambdas:

  for (int i = 0; i < n1; ++i) {
    [&] {
      for (int j = 0; j < n2; ++j) {
        for (int k = 0; k < n3; ++k) {
          return; // yay we're breaking out of 2 loops here
        }
      }
    }();
  }

Tentu saja pola ini memiliki batasan tertentu dan jelas C ++ 11 saja, tetapi saya pikir ini cukup berguna.

Predelnik
sumber
7
Ini mungkin membingungkan pembaca untuk berpikir bahwa pengembalian menyebabkan fungsi lambda kembali, dan bukan lambda itu sendiri.
Xunie
3
@Xunie: Jika loop Anda sangat rumit, sehingga Anda lupa bahwa Anda berada di lambda, sekarang saatnya untuk menempatkannya dalam fungsi yang berbeda, tetapi untuk kasus sederhana, ini seharusnya bekerja dengan cukup baik.
MikeMB
17
Saya pikir solusi ini indah
tuket
Anda dapat menangkap lambda dalam template RIIA yang memberinya nama dan menyebutnya dalam dtor (alias lingkup gaurd). Ini tidak akan menambah perolehan kinerja apa pun, tetapi dapat meningkatkan keterbacaan dan mengurangi risiko kehilangan tanda kurung fungsi.
Merah. Gelombang
56

Pendekatan lain untuk keluar dari nested loop adalah memfaktorkan kedua loop ke fungsi yang terpisah, dan returndari fungsi itu saat Anda ingin keluar.

Tentu saja, ini memunculkan argumen lain apakah Anda harus secara eksplisit returndari suatu fungsi selain dari pada bagian akhir.

Greg Hewgill
sumber
7
Itu masalah C. Dengan RIAA, pengembalian awal bukan masalah karena semua masalah yang terkait dengan pengembalian awal ditangani dengan benar.
Martin York
4
Saya mengerti bahwa aplikasi RIAA yang tepat dapat memecahkan masalah pembersihan sumber daya di C ++, tetapi saya telah melihat argumen filosofis terhadap pengembalian awal terus di lingkungan dan bahasa lain. Satu sistem saya bekerja di mana standar pengkodean melarang pengembalian awal memiliki fungsi yang dikotori dengan variabel boolean (dengan nama seperti continue_processing) yang mengontrol pelaksanaan blok kode lebih jauh dalam fungsi.
Greg Hewgill
21
Apa itu RIAA? Apakah itu seperti RAII? = D
jkeys
1
Tergantung berapa banyak untuk loop yang dia punya dan seberapa dalam sarangnya ... apakah kamu mau pil biru atau pil merah?
Matt
34

break akan keluar hanya loop paling dalam yang berisi itu.

Anda dapat menggunakan goto untuk keluar dari sejumlah loop.

Tentu saja goto sering dianggap berbahaya .

apakah pantas menggunakan fungsi istirahat [...]?

Menggunakan break dan goto dapat mempersulit alasan tentang kebenaran suatu program. Lihat di sini untuk diskusi tentang ini: Dijkstra tidak gila .

Karl Voigtland
sumber
16
Sebuah jawaban yang bagus karena itu menjelaskan bahwa meme "kebanggaan berbahaya" sangat terkait dengan pernyataan "gangguan aliran kontrol yang bersifat umum". Tidak ada artinya mengatakan "goto berbahaya", dan kemudian berbalik dan merekomendasikan menggunakan breakatau return.
Pavel Minaev
6
@Pavel: breakdan returndapatkan keunggulannya gotokarena Anda tidak perlu mencari label untuk menemukan kemana mereka pergi. Ya, di bawahnya ada beberapa goto, tapi yang sangat terbatas. Mereka jauh lebih mudah diurai oleh otak pencocokan pola programmer daripada yang tidak dibatasi goto. Jadi IMO mereka lebih disukai.
sbi
@ Sbi: Benar, tetapi istirahat masih bukan bagian dari pemrograman terstruktur. Itu hanya ditoleransi lebih baik daripada a goto.
jkeys
2
@KarlVoigtland tautan Dijkstra sudah usang; ini tampaknya berfungsi: blog.plover.com/2009/07
Aaron Brager
3
Sama sekali tidak ada yang salah dengan menggunakan goto dalam situasi ini. Sebuah goto yang ditempatkan dengan baik adalah lompatan dan batas yang lebih baik dan lebih mudah dibaca daripada banyak solusi yang berkerut yang diusulkan.
James
22

Meskipun jawaban ini sudah dipresentasikan, saya pikir pendekatan yang baik adalah dengan melakukan hal berikut:

for(unsigned int z = 0; z < z_max; z++)
{
    bool gotoMainLoop = false;
    for(unsigned int y = 0; y < y_max && !gotoMainLoop; y++)
    {
        for(unsigned int x = 0; x < x_max && !gotoMainLoop; x++)
        {
                          //do your stuff
                          if(condition)
                            gotoMainLoop = true;
        }
    }

}
Scigor
sumber
5
yang bagus tapi masih belum bisa dibaca, saya lebih suka kebagian dalam hal itu
Петър Петров
2
ini membuat kode Anda "cukup" lambat karena gotoMainLoopdicentang setiap siklus
Thomas
1
Dalam hal ini, menggunakan nyata gotomembuat inti lebih mudah dibaca dan berkinerja lebih baik.
Петър Петров
20

Bagaimana dengan ini?

for(unsigned int i=0; i < 50; i++)
{
    for(unsigned int j=0; j < 50; j++)
    {
        for(unsigned int k=0; k < 50; k++)
        {
            //Some statement
            if (condition)
            {
                j=50;
                k=50;
            }
        }
    }
}
jebeaudet
sumber
2
pendekatan yang menarik tapi saya sangat suka cara ered @ inf.ig.sh menanganinya. untuk (unsigned int y = 0; y <y_max &&! gotoMainLoop; y ++).
Andy
15

Contoh kode menggunakan gotodan label untuk keluar dari loop bersarang:

for (;;)
  for (;;)
    goto theEnd;
theEnd:
Helio Santos
sumber
11

Salah satu cara yang bagus untuk keluar dari beberapa loop bersarang adalah untuk memperbaiki kode Anda menjadi sebuah fungsi:

void foo()
{
    for(unsigned int i=0; i < 50; i++)
    {
        for(unsigned int j=0; j < 50; j++)
        {
            for(unsigned int k=0; k < 50; k++)
            {
                // If condition is true
                return;
            }
        }
    }
}
Deqing
sumber
4
... yang bukan merupakan pilihan jika kita harus melewatkan 10-20 variabel untuk menumpuk susunan fungsi ini.
Петър Петров
1
@ ПетърПетров lalu cari lambda yang juga lebih baik karena Anda dapat mendefinisikannya tepat di mana Anda membutuhkannya.
DarioP
Memberi +1 untuk lambdas tetapi merombak inti mesin game di mana bahkan satu bingkai tumpukan masih menjadi hambatan. Maaf untuk mengatakannya, tetapi lambdas tidak begitu ringan setidaknya di MSVC 2010.
Петър Петров
@ ПетърПетров Kemudian ubah pasangan fungsi menjadi kelas, dan variabel tumpukan menjadi anggota pribadi.
Arthur Tacca
Ini hanya terlalu rumit kode yang sudah rumit :) Kadang-kadang, goto adalah satu-satunya solusi. Atau jika Anda menulis automata kompleks dengan dokumentasi "goto state X", maka goto sebenarnya membuat kode dibaca seperti yang tertulis dalam dokumen. Juga, C # dan go keduanya memiliki goto dengan tujuan: tidak ada bahasa yang turing-lengkap tanpa goto, dan gotos sering merupakan alat yang paling baik digunakan untuk menulis penerjemah menengah atau kode seperti perakitan.
Петър Петров
5

goto bisa sangat membantu untuk memecahkan loop bersarang

for (i = 0; i < 1000; i++) {
    for (j = 0; j < 1000; j++) {
        for (k = 0; k < 1000; k++) {
             for (l = 0; l < 1000; l++){
                ....
                if (condition)
                    goto break_me_here;
                ....
            }
        }
    }
}

break_me_here:
// Statements to be executed after code breaks at if condition
Azeemali Hashmani
sumber
3

The breakpernyataan mengakhiri pelaksanaan melampirkan terdekat do, for, switch, atau whilepernyataan yang muncul. Kontrol lolos ke pernyataan yang mengikuti pernyataan yang diakhiri.

dari msdn .

rampok
sumber
3

Saya pikir a gotovalid dalam keadaan ini:

Untuk mensimulasikan break/ continue, Anda ingin:

Istirahat

for ( ;  ;  ) {
    for ( ;  ;  ) {
        /*Code here*/
        if (condition) {
            goto theEnd;
        }
    }
}
theEnd:

Terus

for ( ;  ; ) {
    for ( ;  ;  ) {
        /*Code here*/
        if (condition) {
            i++;
            goto multiCont;
        }
    }
    multiCont:
}
JuliusAlphonso
sumber
"Lanjutkan" tidak akan berfungsi di sana, karena ekspresi iterasi dari loop pertama tidak akan dieksekusi
Fabio A.
Saya berasumsi bahwa iterator untuk loop pertama adalah i. Oleh karena itu i++sebelum goto
JuliusAlphonso
0

Bahasa lain seperti PHP menerima parameter untuk break (yaitu break 2;) untuk menentukan jumlah level loop bersarang yang ingin Anda hilangkan, namun C ++ tidak. Anda harus menyelesaikannya dengan menggunakan boolean yang Anda atur ke false sebelum loop, atur true dalam loop jika Anda ingin istirahat, ditambah istirahat bersyarat setelah loop bersarang, memeriksa apakah boolean disetel ke true dan hancurkan jika ya.

Patrick Glandien
sumber
0

Saya tahu ini posting lama. Tetapi saya akan menyarankan jawaban yang sedikit logis dan lebih sederhana.

for(unsigned int i=0; i < 50; i++)
    {
        for(unsigned int j=0; j < conditionj; j++)
        {
            for(unsigned int k=0; k< conditionk ; k++)
            {
                // If condition is true

                j= conditionj;
               break;
            }
        }
    }
Aditya Jagtap
sumber
3
Itu bukan solusi yang sangat scalable karena j = conditionjtidak akan berfungsi jika Anda memiliki predikat yang kompleks j < conditionj.
Sergey
0

Break sejumlah loop hanya dengan satu boolvariabel lihat di bawah:

bool check = true;

for (unsigned int i = 0; i < 50; i++)
{
    for (unsigned int j = 0; j < 50; j++)
    {
        for (unsigned int k = 0; k < 50; k++)
        {
            //Some statement
            if (condition)
            {
                check = false;
                break;
            }
        }
        if (!check)
        {
            break;
        }
    }
    if (!check)
    {
        break;
    }
}

Dalam kode ini kita break;semua loop.

vikas bansal
sumber
0

Saya tidak yakin apakah itu layak, tetapi Anda dapat meniru loop bernama Java dengan beberapa makro sederhana:

#define LOOP_NAME(name) \
    if ([[maybe_unused]] constexpr bool _namedloop_InvalidBreakOrContinue = false) \
    { \
        [[maybe_unused]] CAT(_namedloop_break_,name): break; \
        [[maybe_unused]] CAT(_namedloop_continue_,name): continue; \
    } \
    else

#define BREAK(name) goto CAT(_namedloop_break_,name)
#define CONTINUE(name) goto CAT(_namedloop_continue_,name)

#define CAT(x,y) CAT_(x,y)
#define CAT_(x,y) x##y

Contoh penggunaan:

#include <iostream>

int main()
{
    // Prints:
    // 0 0
    // 0 1
    // 0 2
    // 1 0
    // 1 1

    for (int i = 0; i < 3; i++) LOOP_NAME(foo)
    {
        for (int j = 0; j < 3; j++)
        {
            std::cout << i << ' ' << j << '\n';
            if (i == 1 && j == 1)
                BREAK(foo);
        }
    }
}

Contoh lain:

#include <iostream>

int main()
{
    // Prints: 
    // 0
    // 1
    // 0
    // 1
    // 0
    // 1

    int count = 3;
    do LOOP_NAME(foo)
    {
        for (int j = 0; j < 3; j++)
        {
            std::cout << ' ' << j << '\n';
            if (j == 1)
                CONTINUE(foo);
        }
    }
    while(count-- > 1);
}
HolyBlackCat
sumber
-1
  while (i<n) {
    bool shouldBreakOuter = false;
    for (int j=i + 1; j<n; ++j) {
      if (someCondition) {
          shouldBreakOuter = true;
      }
    }

    if (shouldBreakOuter == true)
      break;

  }
MEnnabah
sumber
-3

Anda bisa menggunakan try ... catch.

try {
    for(int i=0; i<10; ++i) {
        for(int j=0; j<10; ++j) {
            if(i*j == 42)
                throw 0; // this is something like "break 2"
        }
    }
}
catch(int e) {} // just do nothing
// just continue with other code

Jika Anda harus keluar dari beberapa loop sekaligus, seringkali merupakan pengecualian.

lawilog
sumber
1
Saya ingin tahu alasan mengapa jawaban ini mendapatkan banyak suara.
hkBattousai
6
@ hkBattousai Solusinya memiliki suara turun karena menggunakan pengecualian untuk mengontrol aliran eksekusi. Seperti namanya, pengecualian hanya boleh digunakan pada kasus luar biasa.
Helio Santos
4
@HelioSantos Bukankah ini situasi yang luar biasa dimana bahasa tidak memberikan solusi yang tepat?
hkBattousai
8
Pengecualiannya lambat.
Gordon
2
Dampak kinerja lemparan sangat besar untuk sesuatu yang bukan kesalahan yang tidak dapat dipulihkan 99% dari waktu.
Michaël Roy
-4

Melepaskan for-loop sedikit aneh bagi saya, karena semantik for-loop biasanya menunjukkan bahwa ia akan mengeksekusi beberapa kali. Namun, itu tidak buruk dalam semua kasus; jika Anda mencari sesuatu di koleksi dan ingin menghancurkan setelah Anda menemukannya, itu berguna. Melepaskan dari loop bersarang, bagaimanapun, tidak mungkin di C ++; itu dalam bahasa lain melalui penggunaan jeda berlabel. Anda bisa menggunakan label dan goto, tapi itu mungkin membuat Anda mulas di malam hari ..? Sepertinya pilihan terbaik.

Nick Lewis
sumber
11
Sama sekali tidak aneh. Jika Anda mengulangi koleksi untuk mencari sesuatu (dan tidak memiliki cara pencarian yang lebih cepat), tidak ada gunanya menyelesaikan loop. (sebagai salah satu contoh)
Joe