Ist jemals eine "do {…} while ()" - Schleife erforderlich?


74

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


3
@brandaemon Code Review gab es im Juni '09 noch nicht. Was genau ist dein Punkt?
Mast

1
@ Brandaemon Das ist eine wirklich schlechte Empfehlung ...
Ismael Miguel

3
@brandaemon StackOverflow oder Programmers.SE. Die Codeüberprüfung gilt nur für voll funktionsfähigen Code und muss auch Code enthalten . Diese Frage hat überhaupt keinen Code. Das macht es bei der Codeüberprüfung nicht zum Thema.
Ismael Miguel

5
@brandaemon finden Sie in einer Anleitung zur Codeüberprüfung für Benutzer mit Stapelüberlauf .
Mathieu Guindon

1
@brandaemon Ich würde mir etwas Zeit nehmen, um diesen Meta-Beitrag zu lesen, der zu diesem Thema ziemlich umfangreich ist. Es bietet viele Einblicke, welche Fragen zu Programmers.StackExchange aktuell sind. Hoffe das hilft auch!
Enderland

Antworten:


95

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.


1
Hier ist die "while" -Version: char c = -1; while (c <'0' || c> '9') {printf (...); scanf (...); }
Dustin Boswell

46
@Dustin, IMO ist dein Zählerbeispiel weniger lesbar, weil du eine magische Zahl (-1) verwendest. In Bezug auf "besseren" Code (was natürlich subjektiv ist) denke ich, dass der ursprüngliche Code von @ Rekreativc besser ist, da er für das menschliche Auge besser lesbar ist .
Ron Klein

8
Ich bin mir nicht sicher, ob ich es eine "magische" Nummer nennen würde, aber auf jeden Fall könnten Sie es als "char c = '0' - 1;" initialisieren. wenn das klarer ist. Vielleicht widersetzen Sie sich wirklich der Tatsache, dass 'c' überhaupt einen Anfangswert hat? Ich bin damit einverstanden, dass es ein bisschen komisch ist, aber auf der anderen Seite ist eine Weile schön, weil es die Fortsetzung im Voraus angibt, sodass der Leser den Lebenszyklus der Schleife sofort versteht.
Dustin Boswell

6
Mein Einwand ist ja im Prinzip, dass 'c' überhaupt keinen beobachtbaren Anfangswert haben sollte. Deshalb würde mir die Do-While-Version besser gefallen. (Obwohl ich denke, Sie haben einen Punkt über die Lesbarkeit in der realen Welt.)
MQP

8
@Dustin: Der wichtige Nachteil Ihres Umschreibens ist, dass Sie ein Element (hier -1) aus der Domäne von charals 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.
j_random_hacker

39

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

8
eine Voraussetzung? Wenn die Bedingung nach einer Ausführung des Körpers überprüft wird, handelt es sich dann nicht um eine Nachbedingung? :)

8
Hier ist eine Neufassung, die meiner Meinung nach besser lesbar ist (und weniger verschachtelt ist). Handle handle = FindFirstFile (params); while (handle! = Fehler) {process (params); handle = FindNextFile (params); }
Dustin Boswell

Ja, falls Sie nicht für jeden Randfall eine aufwändige Fehlerbehandlung benötigen.
Scharfzahn

Können Sie nicht vermeiden, dass hier eine while-Schleife verwendet wird? while ((handle = FindNextFile (params))! = Fehler)) {process (params); } Ich meine, die Bedingung in der if-Klausel ist genau die gleiche wie die Bedingung in der Schleife.
Juan Pablo Califano

2
@Dustin: es könnte auch so gemacht werden (und sollte es wahrscheinlich sein) for(handle = FindFirstFile(params); handle != Error; handle = FindNextFile(params)) {}.
Falstro

18

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:

oldvalue = LL (address);
newvalue = oldvalue + 1;
while (!SC (address, newvalue, oldvalue))
{
  oldvalue = LL (address);
  newvalue = oldvalue + 1;
}

Aus diesem Grund bin ich äußerst enttäuscht darüber, dass Google Go das Do-While-Konstrukt entfernt hat .


Warum wird dies für Makros benötigt? Kannst du ein Beispiel geben?
Dustin Boswell

5
Schauen Sie sich < c2.com/cgi/wiki?TrivialDoWhileLoop > an. Präprozessor-Makros "beschäftigen" sich nicht mit der C-Syntax. Daher sind clevere Hacks wie diese erforderlich, um zu verhindern, dass Makros beispielsweise in einer ifAnweisung ohne geschweifte Klammern angezeigt werden .
Wesley

1
Es gibt eine Reihe von Gründen, die im Internet aufgeführt sind, aber ich konnte in ein paar Minuten Googeln keinen umfassenden Link finden. Das einfachste Beispiel ist, dass sich mehrzeilige Makros wie eine einzelne Zeile verhalten. Dies kann wichtig sein, da der Benutzer des Makros es möglicherweise in einer nicht in Klammern gesetzten einzeiligen for-Schleife verwendet. Wenn es nicht korrekt umbrochen wird, verhält sich diese Verwendung schlecht .
Dan Olson

Ich finde do while in #defines als ekelhaft. Dies ist einer der Gründe, warum der Präprozessor einen so schlechten Ruf hat.
Pod

1
Sie sollten Pod klarstellen, Sie mögen es einfach nicht ästhetisch, mögen die zusätzliche Sicherheit aus irgendeinem Grund nicht oder sind sich nicht einig, dass es notwendig ist? Hilf mir, zu verstehen.
Dan Olson

7

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

1
Tatsächlich ist dies NICHT gleichbedeutend mit "do {... code ...} while (Bedingung)", wenn "... code ..." eine 'continue'-Anweisung enthält. Die Regel für 'Weiter' ist, für eine Weile nach unten zu gehen.
Dustin Boswell

Guter Punkt, ich sagte jedoch "Fudge", was impliziert, dass es irgendwie falsch ist
hasen

6

(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.


6

In unseren Codierungskonventionen

  • wenn / während / ... Bedingungen keine Nebenwirkungen haben und
  • Variablen müssen initialisiert werden.

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

6
Sehen Sie den Fehler in Ihrem ersten Beispiel? Vielleicht hätten Sie mit der Do-While-Schleife das vermeiden können. :-)
Jouni K. Seppänen

3
Ich hasse es, Code mit do ... while-Schleifen zu lesen. Es ist, als würde Ihnen jemand Details zu etwas geben, das Ihnen überhaupt nicht wichtig ist. Nach 15 Minuten Geschwätz sagt er Ihnen, warum Sie sich überhaupt darum kümmern sollten. Machen Sie den Code lesbar. Sagen Sie mir im Voraus, wofür die Schleife gedacht ist, und lassen Sie mich nicht ohne Grund scrollen.
Kieveli

@ Kieveli - meine Gedanken auch. Ich denke, Programmierer sind es gewohnt, Bedingungen an der Spitze zu haben, wie es bei 'für' und 'wenn' der Fall ist. do / while ist wirklich eine Kuriosität.
Dustin Boswell

3
Die meisten Programmierer scheinen keine Probleme mit Bedingungen in der Mitte zu haben ( if (...) break;), oft sogar kombiniert mit while(true)resp. for(;;). Was macht es also do ... whileschlimmer, wenn Sie zumindest wissen, wo Sie nach der Krankheit suchen müssen?
Celtschk

@celtschk Alle meine früheren Arbeitsplätze betrachteten das 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 kann breakund continueAnzeichen für eine verzögerte Codierung sind.
mg30rg

6

Die folgende allgemeine Redewendung scheint mir sehr einfach zu sein:

do {
    preliminary_work();
    value = get_value();
} while (not_valid(value));

Das zu vermeidende Umschreiben doscheint 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 doKonstrukt eine sehr nützliche Option.


5

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 das while() {...}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.


1
niemand bezweifelte das, besonders nicht das OP. Dies beantwortet die Frage in keiner Weise.
Codymanix

3

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.


3

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

Die Frage ist, was passiert, wenn "LoopBody" lang wird (sagen wir 10 Zeilen) und Sie keine Funktion dafür erstellen möchten. Das Wiederholen dieser 10 Zeilen ist dumm. In vielen Fällen kann der Code jedoch umstrukturiert werden, um das Duplizieren dieser 10 Zeilen zu vermeiden, wobei jedoch immer noch ein "while" verwendet wird.
Dustin Boswell

1
Natürlich ist es für die Wartung ziemlich schlecht, zweimal denselben Code zu haben.
Nosredna

Ja, mit Sicherheit ermöglichen beide Schleifenoptionen einen einfacheren und saubereren Code. Mein Punkt war, dass seit Jahrzehnten bewiesen wurde, dass beide nicht unbedingt notwendig sind.

Ein weiteres Umschreiben wäre : for (bool first=true; first || cond; first=false) { ... }. Dies würde keinen Code duplizieren, sondern eine zusätzliche Variable einführen.
Celtschk

3

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 continuein 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 datanormalerweise kurz ist, der //process dataAbschnitt 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.
} 

Tolles Gegenbeispiel; mit break!
Herr Polywhirl

2

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.


Dies beantwortet die Frage nicht wirklich. In der Situation, in der Sie speziell anrufen, wo Sie sich möglicherweise bereits in einem Zustand befinden, in dem Sie den inneren Code nicht ausführen müssen, ist dies definitiv 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 gibt do {...} 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).
Chaser324

2

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.


1
Dafür brauchen Sie allerdings keine Pause. #define M {one (); zwei(); } wird genügen.
Simon H.

1
Aber dann wenn (Abfrage) M; sonst printf ("Hallo"); ist ein Syntaxfehler.
Jouni K. Seppänen

Nur wenn Sie ein setzen; nach dem M. Sonst funktioniert es gut.
Simon H.

3
Das ist jedoch schlimmer: Benutzer müssen sich bei jeder Verwendung Ihrer Makros daran erinnern, dass einige von ihnen zu einem Block anstelle eines Ausdrucks erweitert werden. Und wenn Sie einen haben, der derzeit ein ungültiger Ausdruck ist, und Sie die Implementierung in etwas ändern müssen, das zwei Anweisungen erfordert, wird sie jetzt zu einem Block erweitert und Sie müssen den gesamten aufrufenden Code aktualisieren. Makros sind schon schlimm genug: Der Makrocode sollte so weit wie möglich im Makrocode und nicht im aufrufenden Code enthalten sein.
Steve Jessop

@Markus: Wenn für dieses spezielle Beispiel do / while nicht in der Sprache verfügbar wäre, könnten Sie es durch #define M ((void) (doOneThing (), doAnother ()) ersetzen.
Steve Jessop

2

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):

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 getestet wird. Für das korrekte Funktionieren des Körpers muss jedoch im ersten Lauf ein ähnlicher Zustand wie im Endzustand gelten. Öfter als erwartet habe ich festgestellt, dass diese Bedingungen nicht zutreffen. Dies war sowohl der Fall, als ich das betreffende Programm von Grund auf neu schrieb und dann testete, als auch nach einer Änderung des Codes. Außerdem bevorzuge ich die Bedingung "vorne, wo ich sie sehen kann". Ich neige daher dazu, Do-Statements zu vermeiden.

(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.


12
"gotos? Luxus! Als ich ein Junge war, haben wir den Anweisungszeiger mit bloßen Händen gepackt und ihn auf den Code gezogen, den wir als nächstes ausführen wollten"
Steve Jessop

2
"Ziehen? Ihr glücklichen Bastarde. Wir mussten Sklaven peitschen, um unsere Anweisungszeiger zu drücken, was sehr schwierig war, weil wir unsere eigenen Sklaven waren. Dann würden unsere Mütter uns mit zerbrochenen Flaschen in den Schlaf verprügeln."
Kieveli

@Tobias - danke, dass du das Angebot gefunden hast. Ich habe es in die ursprüngliche Frage eingefügt (entnommen aus meiner englischen Ausgabe des Buches).
Dustin Boswell

@Tobias Hier ist ein Beispiel dafür, wie die umgeschriebene Version dem vollkommen klaren Code nur Verwirrung stiftete: Angenommen, Sie haben eine Schleifenzählervariable, die zählt, wie oft die Schleife ausgeführt wurde. Sollte sich ein solcher Zähler in Ihrer neu geschriebenen while-Schleife innerhalb der loopBody-Funktion oder innerhalb der while-Schleife befinden? Die Antwort lautet "es kommt darauf an", es gibt keine offensichtliche Antwort. Aber in der Do-While-Schleife hätte es keine Rolle gespielt.
Lundin

2

Zunächst stimme ich zu, dass dies do-whileweniger lesbar ist als while.

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-whileSchleife mit NBedingungsprüfungen, bei der das Ergebnis der Bedingung vom Schleifenkörper abhängt. Wenn wir es dann durch eine whileSchleife ersetzen , erhalten wir N+1stattdessen 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 haben

something_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 xnoch 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).


Das Hinzufügen eines Schecks zur while- while (x == null || check(x)) . . .Adresse
DB Fred

@DBFred Dies führt jedoch immer noch zu einer schlechteren Leistung, da Sie möglicherweise jeder Runde in der Schleife einen zusätzlichen Zweig hinzufügen.
Lundin

1

Betrachten 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.


Inwiefern ist {} während hier sauberer als während {}?
Codymanix

In While {} müssten Sie am Ende der Schleife Code hinzufügen.
Nefzen

Ich denke nicht, dass dies ein gutes Beispiel ist. Stellen Sie sich vor, was passiert mit SumOfString("").
Quinmars

1

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.


Aber jeder wichtige Codierungsstil da draußen ist konsistent, sie schreiben das whilevon immer do-whilein 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);.
Lundin

1

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.


0

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)

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.