Czy można użyć tej break
funkcji, aby wyjść z kilku zagnieżdżonych for
pętli?
Jeśli tak, jak byś to zrobił? Czy możesz również kontrolować, ile pętli break
wychodzi?
Czy można użyć tej break
funkcji, aby wyjść z kilku zagnieżdżonych for
pętli?
Jeśli tak, jak byś to zrobił? Czy możesz również kontrolować, ile pętli break
wychodzi?
Odpowiedzi:
AFAIK, C ++ nie obsługuje pętli nazewnictwa, podobnie jak Java i inne języki. Możesz użyć goto lub utworzyć używaną wartość flagi. Na końcu każdej pętli sprawdź wartość flagi. Jeśli jest ustawiony na true, możesz przerwać tę iterację.
goto
jeśli jest to najlepsza opcja.
goto
: złych programistów i programistów pragmatycznych. Te pierwsze są oczywiste. Ten ostatni, do którego pasowałbyś, gdybyś zdecydował się je dobrze wykorzystać, stosuje tak zwaną koncepcję „zła”, gdy jest to mniejsze (dwa) zło. Przeczytaj to, aby lepiej zrozumieć niektóre koncepcje języka C ++, które mogą być potrzebne od czasu do czasu (makra, goto's, preprocesor, tablice): parashift.com/c++-faq-lite/big-picture.html#faq-6.15
goto
rzadko jest najlepszą opcją. Dlaczego nie zastosować pętli do ich własnej funkcji ( inline
jeśli martwisz się szybkością) i return
od tego?
Nie, nie psuj tego za pomocą break
. To ostatnia zachowana warownia do użytku goto
.
Aby dodać jednoznaczną odpowiedź za pomocą 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
}
}
}();
}
Oczywiście ten wzorzec ma pewne ograniczenia i oczywiście tylko C ++ 11, ale myślę, że jest całkiem użyteczny.
Innym podejściem do wyrwania się z zagnieżdżonej pętli jest podzielenie obu pętli na osobną funkcję i return
od tej funkcji, gdy chcesz wyjść.
Oczywiście rodzi to kolejny argument, czy należy kiedykolwiek jawnie odwoływać się return
do funkcji w dowolnym miejscu innym niż na końcu.
continue_processing
), które kontrolowały wykonywanie bloków kodu w dalszej części funkcji.
break wyjdzie tylko z najbardziej wewnętrznej pętli ją zawierającej.
Możesz użyć goto, aby wyjść z dowolnej liczby pętli.
Oczywiście goto jest często uważane za szkodliwe .
czy właściwe jest użycie funkcji break [...]?
Korzystanie z break i goto może utrudnić wnioskowanie o poprawności programu. Zobacz tutaj dyskusję na ten temat: Dijkstra nie była szalona .
break
lub return
.
break
i return
mają tę przewagę goto
, że nie musisz szukać etykiety, aby znaleźć kierunek . Tak, pod nimi są jakieś goto
, ale bardzo ograniczone. Są one o wiele łatwiejsze do rozszyfrowania przez mózg dopasowujący wzorce programisty niż nieograniczony goto
. Więc IMO są lepsze.
goto
.
Chociaż ten answear został już zaprezentowany, myślę, że dobrym podejściem jest wykonanie następujących czynności:
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;
}
}
}
gotoMainLoop
jest sprawdzany co cykl
goto
czyni rdzeń bardziej czytelnym i wydajniejszym.
Co powiesz na to?
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;
}
}
}
}
Przykład kodu używającego goto
i etykiety do wyrwania się z zagnieżdżonej pętli:
for (;;)
for (;;)
goto theEnd;
theEnd:
Jednym z dobrych sposobów na wyrwanie się z kilku zagnieżdżonych pętli jest przefaktoryzowanie kodu na funkcję:
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;
}
}
}
}
goto może być bardzo pomocny w zrywaniu zagnieżdżonych pętli
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
Myślę, że a goto
jest ważne w takich okolicznościach:
Aby symulować break
/ continue
, chcesz:
for ( ; ; ) {
for ( ; ; ) {
/*Code here*/
if (condition) {
goto theEnd;
}
}
}
theEnd:
for ( ; ; ) {
for ( ; ; ) {
/*Code here*/
if (condition) {
i++;
goto multiCont;
}
}
multiCont:
}
i
. Stąd i++
przed goto
Inne języki, takie jak PHP, akceptują parametr break (tj. Break 2;), aby określić liczbę poziomów zagnieżdżonych pętli, z których chcesz się zerwać, C ++ jednak tego nie robi. Będziesz musiał to wypracować, używając logicznej wartości, którą ustawiłeś na false przed pętlą, ustaw na true w pętli, jeśli chcesz przerwać, oraz warunkową przerwę po zagnieżdżonej pętli, sprawdzając, czy logiczna została ustawiona na true i złamać, jeśli tak.
Wiem, że to stary post. Sugerowałbym jednak nieco logiczną i prostszą odpowiedź.
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;
}
}
}
j = conditionj
nie zadziała, jeśli zamiast tego masz złożony predykat j < conditionj
.
Przerwij dowolną liczbę pętli za pomocą tylko jednej bool
zmiennej, patrz poniżej:
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;
}
}
W tym kodzie mamy break;
wszystkie pętle.
Nie jestem pewien, czy warto, ale możesz emulować nazwane pętle Java za pomocą kilku prostych makr:
#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
Przykładowe użycie:
#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);
}
}
}
Inny przykład:
#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);
}
Możesz użyć 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
Jeśli musisz wyrwać się z kilku pętli jednocześnie, często jest to i tak wyjątek.
Wyłamanie się z pętli for jest dla mnie trochę dziwne, ponieważ semantyka pętli for zwykle wskazuje, że wykona ona określoną liczbę razy. Jednak nie jest to złe we wszystkich przypadkach; jeśli szukasz czegoś w kolekcji i chcesz się zepsuć po jego znalezieniu, jest to przydatne. Przełamywanie zagnieżdżonych pętli nie jest jednak możliwe w C ++; jest w innych językach poprzez użycie przerwy oznaczonej etykietą. Możesz użyć etykiety i goto, ale może to spowodować zgagę w nocy ..? Wydaje się jednak najlepszą opcją.