Alternativen zu „while (1)“, um die Verzweigung zu vereinfachen [duplicate]
Lesezeit: 9 Minuten
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
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:
“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.
@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
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
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:
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
14117200cookie-checkAlternativen zu „while (1)“, um die Verzweigung zu vereinfachen [duplicate]yes
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ösungwhile(1) { if(error) break; }
, es wäre viel ehrlicher. Sie verwenden den Schleifenausgang als Goto-Label, ohne es irgendwo anzugeben. Das ist keinwhile
Schleife, es ist nur verschleierter Code.– JensG
6. August 2014 um 14:34 Uhr