Alternativen zu „while (1)“, um die Verzweigung zu vereinfachen [duplicate]

Lesezeit: 9 Minuten

Benutzeravatar von Philippe A
Philipp A.

Ab und zu benutze ich a while(1) Block, um eine Folge von zu glätten if..else aus dem Verhältnis geraten. In diese Richtung geht es.

Anstatt zu tun:

// process 
if (success) {
  // process 
  if (success) {
    //process
    if (success) {
      // etc
    }
  }
}

Ich tue:

while (1) {
  // process
  if (!success) break;
  // process
  if (!success) break;
  // process
  if (!success) break;
  // etc
  break;
}

Ich bin ein wenig genervt von dem impliziten Sprung am Ende der while. Könnte ich mit einem schlankeren Konstrukt davonkommen (dh Nr break Am Ende)?

Ich könnte das Finale handeln break mit einer Variablen (oder Register?). Das ist nicht gerade schlanker oder klarer.

int once = 1;
while (once--) {
  // process
  if (!success) break;
  // process
  if (!success) break;
  // process
  if (!success) break;
  // etc
}

Eine for-Schleife würde etwas besser aussehen (C99):

for (int once = 1 ; once--; once) {
  // process
  if (!success) break;
  // process
  if (!success) break;
  // process
  if (!success) break;
  // etc
}

Ich dachte an die Verwendung eines Schaltergehäuses. Es sieht nicht viel besser aus, obwohl es funktionieren würde.

switch (1) { default:
  // process
  if (!success) break;
  // process
  if (!success) break;
  // process
  if (!success) break;
  // etc
}

In diesem speziellen Fall scheint das Konzept eines Labels unschlagbar.

// process
if (!success) goto end;
// process
if (!success) goto end;
// process
if (!success) goto end;
// etc

end:

Welchen anderen Ansatz kennst/nutzt ihr?

  • Erlauben Sie mir, eine starke, starke Missbilligung der Verwendung eines Schleifenkonstrukts für etwas auszudrücken, das keine Schleife ist. Der Vorschlag von @ouah, verschachtelte Bedingungen in eine separate Funktion zu heben, ist hier bei weitem die beste Option.

    – Russell Borogove

    5. August 2014 um 20:49 Uhr

  • verwandt: Wie vermeide ich „wenn“-Ketten?

    – chue x

    6. August 2014 um 2:36 Uhr

  • Was auch immer Sie tun, versuchen Sie nicht, eine Schleife in ein Etikett zu machen, nur weil Ihnen jemand gesagt hat, dass Etiketten schlecht sind und Schleifen nicht.

    – Benutzer253751

    6. August 2014 um 3:06 Uhr

  • Weiter zu c++ und throw() 😉

    – SF.

    6. August 2014 um 9:36 Uhr

  • Wenn Sie a verwendet hätten goto für die zweite Lösung while(1) { if(error) break; }, es wäre viel ehrlicher. Sie verwenden den Schleifenausgang als Goto-Label, ohne es irgendwo anzugeben. Das ist kein while Schleife, es ist nur verschleierter Code.

    – JensG

    6. August 2014 um 14:34 Uhr


Benutzeravatar von ouah
ouah

Welchen anderen Ansatz kennst/nutzt ihr?

Sie können Ihre kapseln while Schleife in einer Funktion (und rufe diese Funktion dort auf, wo du deine hattest while Schleife):

static void process(void)
{
   // process
   if (!success) return;
   // process
   if (!success) return;
   // process
   if (!success) return;
   // process
}

Jeder halbwegs anständige Compiler (zB even gcc mit deaktivierten Optimierungen) wird a static Funktion, wenn sie einmal aufgerufen wird. (Natürlich müssen einige Variablen im lexikalischen Bereich von sein process Funktion, geben Sie sie in diesem Fall einfach als Parameter der Funktion an).

Beachten Sie, dass das Schreiben von Code von oben nach unten statt horizontal erfolgt (z. B. Ihr Beispiel mit nested if) wird genannt muffig. Einen schönen Artikel zu diesem Thema gibt es hier:

“Code von oben nach unten lesen”

Auch in der Codierungsstil des Linux-Kernels Es gibt einen speziellen Warnhinweis gegen horizontalen Code:

“Wenn Sie mehr als 3 Einrückungsebenen benötigen, sind Sie sowieso am Arsch und sollten Ihr Programm reparieren.”

  • +1 Dies ist auch eine großartige Lösung. Vielleicht geben Sie stattdessen einen Fehlercode zurück void obwohl.

    – Fiddle Bits

    5. August 2014 um 19:51 Uhr


  • Danke für den nützlichen Artikel. Ich habe versucht, den Ursprung von zu googeln muffig ohne Erfolg?

    – Philippe A.

    5. August 2014 um 20:42 Uhr

  • @PhilippeA. Ich denke, es kommt von Duffs Gerät en.wikipedia.org/wiki/Duff’s_device Wenn Sie noch nie darauf gestoßen sind, empfehle ich Ihnen dringend, die Seite zu lesen und die seltsame Struktur der switch-Anweisung mit dem eingebetteten zu bemerken do while Aussage drin.

    – au

    5. August 2014 um 20:56 Uhr

  • Sehen blog.codinghorror.com/flattening-arrow-code

    – Jim Balter

    6. August 2014 um 11:22 Uhr

  • @ABFORCE – weißt du eigentlich, was der Zweck der Beratung mit einem einzigen Rückkehrpunkt war, oder folgst du nur dem Kult?

    – Dawor

    6. August 2014 um 13:55 Uhr

Benutzeravatar von Fiddling Bits
Gefummel Bits

Das Folgende ist eine Methode, die dem sehr ähnlich ist, was Sie mit den Schleifen machen, aber ohne die Notwendigkeit eines Zählers oder eines break Aussage am Ende.

do
{
    // process
    if (!success) break;
    // process
    if (!success) break;
    // process
    if (!success) break;
    ...
    // No need for a break statement here
}
while(0);

  • while Schleifen wie diese sind eine seltsame Möglichkeit, sie zu vermeiden goto. goto kann und sollte in den von OP erwähnten Fällen verwendet werden.

    – Pavan Yalamanchili

    5. August 2014 um 18:05 Uhr

  • Dies ist unendlich zu goto; das Ziel von break ist gut definiert und unveränderlich. Mit einem goto könnten Sie eine beliebige Anzahl von Zielen haben – die Restriktivität von break erzwingt die Struktur. Ich kann mir vorstellen, einen Codeblock zu kopieren und einzufügen, ohne beispielsweise den Namen des Labels zu ändern.

    – Clifford

    5. August 2014 um 18:59 Uhr

  • @FiddlingBits Den Leuten wird beigebracht, sie nicht zu benutzen goto da dies zu komplexem und verschleiertem Code führen kann. In diesem speziellen Fall replizieren Sie im Wesentlichen was goto tut natürlich durch die Verwendung do / while. Wenn jemand, der mit dem Code nicht vertraut ist, anfängt, sich den Code anzusehen, wird er keine Ahnung haben, warum Sie eine Schleife haben, bis er auf die stößt while(0). Es vermittelt nicht sofort, was der Code tun soll.

    – Pavan Yalamanchili

    5. August 2014 um 19:00 Uhr

  • @PavanYalamanchili : Gut gesagt, es verdient zumindest einen klaren Kommentar – für den armen (und oft unerfahrenen) Betreuer!

    – Clifford

    5. August 2014 um 19:23 Uhr

  • Der Hass auf goto stammt von Dijkstra, der versucht, strukturierte Programmierung in eine Welt zu zwingen, die es nicht wollte. Goto nicht zu verwenden ist keine “gute Praxis”; es ist eine 40 Jahre alte Irrelevanz aus dem Zusammenhang gerissen. Der Anwendungsfall des OP ist eigentlich eine Standardanwendung für goto – eine, in der es die ist Rechts Sprachfunktion: was zu saubererem, einfacherem und schnellerem Code führt.

    – imallett

    6. August 2014 um 11:40 Uhr


Benutzeravatar von Clifford
Clifford

Wenn Sie arrangieren, dass der Körper jedes bedingten Blocks generiert wird success ist eine Funktion wie folgt oder each // process kann ansonsten auf einen booleschen Ausdruck reduziert werden, wie zum Beispiel:

success = f1() ; 
if( success ) 
{
  success = f2() ; 
  if( success ) 
  {
    success = f3() ; 
    if( success ) 
    {
      success = f4()
    }
  }
}

Dann können Sie dies auf einen einzigen booleschen Ausdruck reduzieren, indem Sie die Kurzschlussauswertung ausnutzen:

success = f1() && 
          f2() && 
          f3() && 
          f4() ;

Hier f2() wird nicht aufgerufen, wenn f1() gibt false und für jeden nachfolgenden Aufruf dasselbe zurück – Ausdrucksauswertung bricht beim ersten ab && Operanden-Unterausdruck, um falsch zurückzugeben.

  • Ich mag Ihre Lösung kalte Logik. Es ist jedoch nicht sehr praktisch, meine Verarbeitung in vielen verschiedenen Funktionen zu kapseln. Die Variante von @ouah ist in meinem Fall nützlicher.

    – Philippe A.

    5. August 2014 um 20:11 Uhr


  • Zugegeben, es ist vielleicht nicht allgemeingültig und daher nicht “die Antwort”, aber es ist erwähnenswert.

    – Clifford

    5. August 2014 um 20:27 Uhr

  • Übrigens auch eine positive Bewertung wert.

    – Philippe A.

    5. August 2014 um 20:38 Uhr

  • Oder Sie könnten nur eine zusätzliche Funktion schreiben, wie in oauhs Antwort. Es hängt davon ab, ob diese Funktionen wirklich trennbar sind oder nicht.

    – Jim Balter

    6. August 2014 um 11:19 Uhr


  • @JimBalter: Natürlich, aber “Welchen anderen Ansatz kennen/verwenden Sie?” lädt vielmehr zu Alternativen ein, die nicht alle akzeptiert werden können. Diese Art von Frage passt vielleicht nicht gut zu SO.

    – Clifford

    6. August 2014 um 14:45 Uhr

Nicht klar, warum Sie nisten oder brechen müssen. Ich mache das die ganze Zeit, wenn eine Sequenz beim ersten Fehler abspringen muss:

// process

if (success) {
  // more process
}

if (success) {
  // still more process
}

if (success) {
  // even more process
}

Fiddle Bits hat einen gemeinsamen Ansatz bereitgestellt. Ein weiterer üblicher Ansatz besteht darin, eine einzelne Statusvariable/ein einzelnes Flag zu verwenden, um ein ähnliches Ergebnis zu erzielen.

bool bErr = false;

if (!bErr && success) {
   // do something
} else {
   bErr = true;
}
if (!bErr && success2) {
   // do something
} else {
   bErr = true;
}

if (bErr) {
   // hanlde cleanup from errors
}

  • Sie könnten die if-Anweisungen in ändern if (!(bErr |= !success)) {...} (oder etwas Ähnliches) und verzichten Sie auf diese ausführlichen Else-Anweisungen (meine Meinung)

    – Serge

    5. August 2014 um 18:06 Uhr


  • @Serge Der Punkt der else Anweisungen besteht darin, das Fehler-Flag zu setzen. Der Punkt dabei ist, dass, wenn ein einzelner Block fehlschlägt, das Fehler-Flag gesetzt wird und keine weiteren Blöcke ausgeführt werden.

    – Wolke

    5. August 2014 um 18:07 Uhr

  • In meinem Beispiel wird das bErr-Flag jedes Mal auf true gesetzt, wenn der Erfolg falsch ist, oder bleibt auf true gesetzt, selbst wenn der Erfolg wahr ist, und zwingt die if-Anweisung zum Scheitern. Gleiche Idee, andere Umsetzung. Aber ja, ich weiß, dass einige Leute Zuweisungsoperatoren nicht gerne in if-Anweisungen verwenden.

    – Serge

    5. August 2014 um 18:18 Uhr

  • @Serge Ja, Sie sollten Zuweisungen innerhalb einer Bewertung wirklich vermeiden (dh: if Aussage), da es “aus der Existenz kurzgeschlossen” werden könnte (en.wikipedia.org/wiki/Short-circuit_evaluation) oder innerhalb einer Protokollierungs- oder Debug-Anweisung, da der Optimierer oder sogar Einstellungen zur Kompilierzeit (dh: ifdefs) kann verhindern, dass die Abtretung jemals erfolgt.

    – Wolke

    5. August 2014 um 18:20 Uhr

  • Sie sind nicht schädlich, wenn die Leute wissen, was sie tun (im if Anweisungsszenario). Kurzschließen sollte gelten für die ||, && und ? Operatoren (gemäß der angegebenen Quelle) und nicht die if Aussage selbst. Dennoch ist es ein Kompromiss zwischen Prägnanz und Lesbarkeit.

    – Serge

    5. August 2014 um 18:31 Uhr

Eine andere Möglichkeit wäre die Verwendung einer einfachen Flag-Variablen

bool okay = true;

if(okay &= success){
    // process
}

if(okay &= success){
    // process
}

if(okay &= success){
    // process
}

  • Sie könnten die if-Anweisungen in ändern if (!(bErr |= !success)) {...} (oder etwas Ähnliches) und verzichten Sie auf diese ausführlichen Else-Anweisungen (meine Meinung)

    – Serge

    5. August 2014 um 18:06 Uhr


  • @Serge Der Punkt der else Anweisungen besteht darin, das Fehler-Flag zu setzen. Der Punkt dabei ist, dass, wenn ein einzelner Block fehlschlägt, das Fehler-Flag gesetzt wird und keine weiteren Blöcke ausgeführt werden.

    – Wolke

    5. August 2014 um 18:07 Uhr

  • In meinem Beispiel wird das bErr-Flag jedes Mal auf true gesetzt, wenn der Erfolg falsch ist, oder bleibt auf true gesetzt, selbst wenn der Erfolg wahr ist, und zwingt die if-Anweisung zum Scheitern. Gleiche Idee, andere Umsetzung. Aber ja, ich weiß, dass einige Leute Zuweisungsoperatoren nicht gerne in if-Anweisungen verwenden.

    – Serge

    5. August 2014 um 18:18 Uhr

  • @Serge Ja, Sie sollten Zuweisungen innerhalb einer Bewertung wirklich vermeiden (dh: if Aussage), da es “aus der Existenz kurzgeschlossen” werden könnte (en.wikipedia.org/wiki/Short-circuit_evaluation) oder innerhalb einer Protokollierungs- oder Debug-Anweisung, da der Optimierer oder sogar Einstellungen zur Kompilierzeit (dh: ifdefs) kann verhindern, dass die Abtretung jemals erfolgt.

    – Wolke

    5. August 2014 um 18:20 Uhr

  • Sie sind nicht schädlich, wenn die Leute wissen, was sie tun (im if Anweisungsszenario). Kurzschließen sollte gelten für die ||, && und ? Operatoren (gemäß der angegebenen Quelle) und nicht die if Aussage selbst. Dennoch ist es ein Kompromiss zwischen Prägnanz und Lesbarkeit.

    – Serge

    5. August 2014 um 18:31 Uhr

1411720cookie-checkAlternativen zu „while (1)“, um die Verzweigung zu vereinfachen [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy