it-swarm.com.de

Wie bricht man eine Schleife aus einem Switch heraus?

Ich schreibe Code, der so aussieht:

while(true) {
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        break; // **HERE, I want to break out of the loop itself**
    }
}

Gibt es einen direkten Weg, das zu tun?

Ich weiß, dass ich ein Flag verwenden kann, und aus der Schleife ausbrechen kann, indem Sie unmittelbar nach der Umschaltung eine bedingte Pause setzen. Ich möchte nur wissen, ob C++ bereits ein Konstrukt dafür hat.

93
jrharshath

Prämisse

Der folgende Code sollte ungeachtet der Sprache oder der gewünschten Funktionalität als fehlerhaft betrachtet werden:

while( true ) {
}

Unterstützende Argumente

Die while( true )-Schleife hat eine schlechte Form, weil sie:

  • Bricht den implizierten Vertrag einer while-Schleife.
    • Die while-Schleifendeklaration sollte explizit die only exit-Bedingung angeben.
  • impliziert, dass es für immer eine Schleife ist.
    • Code innerhalb der Schleife muss gelesen werden, um die Beendigungsklausel zu verstehen.
    • Wiederholte Schleifen verhindern, dass der Benutzer das Programm innerhalb des Programms beendet.
  • Ist ineffizient.
    • Es gibt mehrere Beendigungsbedingungen für die Schleife, einschließlich der Prüfung auf "wahr".
  • Ist anfällig für Fehler.
    • Kann nicht leicht ermitteln, wo Code eingefügt wird, der für jede Iteration immer ausgeführt wird.
  • Führt zu unnötig komplexem Code.
  • Automatische Quellcode-Analyse.
    • Um Fehler zu finden, Programmkomplexitätsanalysen, Sicherheitsprüfungen durchzuführen oder ein beliebiges anderes Quellcode-Verhalten ohne Code-Ausführung automatisch abzuleiten, können durch Angabe der anfänglichen Unterbrechungsbedingung (en) die Algorithmen nützliche Invarianten bestimmen, wodurch die Metriken der automatischen Quellcode-Analyse verbessert werden.
  • Unendliche Schleifen.
    • Wenn jeder immer while(true) für Schleifen verwendet, die nicht unendlich sind, können wir nicht mehr direkt kommunizieren, wenn Schleifen tatsächlich keine Beendigungsbedingung haben. (Das ist wohl schon passiert, also ist der Punkt irrelevant.)

Alternative zu "Gehe zu"

Der folgende Code hat eine bessere Form:

while( isValidState() ) {
  execute();
}

bool isValidState() {
  return msg->state != DONE;
}

Vorteile

Keine Fahne Nein goto. Keine Ausnahmen. Einfach zu ändern Leicht zu lesen. Einfach zu reparieren Zusätzlich der Code:

  1. Trennt das Wissen über die Arbeitslast der Schleife von der Schleife selbst.
  2. Ermöglicht jemandem, der den Code verwaltet, die Funktionalität einfach zu erweitern.
  3. Ermöglicht die Zuweisung mehrerer Beendigungsbedingungen an einer Stelle.
  4. Trennt die Beendigungsklausel vom auszuführenden Code.
  5. Ist sicherer für Kernkraftwerke. ;-)

Der zweite Punkt ist wichtig. Ohne zu wissen, wie der Code funktioniert, wenn mir jemand fragt, ob die Hauptschleife anderen Threads (oder Prozessen) etwas CPU-Zeit hätte, fallen zwei Lösungen ein:

Option 1

Fügen Sie einfach die Pause ein:

while( isValidState() ) {
  execute();
  sleep();
}

Option 2

Überschreiben ausführen:

void execute() {
  super->execute();
  sleep();
}

Dieser Code ist einfacher (also leichter lesbar) als eine Schleife mit einer eingebetteten switch. Die isValidState-Methode sollte nur bestimmen, ob die Schleife fortgesetzt werden soll. Das Arbeitspferd der Methode sollte in die execute-Methode abstrahiert werden, wodurch Unterklassen das Standardverhalten überschreiben können (eine schwierige Aufgabe, die eine eingebettete switch und goto verwendet).

Python-Beispiel

Vergleichen Sie die folgende Antwort (zu einer Python-Frage), die in StackOverflow gepostet wurde:

  1. Schleife für immer.
  2. Bitten Sie den Benutzer, seine Wahl einzugeben.
  3. Wenn die Benutzereingabe "restart" ist, fahren Sie für immer fort.
  4. Beenden Sie andernfalls die Schleife für immer.
  5. Ende.
while True: 
    choice = raw_input('What do you want? ')

    if choice == 'restart':
        continue
    else:
        break

print 'Break!' 

Gegen:

  1. Initialisieren Sie die Wahl des Benutzers.
  2. Schleife, während der Benutzer das Wort "Neustart" wählt.
  3. Bitten Sie den Benutzer, seine Wahl einzugeben.
  4. Ende.
choice = 'restart';

while choice == 'restart': 
    choice = raw_input('What do you want? ')

print 'Break!'

Hier führt while True zu irreführendem und zu komplexem Code.

52
Dave Jarvis

Sie können goto verwenden.

while ( ... ) {
   switch( ... ) {
     case ...:
         goto exit_loop;

   }
}
exit_loop: ;
144
Mehrdad Afshari

Eine alternative Lösung ist die Verwendung des Schlüsselworts continue in Kombination mit break, d. H .:

for (;;) {
    switch(msg->state) {
    case MSGTYPE
        // code
        continue; // continue with loop
    case DONE:
        break;
    }
    break;
}

Verwenden Sie die continue-Anweisung, um jede Fallbezeichnung zu beenden, an der die Schleife fortgesetzt werden soll, und verwenden Sie die break-Anweisung, um die Fallbezeichnungen zu beenden, die die Schleife beenden sollen.

Natürlich funktioniert diese Lösung nur, wenn nach der switch-Anweisung kein zusätzlicher Code ausgeführt werden muss.

49
sakra

Eine nette Methode, dies zu tun, wäre, dies in eine Funktion einzufügen:

int yourfunc() {

    while(true) {

        switch(msg->state) {
        case MSGTYPE: // ... 
            break;
        // ... more stuff ...
        case DONE:
            return; 
        }

    }
}

Optional (aber "schlechte Praktiken"): Wie bereits vorgeschlagen, können Sie einen goto verwenden oder eine Ausnahme innerhalb des Switch auslösen.

20
Alterlife

AFAIK gibt es in C++ keinen "double break" oder ein ähnliches Konstrukt. Am nächsten wäre ein goto - der zwar einen schlechten Bezug zu seinem Namen hat, jedoch aus einem bestimmten Grund in der Sprache vorhanden ist - solange er vorsichtig und sparsam verwendet wird, ist dies eine gangbare Option.

13
Amber

Sie können Ihren Switch in eine separate Funktion wie folgt setzen:

bool myswitchfunction()
{
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        return false; // **HERE, I want to break out of the loop itself**
    }
    return true;
}

while(myswitchfunction())
    ;
8
Adam Pierce

Auch wenn Sie "goto" nicht mögen, verwenden Sie keine Ausnahme, um eine Schleife zu beenden. Das folgende Beispiel zeigt, wie hässlich es sein kann:

try {
  while ( ... ) {
    switch( ... ) {
      case ...:
        throw 777; // I'm afraid of goto
     }
  }
}
catch ( int )
{
}

Ich würde goto wie in this answer verwenden. In diesem Fall macht goto den Code klarer als jede andere Option. Ich hoffe, dass diese Frage hilfreich sein wird.

Ich denke jedoch, dass die Verwendung von goto hier die einzige Option ist, da der String while(true) verwendet wird. Sie sollten das Refactoring Ihrer Schleife in Betracht ziehen. Ich würde die folgende Lösung annehmen:

bool end_loop = false;
while ( !end_loop ) {
    switch( msg->state ) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        end_loop = true; break;
    }
}

Oder sogar das Folgende:

while ( msg->state != DONE ) {
    switch( msg->state ) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
}

In diesem Fall gibt es kein C++ - Konstrukt, um die Schleife zu durchbrechen.

Verwenden Sie entweder ein Flag, um die Schleife zu unterbrechen, oder extrahieren Sie (falls zutreffend) Ihren Code in eine Funktion, und verwenden Sie return.

5
sharptooth

Sie könnten möglicherweise goto verwenden, aber ich würde es vorziehen, ein Flag zu setzen, das die Schleife stoppt. Dann brechen Sie den Schalter aus.

2
Martin York

Nein, C++ hat dafür kein Konstrukt, da das Schlüsselwort "break" bereits für das Verlassen des Switch-Blocks reserviert ist. Alternativ könnte ein do..while () mit einem Exit-Flag ausreichen.

do { 
    switch(option){
        case 1: ..; break;
        ...
        case n: .. ;break;
        default: flag = false; break;
    }
} while(flag);
2

Warum nicht einfach die Bedingung in Ihrer while-Schleife beheben, wodurch das Problem behoben wird?

while(msg->state != DONE)
{
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        // We can't get here, but for completeness we list it.
        break; // **HERE, I want to break out of the loop itself**
    }
}
2
Mark B

Meiner Ansicht nach;

while(msg->state != mExit) 
{
    switch(msg->state) 
    {
      case MSGTYPE: // ...
         break;
      case DONE:
      //  .. 
      //  ..
      msg->state =mExit;
      break;
    }
}
if (msg->state ==mExit)
     msg->state =DONE;
1
ercan

Es überrascht mich, wie einfach dies ist, wenn man die Tiefe der Erklärungen bedenkt.

bool imLoopin = true;

while(imLoopin) {

    switch(msg->state) {

        case MSGTYPE: // ... 
            break;

        // ... more stuff ...

        case DONE:
            imLoopin = false;
            break;

    }

}

LOL !! Ja wirklich! Das ist alles was du brauchst! Eine zusätzliche Variable!

0
MarqueIV

Der einfachste Weg, dies zu tun, besteht darin, vor dem SWITCH eine einfache IF zu setzen, und diese IF testet Ihre Bedingung, um die Schleife zu beenden. So einfach wie möglich

0
SpiriAd

Das Schlüsselwort break in C++ beendet nur die am stärksten verschachtelte umgebende Iteration oder die Anweisung switch. Daher konnten Sie die while (true)-Schleife nicht direkt in der switch-Anweisung durchbrechen. Sie könnten jedoch den folgenden Code verwenden, der meiner Meinung nach ein hervorragendes Muster für diese Art von Problem ist:

for (; msg->state != DONE; msg = next_message()) {
    switch (msg->state) {
    case MSGTYPE:
        //...
        break;

    //...
    }
}

Wenn Sie etwas tun müssen, wenn msg->state gleich DONE ist (beispielsweise eine Bereinigungsroutine ausführen), platzieren Sie diesen Code unmittelbar nach der for-Schleife. wenn Sie derzeit haben:

while (true) {
    switch (msg->state) {
    case MSGTYPE:
        //... 
        break;

    //...

    case DONE:
        do_cleanup();
        break;
    }

    if (msg->state == DONE)
        break;

    msg = next_message();
}

Dann verwenden Sie stattdessen:

for (; msg->state != DONE; msg = next_message()) {
    switch (msg->state) {
    case MSGTYPE:
        //...
        break;

    //...
    }
}

assert(msg->state == DONE);
do_cleanup();
0
Daniel Trebbien
while(MyCondition) {
switch(msg->state) {
case MSGTYPE: // ... 
    break;
// ... more stuff ...
case DONE:
   MyCondition=false; // just add this code and you will be out of loop.
    break; // **HERE, you want to break out of the loop itself**
}
}
0

Ich habe dasselbe Problem und wurde mit einer Flagge gelöst.

bool flag = false;
while(true) {
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        flag = true; // **HERE, I want to break out of the loop itself**
    }
    if(flag) break;
}
0
Ram Suthar