Jak wyrwać się z zagnieżdżonych pętli?

98

Jeśli użyję breakinstrukcji, spowoduje to tylko przerwanie pętli wewnętrznej i muszę użyć flagi, aby przerwać pętlę zewnętrzną. Ale jeśli istnieje wiele zagnieżdżonych pętli, kod nie będzie dobrze wyglądał.

Czy jest inny sposób na przerwanie wszystkich pętli? (Proszę nie używać goto stmt.)

for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; j++) {
       if(condition) {
            // both of the loops need to break and control will go to stmt2
       }
   }

}

stmt2
user966379
źródło
2
możesz spróbować int i i int j przed rozpoczęciem pętli, a następnie pod warunkiem, że sprawią, że 1001 pętla nie będzie iterować następnej.
Khurram Ijaz
Wersja C ++: stackoverflow.com/questions/1257744/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Odpowiedzi:

45

Posługiwać się:

if (condition) {
    i = j = 1000;
    break;
}
Peter Mortensen
źródło
50
Działa, ale brzydko i nie jest ogólnie. Co jeśli ktoś zmieni limit na 2000 (przypuśćmy, że kod jest dłuższy, więc nie od razu to zauważysz)?
ugoren
1
@ugoren To także takie proste. co by było, gdybyś użył pliku const int count =1000 , w globalnej inicjalizacji. lub jako #definemakro.
Laksith
4
Jak wskazuje @ugoren, nie jest to ogólne rozwiązanie. Ponieważ jest to pierwszy hit Google na to pytanie, dobrze by było, gdyby wybrano ogólne rozwiązanie. Cóż, ludzie i tak są przyzwyczajeni do sprawdzania numeru 2.
BeeOnRope
1
Myślę, że potrzebuję tylko i = 1000?
Peter Wu
191

Nie, nie psuj zabawy plikiem break. To jest ostatnie ważne użycie goto;)

Jeśli nie to, możesz użyć flag, aby wyrwać się z głęboko zagnieżdżonych pętli.

Innym podejściem do wyrwania się z zagnieżdżonej pętli jest rozłożenie obu pętli na osobną funkcję i powrót z tej funkcji, gdy chcesz wyjść.

Podsumowanie - aby wyrwać się z zagnieżdżonych pętli:

  1. posługiwać się goto
  2. użyj flag
  3. rozróżnij pętle na oddzielne wywołania funkcji

Nie mogłem się oprzeć włączeniu tutaj xkcd :)

wprowadź opis obrazu tutaj

źródło

Goto są uważane za szkodliwe, ale wiele osób w komentarzach sugeruje, że nie musi. Jeśli jest używany rozsądnie, może być świetnym narzędziem. Wszystko, co jest używane z umiarem, jest zabawne.

Srikar Appalaraju
źródło
30
Goto jest tak jasne, jak tu dotrzesz, tak. Ustawienie zmiennej wyjścia na 1000 jest jeszcze bardziej niezdarne.
correnos
3
Chciałbym dodać, że goto nie jest jawnie złe, po prostu można je wykorzystać do zła. Uważam, że istnieje kilka przypadków, na przykład ten, w których są one najlepszym rozwiązaniem. „Nie używaj goto” to dobry początek, ale myślę, że następny krok umiejętności pozwoli ci „Nie używaj znaków gotowych z dużej odległości”.
Aatch
1
Nie zgadzam się z tym: „Tworzenie funkcji powoduje wykładnicze ilości dodawania / odejmowania wskaźnika stosu”. Jeśli istnieje lokalna (statyczna) funkcja, która jest wywoływana tylko w jednym punkcie przebiegu programu, wstawi ją każdy w połowie przyzwoity kompilator, a wynikowy kod jest zasadniczo taki sam jak w przypadku goto. To prawdopodobnie najłatwiejszy przypadek optymalizacji dla dowolnego kompilatora.
DrV
1
Refaktoryzacja jest zwykle najczystszym rozwiązaniem. Jednak jeśli jakiekolwiek zmienne spoza pętli zostaną zmienione podczas wewnętrznej pętli, sprawy się skomplikują. Jedną z możliwości jest przekazanie zmiennej do funkcji wewnętrznej przez odniesienie (wskaźnik), ale może to zmylić optymalizację kompilatora i wygenerować niepotrzebny dodatkowy kod. Inną możliwością jest uczynienie takich zmiennych statycznymi na poziomie modułu, ale to też nie jest zbyt piękne. W C niestety brakuje funkcji zagnieżdżonych, ponieważ rozwiązałyby one ten problem - chyba że zechcesz powiązać się z użyciem gcc, które zapewnia rozszerzenie.
DrV
2
+1. A Structured ProgrammingWithGoToStatements Donalda E. Knutha z przejściem do instrukcji ( wiki.c2.com/?StructuredProgrammingWithGoToStatements ) jest interesującym artykułem przedstawiającym równowagę między Dijkstrą.
kmkaplan
41
bool stop = false;
for (int i = 0; (i < 1000) && !stop; i++)
{
    for (int j = 0; (j < 1000) && !stop; j++)
    {
        if (condition)
            stop = true;
    }
}
Tung
źródło
Rozwiązanie nadal zwiększa obie zmienne o jedną w przypadku przerwy, co może powodować problemy
TheSola10
8
Można ustawić "stop = true;" a następnie „złamać”. Następnie zaraz po zakończeniu wewnętrznej pętli „for” wykonaj „if (stop) break;”.
Jeff Grigg,
Myślę, że jest to najbardziej eleganckie gotorozwiązanie. Nie wymaga znajomości ograniczeń ii jwewnątrz stanu. Podoba mi się pomysł umieszczenia if (stop) break;po wewnętrznej pętli, która mogłaby się sama breakpo ustawieniu stop.
Willis Blackburn
35

Jednym ze sposobów jest umieszczenie wszystkich zagnieżdżonych pętli w funkcji i powrót z najbardziej wewnętrznej pętli na wypadek potrzeby wyrwania się ze wszystkich pętli.

function() 
{    
  for(int i=0; i<1000; i++)
  {
   for(int j=0; j<1000;j++)
   {
      if (condition)
        return;
   }
  }    
}
Sójka
źródło
2
wydaje mi się najlepszym rozwiązaniem dla mnie
Luca Steeb
21

Myślę, że gotorozwiąże problem

for(int i = 0; i < 1000; i++) {
    for(int j = 0; j < 1000; j++) {
        if (condition) {
            goto end;
        }
    }
}

end:
stmt2 

 
Renjith KN
źródło
@chikuba Otrzymałem odpowiedź z cprogramming.com/tutorial/goto.html, a Twoja odpowiedź nie jest publikowana, gdy robię to samo, dlatego nie widzę twojego postu
Renjith KN
16

Będziesz potrzebować zmiennej boolowskiej, jeśli chcesz, aby była czytelna:

bool broke = false;
for(int i = 0; i < 1000; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      broke = true;
      break;
    }
  }
  if (broke)
    break;
}

Jeśli chcesz, aby była mniej czytelna, możesz dołączyć do oceny boolowskiej:

bool broke = false;
for(int i = 0; i < 1000 && !broke; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      broke = true;
      break;
    }
  }
}

Ostatecznym sposobem na unieważnienie początkowej pętli jest:

for(int i = 0; i < size; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      i = size;
      break;
    }
  }
}
Jacek
źródło
4

Uwaga: ta odpowiedź pokazuje naprawdę niejasną konstrukcję.

Jeśli używasz GCC, sprawdź tę bibliotekę . Podobnie jak w PHP, breakmoże zaakceptować liczbę zagnieżdżonych pętli, które chcesz zakończyć. Możesz napisać coś takiego:

for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; j++) {
       if(condition) {
            // break two nested enclosing loops
            break(2);
       }
   }
}
DaBler
źródło
A pod maską rzeczywiście używagoto :)
jacobq
@ iX3 Mogę użyć wbudowanego asemblera i instrukcji jmp, jeśli to pomoże.
DaBler
@DaBler, nie wiedziałem, że jesteś autorem tej biblioteki. Mój komentarz nie miał być informacją zwrotną, ale raczej wskazaniem, że ta biblioteka używa tej samej metody, co zaakceptowana odpowiedź . Miejmy nadzieję, że twój komentarz został pomyślany jako żart, ponieważ uważam, że używanie funkcji języka (nawet goto) jest o wiele lepsze niż w przypadku wbudowanego asm (specyficzne dla komputera, łatwiejsze do popełnienia błędu, trudniejsze do odczytania, ...).
jacobq
3
for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; i++) {
       if(condition) {
            goto end;
   }
} 

end:
chikuba
źródło
3

Jeśli potrzebujesz wartości i i j, powinno to działać, ale z mniejszą wydajnością niż inne

for(i;i< 1000; i++){    
    for(j; j< 1000; j++){
        if(condition)
            break;
    }
    if(condition) //the same condition
        break;
}
Ali Eren Çelik
źródło
Zauważ, że jeśli warunek jest zależny, jwartość warunku musi być w jakiś sposób zapisana, aby to nadal działało.
SuperBiasedMan
1
Masz rację, ale po przerwie wartość j się nie zmienia, podobnie jak wartość warunku.
Ali Eren Çelik
To jest zepsute rozwiązanie i ogólnie nieważne. Albo j nie jest zdefiniowana poza jego pętli lub for (int i = 0; i < 1000; i++) { for (int j = 0; j < 1000; j++) { if (workComplete[i][j]) break; /* do work */ workComplete[i][j] = true; } if (workComplete[i][j]) break; ... }będzie zawsze wyrwać się z pętli zewnętrznej po pierwszej iteracji pętli wewnętrznej.
Chai T. Rex
0

Innym podejściem jest refaktoryzacja kodu z dwóch pętli for do pętli for i jednej pętli ręcznej. W ten sposób przerwa w pętli ręcznej dotyczy pętli zewnętrznej. Użyłem tego raz w eliminacji Gaussa-Jordana, która wymagała trzech zagnieżdżonych pętli do przetworzenia.

for (int i = 0; i < 1000; i++)
{
    int j = 0;

MANUAL_LOOP:;

    if (j < 1000)
    {
       if (condition)
       {
           break;
       }

       j++;
       goto MANUAL_LOOP;
    }
}
EvilTeach
źródło
Jeśli masz zamiar goto, dlaczego nie gotowyjść poza pętlę?
Willis Blackburn
0

Zauważam, że pytanie brzmi po prostu: „Czy istnieje inny sposób na przerwanie wszystkich pętli?” Nie widzę żadnych zastrzeżeń, ale tak nie jest goto, w szczególności PO nie prosił o dobrą drogę. A co powiesz na longjmpwyjście z wewnętrznej pętli? :-)

#include <stdio.h>
#include <setjmp.h>

int main(int argc, char* argv[]) {
  int counter = 0;
  jmp_buf look_ma_no_goto;
  if (!setjmp(look_ma_no_goto)) {
    for (int i = 0; i < 1000; i++) {
      for (int j = 0; j < 1000; j++) {
        if (i == 500 && j == 500) {
          longjmp(look_ma_no_goto, 1);
        }
        counter++;
      }
    }
  }
  printf("counter=%d\n", counter);
}

setjmpZwraca dwukrotnie. Za pierwszym razem zwraca 0, a program wykonuje zagnieżdżone pętle for. Następnie, gdy oba ii jwynoszą 500, wykonuje się longjmp, co powoduje setjmppowrót z wartością 1, pomijając pętlę.

Nie tylko pozwala longjmpwydostać się z zagnieżdżonych pętli, ale działa również z zagnieżdżonymi funkcjami!

Willis Blackburn
źródło
-3
int i = 0, j= 0;

for(i;i< 1000; i++){    
    for(j; j< 1000; j++){
        if(condition){
            i = j = 1001;
            break;
        }
    }
}

Zerwie obie pętle.

Khurram Ijaz
źródło
-3
for(int i = 0; i < 1000; i++) {
    for(int j = 0; j < 1000; i++) {
       if(condition) {
          func(para1, para2...);
          return;
       }
    }
}

func(para1, para2...) {
    stmt2;
}
berlloon
źródło
Więc w zasadzie mówisz, że powinien (1) wykonać kilka dodatkowych wywołań funkcji, a następnie (2) obracać się przez resztę czasu, kiedy stanie conditionsię fałszywy. Aha, i druga pętla będzie działać wiecznie, ponieważ ij
narasta
-4
i = 0;

do
{
  for (int j = 0; j < 1000; j++) // by the way, your code uses i++ here!
  {
     if (condition)
     {
       break;
     }
  }

  ++i;

} while ((i < 1000) && !condition);
guga
źródło