Kann ich break verwenden, um mehrere verschachtelte 'for'-Schleifen zu beenden?

305

Ist es möglich, mit dieser breakFunktion mehrere verschachtelte forSchleifen zu verlassen?

Wenn ja, wie würden Sie dies tun? Können Sie auch steuern, wie viele Schleifen die breakAusgänge haben?

Gefälscht
quelle
1
Anstatt break oder goto zum Beenden mehrerer verschachtelter Schleifen zu verwenden, können Sie diese bestimmte Logik in eine Funktion einschließen und mit return mehrere verschachtelte Schleifen beenden. Dies behält die Ästhetik Ihres Codes bei und verhindert, dass Sie goto verwenden, was eine schlechte Programmierpraxis ist.
Rishab Shinghal

Antworten:

239

AFAIK, C ++ unterstützt keine Namensschleifen wie Java und andere Sprachen. Sie können ein goto verwenden oder einen von Ihnen verwendeten Flag-Wert erstellen. Überprüfen Sie am Ende jeder Schleife den Flag-Wert. Wenn es auf true gesetzt ist, können Sie aus dieser Iteration ausbrechen.

Cullen Walsh
quelle
317
Haben Sie keine Angst, eine zu verwenden, gotowenn dies die beste Option ist.
Jkeys
18
Ich bin ein neuer C ++ - Programmierer (und einer ohne formale Programmierschulung), nachdem ich über die Beschimpfungen der Leute auf goto gelesen habe. Ich zögere, es zu verwenden, aus Angst, mein Programm könnte plötzlich explodieren und mich töten. Abgesehen davon, als ich Programme auf meinem ti-83 schrieb (natürlich im langweiligen Matheunterricht), erforderten die Funktionen, die der Basiseditor bereitstellte, die Verwendung von gotos.
Gefälscht
26
@Faken: Zwei Arten von Programmierern verwenden goto: schlechte Programmierer und pragmatische Programmierer. Ersteres ist selbsterklärend. Letztere, in die Sie passen würden, wenn Sie sie gut verwenden möchten, verwenden ein sogenanntes "böses" Konzept, wenn es das geringere von (zwei) Übeln ist. Lesen Sie dies, um einige C ++ - Konzepte besser zu verstehen, die Sie möglicherweise von Zeit zu Zeit verwenden müssen (Makros, Goto, Präprozessor, Arrays): parashift.com/c++-faq-lite/big-picture.html#faq-6.15
jkeys
41
@Faken: Es ist nichts falsch mit goto. Es ist ein Missbrauch eines goto, der problematisch ist.
Jeder
28
@Hooked: Das stimmt, außer dass die Verwendung gotoselten die beste Option ist. Warum nicht die Loops in ihre eigene Funktion versetzen ( inlinewenn Sie sich Gedanken über die Geschwindigkeit machen) und returndaraus?
sbi
265

Nein, verwöhne es nicht mit einem break. Dies ist die letzte verbleibende Festung für die Nutzung von goto.

Henk Holterman
quelle
19
Der MISRA C ++ - Codierungsstandard ermöglicht die Verwendung von goto, um genau diese Situation abzudecken.
Richard Corden
11
Echte Programmierer haben keine Angst davor, goto zu verwenden. 25 Jahre lang getan - kein Bedauern - hat eine Menge Entwicklungszeit gespart.
Doug Null
1
Genau. gotos sind ein Muss, wenn Sie nicht über 8 KB Pixel verfügen und eine Folge von 30 Windows-Betriebssystemaufrufen aufrufen müssen.
Michaël Roy
Oder eine einfache "Rückkehr", indem Sie den Code in eine Funktion einfügen.
Tara
68

Nur um eine explizite Antwort mit Lambdas hinzuzufügen:

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

Natürlich hat dieses Muster einige Einschränkungen und natürlich nur C ++ 11, aber ich denke, es ist ziemlich nützlich.

Predelnik
quelle
7
Dies könnte den Leser verwirren, zu glauben, dass die Rückkehr die Funktion bewirkt, in der sich das Lambda befindet, und nicht das Lambda selbst.
Xunie
3
@Xunie: Wenn Ihre Schleife so kompliziert ist, dass Sie vergessen, dass Sie sich in einem Lambda befinden, ist es Zeit, sie in eine andere Funktion zu versetzen, aber in einfachen Fällen sollte dies recht gut funktionieren.
MikeMB
17
Ich denke, diese Lösung ist wunderschön
Tuket
Sie können das Lambda in einer RIIA-Vorlage erfassen, die ihm einen Namen gibt und es in dtor (auch bekannt als scope gaurd) aufruft. Dies würde keinen Leistungsgewinn hinzufügen, kann jedoch die Lesbarkeit verbessern und das Risiko verringern, dass die Klammer des Funktionsaufrufs fehlt.
Red.Wave
56

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

Dies wirft natürlich das andere Argument auf, ob Sie jemals explizit returnvon einer anderen Funktion als am Ende ausgehen sollten.

Greg Hewgill
quelle
7
Das ist ein C-Problem. Mit RIAA ist eine vorzeitige Rückgabe kein Problem, da alle mit der vorzeitigen Rückgabe verbundenen Probleme korrekt behandelt werden.
Martin York
4
Ich verstehe, dass eine ordnungsgemäße Anwendung von RIAA das Problem der Ressourcenbereinigung in C ++ lösen kann, aber ich habe gesehen, dass das philosophische Argument gegen eine frühzeitige Rückkehr in anderen Umgebungen und Sprachen fortgesetzt wird. Ein System, an dem ich gearbeitet habe, bei dem der Codierungsstandard die vorzeitige Rückgabe untersagte, hatte Funktionen, die mit booleschen Variablen (mit Namen wie continue_processing) übersät waren , die die Ausführung von Codeblöcken weiter unten in der Funktion kontrollierten.
Greg Hewgill
21
Was ist RIAA? Ist das so etwas wie RAII? = D
jkeys
1
Kommt darauf an, wie viele Schleifen er hat und wie tief das Nest geht ... willst du die blaue oder die rote Pille?
Matt
34

break beendet nur die innerste Schleife, die es enthält.

Sie können goto verwenden , um aus einer beliebigen Anzahl von Schleifen auszubrechen.

Natürlich wird goto oft als schädlich angesehen .

Ist es richtig, die Unterbrechungsfunktion [...] zu verwenden?

Die Verwendung von break und goto kann es schwieriger machen, über die Richtigkeit eines Programms nachzudenken. Hier finden Sie eine Diskussion dazu: Dijkstra war nicht verrückt .

Karl Voigtland
quelle
16
Eine gute Antwort darin, dass es erklärt, dass "goto is schädlich" meme stark mit der allgemeineren Aussage "Unterbrechung des Kontrollflusses ist schädlich" verbunden ist. Es ist sinnlos zu sagen, "goto ist schädlich", und sich dann umzudrehen und die Verwendung von breakoder zu empfehlen return.
Pavel Minaev
6
@Pavel: breakund returnhaben den Vorteil, gotodass Sie nicht nach einem Label suchen müssen, um herauszufinden, wohin sie gehen. Ja, darunter sind sie eine Art goto, aber eine sehr eingeschränkte. Sie sind vom Mustervergleichsgehirn eines Programmierers viel einfacher zu entziffern als das uneingeschränkte goto. Also IMO sind sie vorzuziehen.
sbi
@sbi: Stimmt, aber Pause ist immer noch nicht Teil der strukturierten Programmierung. Es ist einfach besser verträglich als ein goto.
Jkeys
2
@KarlVoigtland der Dijkstra Link ist veraltet; Dies scheint zu funktionieren: blog.plover.com/2009/07
Aaron Brager
3
In dieser Situation ist absolut nichts falsch daran, goto zu verwenden. Ein gut platzierter Goto ist sprunghaft besser und lesbarer als viele der sonst vorgeschlagenen verzerrten Lösungen.
James
22

Obwohl diese Antwort bereits vorgestellt wurde, denke ich, dass ein guter Ansatz darin besteht, Folgendes zu tun:

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
quelle
5
Das ist gut, aber immer noch nicht so lesbar, ich würde es vorziehen, in diesem Fall zu gehen
28етър Петров
2
Dies macht Ihren Code "ziemlich" langsam, weil der gotoMainLoopin jedem Zyklus überprüft wird
Thomas
1
In diesem Fall gotomacht die Verwendung des Real den Kern lesbarer und leistungsfähiger.
25етър Петров
20

Wie wäre es damit?

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
quelle
2
interessanter Ansatz, aber ich mag definitiv die Art und Weise, wie ered @ inf.ig.sh damit umgeht. for (vorzeichenloses int y = 0; y <y_max &&! gotoMainLoop; y ++).
Andy
15

Ein Codebeispiel mit gotound eine Bezeichnung zum Ausbrechen einer verschachtelten Schleife:

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

Eine gute Möglichkeit, aus mehreren verschachtelten Schleifen auszubrechen, besteht darin, Ihren Code in eine Funktion umzuwandeln:

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
quelle
4
... was keine Option ist, wenn wir 10-20 Variablen für das Stack-Framing dieser Funktion übergeben müssen.
28етър Петров
1
@ ПетърПетров dann gehen Sie für ein Lambda, das auch besser ist, da Sie es genau dort definieren können, wo Sie es brauchen.
DarioP
+1 für Lambdas, aber Überarbeitung des Game-Engine-Kerns, bei dem selbst ein Stack-Frame immer noch ein Engpass ist. Tut mir leid zu sagen, aber Lambdas sind zumindest in MSVC 2010 nicht so leicht.
Петров
@ ПетърПетров Ändern Sie dann das Funktionspaar in eine Klasse und die Stapelvariablen in private Mitglieder.
Arthur Tacca
Dies macht den bereits komplexen Code nur zu kompliziert :) Manchmal ist goto die einzige Lösung. Oder wenn Sie komplexe Automaten mit der Dokumentation "goto state X" schreiben, lässt goto den Code tatsächlich so lesen, wie er im Dokument geschrieben ist. Außerdem haben C # und go beide einen Zweck: Keine Sprache ist ohne goto vollständig, und gotos sind häufig die am besten verwendeten Werkzeuge zum Schreiben von Zwischenübersetzern oder Assembler-ähnlichem Code.
1етър Петров
5

goto kann sehr hilfreich sein, um verschachtelte Schleifen zu brechen

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
quelle
3

Die breakAnweisung beendet die Ausführung der nächsten einschließenden do, for, switch, oder whileErklärung , in der sie erscheint. Die Steuerung wird an die Anweisung übergeben, die auf die terminierte Anweisung folgt.

von msdn .

rauben
quelle
3

Ich denke, a gotoist unter diesen Umständen gültig:

Um ein break/ zu simulieren continue, möchten Sie:

Brechen

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

Fortsetzen

for ( ;  ; ) {
    for ( ;  ;  ) {
        /*Code here*/
        if (condition) {
            i++;
            goto multiCont;
        }
    }
    multiCont:
}
JuliusAlphonso
quelle
"Weiter" funktioniert dort nicht, da der Iterationsausdruck der ersten Schleife nicht ausgeführt wird
Fabio A.
Ich gehe davon aus, dass der Iterator für die erste Schleife ist i. Daher i++vor dem goto
JuliusAlphonso
0

Andere Sprachen wie PHP akzeptieren einen Parameter für break (dh break 2;), um die Anzahl der verschachtelten Schleifenebenen anzugeben, aus denen Sie ausbrechen möchten, C ++ jedoch nicht. Sie müssen es herausfinden, indem Sie einen Booleschen Wert verwenden, den Sie vor der Schleife auf false gesetzt haben, der in der Schleife auf true gesetzt ist, wenn Sie brechen möchten, sowie eine bedingte Unterbrechung nach der verschachtelten Schleife, um zu überprüfen, ob der Boolesche Wert auf true gesetzt wurde und brechen wenn ja.

Patrick Glandien
quelle
0

Ich weiß, das ist ein alter Beitrag. Aber ich würde eine etwas logische und einfachere Antwort vorschlagen.

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
quelle
3
Dies ist keine sehr skalierbare Lösung, da j = conditionjsie nicht funktioniert, wenn Sie stattdessen ein komplexes Prädikat haben j < conditionj.
Sergey
0

Brechen Sie eine beliebige Anzahl von Schleifen mit nur einer boolVariablen (siehe unten):

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

In diesem Code haben wir break;alle Schleifen.

Vikas Bansal
quelle
0

Ich bin mir nicht sicher, ob es sich lohnt, aber Sie können Javas benannte Schleifen mit ein paar einfachen Makros emulieren:

#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

Anwendungsbeispiel:

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

Ein anderes Beispiel:

#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
quelle
-1
  while (i<n) {
    bool shouldBreakOuter = false;
    for (int j=i + 1; j<n; ++j) {
      if (someCondition) {
          shouldBreakOuter = true;
      }
    }

    if (shouldBreakOuter == true)
      break;

  }
MEnnabah
quelle
-3

Sie können try ... catch verwenden.

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

Wenn Sie aus mehreren Schleifen gleichzeitig ausbrechen müssen, ist dies oft eine Ausnahme.

lawilog
quelle
1
Ich würde gerne wissen, warum diese Antwort so viele Abstimmungen erhält.
hkBattousai
6
@hkBattousai Die Lösung hat Abstimmungen, da eine Ausnahme zur Steuerung des Ausführungsflusses verwendet wird. Wie der Name schon sagt, sollten Ausnahmen nur in Ausnahmefällen verwendet werden.
Helio Santos
4
@HelioSantos Ist dies nicht eine Ausnahmesituation, für die die Sprache keine angemessene Lösung liefert?
hkBattousai
8
Ausnahmen sind langsam.
Gordon
2
Die Auswirkung des Wurfs auf die Leistung ist enorm für etwas, das in 99% der Fälle kein nicht behebbarer Fehler ist.
Michaël Roy
-4

Das Ausbrechen einer for-Schleife ist für mich etwas seltsam, da die Semantik einer for-Schleife normalerweise anzeigt, dass sie eine bestimmte Anzahl von Malen ausgeführt wird. Es ist jedoch nicht in allen Fällen schlecht; Wenn Sie nach etwas in einer Sammlung suchen und es brechen möchten, nachdem Sie es gefunden haben, ist es nützlich. Das Aufbrechen verschachtelter Schleifen ist in C ++ jedoch nicht möglich. Es ist in anderen Sprachen durch die Verwendung einer beschrifteten Pause. Sie können ein Etikett und ein Goto verwenden, aber das kann nachts zu Sodbrennen führen. Scheint jedoch die beste Option zu sein.

Nick Lewis
quelle
11
Es ist überhaupt nicht seltsam. Wenn Sie eine Sammlung durchlaufen, um nach etwas zu suchen (und keine schnellere Suchmethode haben), macht es keinen Sinn, die Schleife zu beenden. (als ein Beispiel)
Joe