Wie kann man aus verschachtelten Schleifen ausbrechen?

96

Wenn ich eine breakAnweisung verwende, wird nur die innere Schleife unterbrochen, und ich muss ein Flag verwenden, um die äußere Schleife zu unterbrechen. Wenn es jedoch viele verschachtelte Schleifen gibt, sieht der Code nicht gut aus.

Gibt es eine andere Möglichkeit, alle Schleifen zu durchbrechen? (Bitte nicht benutzen 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
quelle
2
Sie können int i und int j versuchen, bevor die Schleife beginnt, und dann unter der Bedingung 1001, dass die Schleife die nächste nicht wiederholt.
Khurram Ijaz
C ++ - Version: stackoverflow.com/questions/1257744/…
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功

Antworten:

43

Verwenden:

if (condition) {
    i = j = 1000;
    break;
}
Peter Mortensen
quelle
49
Funktioniert, aber hässlich und nicht allgemein. Was ist, wenn jemand das Limit auf 2000 ändert (angenommen, der Code ist länger, sodass Sie ihn nicht sofort bemerken)?
Ugoren
1
@ugoren Dann ist es auch so einfach. Was ist, wenn Sie const int count =1000 in der globalen Initialisierung a verwendet haben ? oder als #defineMakro.
Laksith
4
Wie @ugoren betont, ist dies keine allgemeine Lösung. Da dies der erste Google-Treffer für diese Frage ist, wäre es schön, wenn die allgemeine Lösung ausgewählt worden wäre. Nun, die Leute sind es sowieso gewohnt, die Nummer 2 zu überprüfen.
BeeOnRope
1
Ich denke, ich brauche nur i = 1000?
Peter Wu
189

Nein, verdirb den Spaß nicht mit einem break. Dies ist die letzte noch gültige Verwendung von goto;)

Wenn dies nicht der Fall ist, können Sie Flags verwenden, um aus tief verschachtelten Schleifen auszubrechen.

Ein anderer Ansatz zum Ausbrechen einer verschachtelten Schleife besteht darin, beide Schleifen in eine separate Funktion zu zerlegen und von dieser Funktion zurückzukehren, wenn Sie sie beenden möchten.

Zusammengefasst - um aus verschachtelten Schleifen auszubrechen:

  1. verwenden goto
  2. benutze Flags
  3. Schleifen in separate Funktionsaufrufe zerlegen

Konnte nicht widerstehen, xkcd hier aufzunehmen :)

Geben Sie hier die Bildbeschreibung ein

Quelle

Goto's gelten als schädlich, aber wie viele Leute in den Kommentaren vermuten, muss es nicht sein. Wenn es mit Bedacht eingesetzt wird, kann es ein großartiges Werkzeug sein. Alles, was in Maßen verwendet wird, macht Spaß.

Srikar Appalaraju
quelle
29
Gehe zu ist ungefähr so ​​klar wie du hier sein wirst, ja. Das Setzen der Exit-Variablen auf 1000 ist noch klobiger.
Correnos
3
Ich möchte hinzufügen, dass Gotos nicht explizit böse sind, sondern nur für das Böse verwendet werden können. Ich finde, dass es einige Fälle gibt, zum Beispiel, in denen sie die beste Lösung sind. "Keine Gotos verwenden" ist ein guter Anfang, aber ich denke, der nächste Schritt in der Fertigkeit ermöglicht es Ihnen, "keine Gotos mit großer Reichweite zu verwenden".
Aatch
1
Ich möchte damit nicht einverstanden sein: "Das Erstellen einer Funktion führt zu exponentiellen Mengen an Addieren / Subtrahieren des Stapelzeigers". Wenn es eine lokale (statische) Funktion gibt, die nur an einer Stelle im Programmablauf aufgerufen wird, wird sie von jedem halbwegs anständigen Compiler eingebunden, und der resultierende Code ist im Wesentlichen der gleiche wie bei goto. Dies ist möglicherweise der einfachste Optimierungsfall für jeden Compiler.
DrV
1
Refactoring ist normalerweise die sauberste Lösung. Wenn jedoch Variablen außerhalb der Schleife während der inneren Schleife geändert werden, werden die Dinge kompliziert. Eine Möglichkeit besteht darin, die Variable als Referenz (Zeiger) an die innere Funktion zu übergeben. Dies kann jedoch die Compileroptimierung verwirren und unnötigen zusätzlichen Code erzeugen. Eine andere Möglichkeit besteht darin, solche Variablen auf Modulebene statisch zu machen, aber das ist auch nicht sehr schön. C fehlen leider verschachtelte Funktionen, da sie dieses Problem lösen würden - es sei denn, Sie sind bereit, sich an die Verwendung von gcc zu binden, das eine Erweiterung bietet.
DrV
1
+1. Und Donald E. Knuths strukturierte Programmierung mit go to Statements ( wiki.c2.com/?StructuredProgrammingWithGoToStatements ) ist ein interessanter Artikel, um Dijkstra's auszugleichen.
kmkaplan
39
bool stop = false;
for (int i = 0; (i < 1000) && !stop; i++)
{
    for (int j = 0; (j < 1000) && !stop; j++)
    {
        if (condition)
            stop = true;
    }
}
Tung
quelle
Die Lösung erhöht beide Variablen bei Unterbrechung immer noch um eins , was zu Problemen führen kann
TheSola10
7
Man kann "stop = true" setzen; und dann "Pause;". Führen Sie dann direkt nach dem Ende der inneren "for" -Schleife "if (stop) break;" aus.
Jeff Grigg
34

Eine Möglichkeit besteht darin, alle verschachtelten Schleifen in eine Funktion zu setzen und von der innersten Schleife zurückzukehren, falls alle Schleifen ausbrechen müssen.

function() 
{    
  for(int i=0; i<1000; i++)
  {
   for(int j=0; j<1000;j++)
   {
      if (condition)
        return;
   }
  }    
}
Jay
quelle
1
scheint die beste Lösung für mich
Luca Steeb
20

Ich denke, gotowird das Problem lösen

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

end:
stmt2 
Renjith KN
quelle
@chikuba Ich habe eine Antwort von cprogramming.com/tutorial/goto.html erhalten und Ihre Antwort wird nicht veröffentlicht, wenn ich das Gleiche tue. Deshalb sehe ich Ihren Beitrag nicht
Renjith KN
15

Sie benötigen eine boolesche Variable, wenn Sie sie lesbar haben möchten:

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;
}

Wenn Sie möchten, dass es weniger lesbar ist, können Sie sich der booleschen Bewertung anschließen:

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

Als ultimative Möglichkeit können Sie die anfängliche Schleife ungültig machen:

for(int i = 0; i < size; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      i = size;
      break;
    }
  }
}
Jack
quelle
4

Achtung: Diese Antwort zeigt eine wirklich dunkle Konstruktion.

Wenn Sie GCC verwenden, überprüfen Sie diese Bibliothek . breakKann wie in PHP die Anzahl der verschachtelten Schleifen akzeptieren, die Sie beenden möchten. Sie können so etwas schreiben:

for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; j++) {
       if(condition) {
            // break two nested enclosing loops
            break(2);
       }
   }
}
DaBler
quelle
Und unter der Haube es in der Tat wird mitgoto :)
Ix3
@ iX3 Ich kann Inline-Assembler und JMP-Anweisungen verwenden, wenn das hilft.
DaBler
@DaBler, ich wusste nicht, dass Sie der Autor dieser Bibliothek sind. Mein Kommentar war nicht als Feedback gedacht, sondern als Hinweis darauf, dass diese Bibliothek dieselbe Methode wie die akzeptierte Antwort verwendet . Hoffentlich war Ihr Kommentar als Scherz gedacht, da ich denke, dass die Verwendung einer Sprachfunktion (gerade goto) weitaus besser ist, um asm inline zu verwenden (maschinenspezifisch, leichter Fehler zu machen, schwerer zu lesen, ...).
iX3
3
for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; i++) {
       if(condition) {
            goto end;
   }
} 

end:
Chikuba
quelle
3

Wenn Sie die Werte von i und j benötigen, sollte dies funktionieren, jedoch mit weniger Leistung als bei anderen

for(i;i< 1000; i++){    
    for(j; j< 1000; j++){
        if(condition)
            break;
    }
    if(condition) //the same condition
        break;
}
Ali Eren Çelik
quelle
Beachten Sie, dass, wenn die Bedingung abhängig jist, der Wert der Bedingung auf irgendeine Weise gespeichert werden muss, damit dies weiterhin funktioniert.
SuperBiasedMan
1
Sie haben Recht, aber nach der Pause ändert sich der Wert von j nicht, ebenso wie der Wert der Bedingung.
Ali Eren Çelik
Dies ist eine kaputte Lösung und im Allgemeinen nicht gültig. Entweder j nicht außerhalb ihrer Schleife definiert , oder 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; ... }geht zu immer bricht aus der äußeren Schleife nach der ersten Iteration der inneren Schleife.
Chai T. Rex
-3
int i = 0, j= 0;

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

Bricht beide Schleifen.

Khurram Ijaz
quelle
-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
quelle
Sie sagen also im Grunde, dass es (1) eine Reihe zusätzlicher Funktionsaufrufe machen und dann (2) für den Rest der Zeit drehen sollte, wenn es conditionfalsch wird. Oh, und die zweite Schleife wird für immer laufen, weil sie statt inkrementiert iwird j, whoops ...
iX3
-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
quelle