Bjarne Stroustrup (C ++ - Ersteller) hat einmal gesagt, dass er "do / while" -Schleifen vermeidet und den Code lieber als "while" -Schleife schreibt. [Siehe Zitat unten.]
Seit ich das gehört habe, habe ich festgestellt, dass dies wahr ist. Was sind deine Gedanken? Gibt es ein Beispiel, in dem ein "do / while" viel sauberer und leichter zu verstehen ist, als wenn Sie stattdessen ein "while" verwendet haben?
Antwort auf einige der Antworten: Ja, ich verstehe den technischen Unterschied zwischen "do / while" und "while". Dies ist eine tiefere Frage zur Lesbarkeit und Strukturierung von Code mit Schleifen.
Lassen Sie mich einen anderen Weg fragen: Angenommen, Ihnen wurde die Verwendung von "do / while" verboten. Gibt es ein realistisches Beispiel, bei dem Sie keine andere Wahl haben, als unreinen Code mit "while" zu schreiben?
Aus "The C ++ Programming Language", 6.3.3:
Nach meiner Erfahrung ist die do-Anweisung eine Quelle von Fehlern und Verwirrung. Der Grund ist, dass sein Körper immer einmal ausgeführt wird, bevor die Bedingung bewertet wird. Damit der Körper jedoch richtig funktioniert, muss etwas, das dem Zustand sehr ähnlich ist, auch beim ersten Mal Bestand haben. Öfter als ich vermutet hätte, habe ich festgestellt, dass diese Bedingung weder beim ersten Schreiben und Testen des Programms noch später, nachdem der vorhergehende Code geändert wurde, nicht wie erwartet gilt. Ich bevorzuge auch die Bedingung "vorne, wo ich es sehen kann". Infolgedessen neige ich dazu, Do-Statements zu vermeiden. -Bjarne
Antworten:
Ja, ich stimme zu, dass do while-Schleifen in eine while-Schleife umgeschrieben werden können. Ich bin jedoch nicht der Meinung, dass es besser ist, immer eine while-Schleife zu verwenden. do while wird immer mindestens einmal ausgeführt und das ist eine sehr nützliche Eigenschaft (das typischste Beispiel ist die Eingabeprüfung (über die Tastatur)).
#include <stdio.h> int main() { char c; do { printf("enter a number"); scanf("%c", &c); } while (c < '0' || c > '9'); }
Dies kann natürlich in eine while-Schleife umgeschrieben werden, aber dies wird normalerweise als viel elegantere Lösung angesehen.
quelle
char
als speziellen Marker reservieren müssen . Obwohl dies die Logik hier nicht beeinflusst, bedeutet dies im Allgemeinen, dass Sie einen Strom beliebiger Zeichen nicht mehr verarbeiten können, da er möglicherweise Zeichen mit dem Wert -1 enthält.do-while ist eine Schleife mit einer Nachbedingung. Sie benötigen es in Fällen, in denen der Schleifenkörper mindestens einmal ausgeführt werden soll. Dies ist für Code erforderlich, der eine Aktion benötigt, bevor der Schleifenzustand sinnvoll ausgewertet werden kann. Mit der while-Schleife müssten Sie den Initialisierungscode von zwei Sites aus aufrufen. Mit do-while können Sie ihn nur von einer Site aus aufrufen.
Ein anderes Beispiel ist, wenn Sie bereits ein gültiges Objekt haben, wenn die erste Iteration gestartet werden soll, und daher vor Beginn der ersten Iteration nichts ausführen möchten (einschließlich der Bewertung der Schleifenbedingungen). Ein Beispiel sind die Win32-Funktionen von FindFirstFile / FindNextFile: Sie rufen FindFirstFile auf, das entweder einen Fehler oder ein Suchhandle für die erste Datei zurückgibt, und rufen dann FindNextFile auf, bis ein Fehler zurückgegeben wird.
Pseudocode:
Handle handle; Params params; if( ( handle = FindFirstFile( params ) ) != Error ) { do { process( params ); //process found file } while( ( handle = FindNextFile( params ) ) != Error ) ); }
quelle
for(handle = FindFirstFile(params); handle != Error; handle = FindNextFile(params)) {}
.do { ... } while (0)
ist ein wichtiges Konstrukt, damit sich Makros gut verhalten.Auch wenn es in echtem Code unwichtig ist (dem ich nicht unbedingt zustimme), ist es wichtig, um einige der Mängel des Präprozessors zu beheben.
Bearbeiten: Ich bin in eine Situation geraten, in der do / while heute in meinem eigenen Code viel sauberer war. Ich habe eine plattformübergreifende Abstraktion der gepaarten LL / SC- Anweisungen vorgenommen. Diese müssen wie folgt in einer Schleife verwendet werden:
do { oldvalue = LL (address); newvalue = oldvalue + 1; } while (!SC (address, newvalue, oldvalue));
(Experten stellen möglicherweise fest, dass der alte Wert in einer SC-Implementierung nicht verwendet wird, er ist jedoch enthalten, damit diese Abstraktion mit CAS emuliert werden kann.)
LL und SC sind ein hervorragendes Beispiel für eine Situation, in der do / while deutlich sauberer ist als die entsprechende while-Form:
Aus diesem Grund bin ich äußerst enttäuscht darüber, dass Google Go das Do-While-Konstrukt entfernt hat .
quelle
if
Anweisung ohne geschweifte Klammern angezeigt werden .Dies ist nützlich, wenn Sie etwas "tun" möchten, bis "eine Bedingung erfüllt ist".
Es kann in einer while-Schleife wie folgt verfälscht werden:
while(true) { // .... code ..... if(condition_satisfied) break; }
quelle
(Vorausgesetzt, Sie kennen den Unterschied zwischen den beiden)
Do / While eignet sich zum Booten / Vorinitialisieren von Code, bevor Ihr Zustand überprüft und die while-Schleife ausgeführt wird.
quelle
In unseren Codierungskonventionen
Wir haben also fast nie ein
do {} while(xx)
Weil:int main() { char c; do { printf("enter a number"); scanf("%c", &c); } while (c < '0' || c > '9'); }
wird umgeschrieben in:
int main() { char c(0); while (c < '0' || c > '9'); { printf("enter a number"); scanf("%c", &c); } }
und
Handle handle; Params params; if( ( handle = FindFirstFile( params ) ) != Error ) { do { process( params ); //process found file } while( ( handle = FindNextFile( params ) ) != Error ) ); }
wird umgeschrieben in:
Params params(xxx); Handle handle = FindFirstFile( params ); while( handle!=Error ) { process( params ); //process found file handle = FindNextFile( params ); }
quelle
if (...) break;
), oft sogar kombiniert mitwhile(true)
resp.for(;;)
. Was macht es alsodo ... while
schlimmer, wenn Sie zumindest wissen, wo Sie nach der Krankheit suchen müssen?if (bCondition) break;
Design als schlecht und ermutigten zur Verwendung geeigneter Schleifenbedingungen . Natürlich gibt es einige Fälle, in denen dies meistens nicht vermieden werden kannbreak
undcontinue
Anzeichen für eine verzögerte Codierung sind.Die folgende allgemeine Redewendung scheint mir sehr einfach zu sein:
do { preliminary_work(); value = get_value(); } while (not_valid(value));
Das zu vermeidende Umschreiben
do
scheint zu sein:value = make_invalid_value(); while (not_valid(value)) { preliminary_work(); value = get_value(); }
Diese erste Zeile wird verwendet, um sicherzustellen, dass der Test beim ersten Mal immer als wahr ausgewertet wird. Mit anderen Worten, der Test ist beim ersten Mal immer überflüssig. Wenn dieser überflüssige Test nicht vorhanden wäre, könnte man auch die anfängliche Zuordnung weglassen. Dieser Code erweckt den Eindruck, dass er sich selbst bekämpft.
In solchen Fällen ist das
do
Konstrukt eine sehr nützliche Option.quelle
Es geht nur um Lesbarkeit .
Mehr lesbarer Code führt zu weniger Kopfschmerzen bei der Codepflege und einer besseren Zusammenarbeit.
Andere Überlegungen (wie die Optimierung) sind in den meisten Fällen bei weitem weniger wichtig.
Ich werde näher darauf eingehen, da ich hier einen Kommentar erhalten habe:
Wenn Sie ein Code-Snippet A haben , das verwendet
do { ... } while()
wird und das besser lesbar ist als daswhile() {...}
entsprechende B , dann würde ich für A stimmen . Wenn Sie es vorziehen , B , da Sie die Schleifenbedingung „vorne“ zu sehen, und Sie denken , es besser lesbar ist (und damit wartbar, etc.) - dann gehen Sie nach rechts weiter, Verwendung B .Mein Punkt ist: Verwenden Sie den Code, der für Ihre Augen (und die Ihrer Kollegen) besser lesbar ist. Die Wahl ist natürlich subjektiv.
quelle
Es ist meiner Meinung nach nur eine persönliche Entscheidung.
Meistens können Sie eine Möglichkeit finden, eine do ... while-Schleife in eine while-Schleife umzuschreiben. aber nicht unbedingt immer. Es kann auch logischer sein, manchmal eine do while-Schleife zu schreiben, um sie an den Kontext anzupassen, in dem Sie sich befinden.
Wenn Sie oben schauen, die Antwort von TimW, spricht es für sich. Der zweite mit Griff ist meiner Meinung nach besonders chaotisch.
quelle
Lesen Sie den Satz zum strukturierten Programm . Ein do {} while () kann immer in while () do {} umgeschrieben werden. Sequenz, Auswahl und Iteration sind alles, was jemals benötigt wird.
Da alles, was im Schleifenkörper enthalten ist, immer in eine Routine eingekapselt werden kann, muss die Schmutzigkeit der Verwendung von while () do {} niemals schlimmer werden als
LoopBody() while(cond) { LoopBody() }
quelle
for (bool first=true; first || cond; first=false) { ... }
. Dies würde keinen Code duplizieren, sondern eine zusätzliche Variable einführen.Dies ist die sauberste Alternative zu Do-While, die ich gesehen habe. Es ist die für Python empfohlene Redewendung, die keine Do-While-Schleife hat.
Ein Nachteil ist , dass Sie nicht ein haben
continue
in der ,<setup code>
da er die Abbruchbedingung springen würde, aber keines der Beispiele, die die Vorteile der do-while Notwendigkeit zeigen eine vor der Bedingung fortsetzen.while (true) { <setup code> if (!condition) break; <loop body> }
Hier wird es auf einige der besten Beispiele der oben genannten Do-While-Schleifen angewendet.
while (true); { printf("enter a number"); scanf("%c", &c); if (!(c < '0' || c > '9')) break; }
Dieses nächste Beispiel ist ein Fall, in dem die Struktur besser lesbar ist als eine Pause, da der Zustand in der Nähe der Oberseite gehalten wird, da er
//get data
normalerweise kurz ist, der//process data
Abschnitt jedoch lang sein kann.while (true); { // get data if (data == null) break; // process data // process it some more // have a lot of cases etc. // wow, we're almost done. // oops, just one more thing. }
quelle
break
!Ich benutze sie selten nur aus folgenden Gründen:
Auch wenn die Schleife nach einer Nachbedingung sucht, müssen Sie diese Nachbedingung in Ihrer Schleife überprüfen, damit Sie die Nachbedingung nicht verarbeiten.
Nehmen Sie den Beispielpseudocode:
do { // get data // process data } while (data != null);
Klingt theoretisch einfach, aber in realen Situationen würde es wahrscheinlich so aussehen:
do { // get data if (data != null) { // process data } } while (data != null);
Der zusätzliche "Wenn" -Check ist es IMO einfach nicht wert. Ich habe nur sehr wenige Fälle gefunden, in denen es knapper ist, eine Do-While-Schleife anstelle einer While-Schleife durchzuführen. YMMV.
quelle
while() {...}
die richtige Wahl -while() {...}
genau für den Fall, in dem Sie ausgeführt werden müssen Code null oder mehrmals basierend auf einer Bedingung. Dies bedeutet jedoch nicht, dass es niemals Fälle gibtdo {...} while()
, in denen Sie wissen, dass Code mindestens einmal ausgeführt werden muss, bevor die Bedingung überprüft wird (siehe die ausgewählte Antwort von @ david-božjak).Als Antwort auf eine Frage / einen Kommentar von unbekannt (google) auf die Antwort von Dan Olson:
"do {...} while (0) ist ein wichtiges Konstrukt, damit sich Makros gut verhalten."
#define M do { doOneThing(); doAnother(); } while (0) ... if (query) M; ...
Sehen Sie, was ohne das passiert
do { ... } while(0)
? Es wird immer doAnother () ausgeführt.quelle
Eine do-while-Schleife kann immer als while-Schleife umgeschrieben werden.
Ob Sie nur while-Loops oder while-, do-while- und for-Loops (oder eine beliebige Kombination davon) verwenden, hängt weitgehend von Ihrem Geschmack für Ästhetik und den Konventionen des Projekts ab, an dem Sie arbeiten.
Persönlich bevorzuge ich while-Schleifen, weil dies meiner Meinung nach das Denken über Schleifeninvarianten vereinfacht.
Ob es Situationen gibt, in denen Sie Do-While-Schleifen benötigen: Anstelle von
do { loopBody(); } while (condition());
du kannst immer
loopBody(); while(condition()) { loopBody(); }
Also, nein, Sie müssen niemals do-while verwenden, wenn Sie dies aus irgendeinem Grund nicht können. (Natürlich verstößt dieses Beispiel gegen DRY, aber es ist nur ein Proof-of-Concept. Nach meiner Erfahrung gibt es normalerweise eine Möglichkeit, eine Do-While-Schleife in eine While-Schleife umzuwandeln und DRY in keinem konkreten Anwendungsfall zu verletzen.)
"Man soll sich an örtliche Gepflogenheiten halten."
Übrigens: Das Zitat, das Sie suchen, ist vielleicht dieses ([1], letzter Absatz von Abschnitt 6.3.3):
(Hinweis: Dies ist meine Übersetzung der deutschen Ausgabe. Wenn Sie zufällig die englische Ausgabe besitzen, können Sie das Zitat so bearbeiten, dass es seinem ursprünglichen Wortlaut entspricht. Leider hasst Addison-Wesley Google.)
[1] B. Stroustrup: Die Programmiersprache C ++. 3. Auflage. Addison-Wessley, Reading, 1997.
quelle
Zunächst stimme ich zu, dass dies
do-while
weniger lesbar ist alswhile
.Aber ich bin erstaunt, dass nach so vielen Antworten niemand darüber nachgedacht hat, warum es
do-while
überhaupt in der Sprache existiert. Der Grund ist die Effizienz.Nehmen wir an, wir haben eine
do-while
Schleife mitN
Bedingungsprüfungen, bei der das Ergebnis der Bedingung vom Schleifenkörper abhängt. Wenn wir es dann durch einewhile
Schleife ersetzen , erhalten wirN+1
stattdessen Bedingungsprüfungen, bei denen die zusätzliche Prüfung sinnlos ist. Das ist keine große Sache, wenn die Schleifenbedingung nur eine Überprüfung eines ganzzahligen Werts enthält, aber sagen wir, wir habensomething_t* x = NULL; while( very_slowly_check_if_something_is_done(x) ) { set_something(x); }
Dann ist der Funktionsaufruf in der ersten Runde der Schleife redundant: Wir wissen bereits, dass
x
noch nichts festgelegt ist. Warum also sinnlosen Overhead-Code ausführen?Zu diesem Zweck verwende ich häufig do-while, wenn ich eingebettete Echtzeitsysteme codiere, bei denen der Code innerhalb des Zustands relativ langsam ist (Überprüfung der Antwort von einem langsamen Hardware-Peripheriegerät).
quelle
while (x == null || check(x)) . . .
AdresseBetrachten Sie so etwas:
int SumOfString(char* s) { int res = 0; do { res += *s; ++s; } while (*s != '\0'); }
Es kommt also vor, dass '\ 0' 0 ist, aber ich hoffe, Sie verstehen den Punkt.
quelle
SumOfString("")
.Mein Problem mit do / while liegt ausschließlich in der Implementierung in C. Aufgrund der Wiederverwendung des Schlüsselworts while werden häufig Personen ausgelöst, da dies wie ein Fehler aussieht.
Wenn während hatte nur reserviert , während Schleifen und tun / während hatte in geändert do / bis oder Wiederholung / bis , ich glaube nicht , die Schleife (was sicherlich praktisch ist , und die „richtige“ Weg , um Code einige Schleifen) würde dazu führen , so viel Ärger.
Ich habe vorher darüber in Bezug auf JavaScript geschimpft , das auch diese traurige Wahl von C geerbt hat.
quelle
while
von immerdo-while
in die gleiche Zeile wie das}
. Und am Ende steht immer ein Semikolon. Kein C-Programmierer mit mindestens ein wenig Kompetenz wird verwirrt oder "gestolpert", wenn er auf die Linie stößt :} while(something);
.Nun, vielleicht geht das ein paar Schritte zurück, aber im Fall von
do { output("enter a number"); int x = getInput(); //do stuff with input }while(x != 0);
Es wäre möglich, aber nicht unbedingt lesbar zu verwenden
int x; while(x = getInput()) { //do stuff with input }
Wenn Sie nun eine andere Zahl als 0 verwenden möchten, um die Schleife zu verlassen
while((x = getInput()) != 4) { //do stuff with input }
Aber auch hier gibt es einen Verlust an Lesbarkeit, ganz zu schweigen davon, dass es als schlechte Praxis angesehen wird, eine Zuweisungsanweisung innerhalb einer Bedingung zu verwenden. Ich wollte nur darauf hinweisen, dass es kompaktere Möglichkeiten gibt, dies zu tun, als einen "reservierten" Wert zuzuweisen zu der Schleife, die der erste Durchlauf ist.
quelle
Ich mag das Beispiel von David Božjak. Um Devil's Advocate zu spielen, können Sie jedoch den Code, den Sie mindestens einmal ausführen möchten, immer in eine separate Funktion zerlegen, um die vielleicht am besten lesbare Lösung zu erzielen. Zum Beispiel:
int main() { char c; do { printf("enter a number"); scanf("%c", &c); } while (c < '0' || c > '9'); }
könnte dies werden:
int main() { char c = askForCharacter(); while (c < '0' || c > '9') { c = askForCharacter(); } } char askForCharacter() { char c; printf("enter a number"); scanf("%c", &c); return c; }
(Verzeihen Sie eine falsche Syntax; ich bin kein C-Programmierer)
quelle