webentwicklung-frage-antwort-db.com.de

Gibt es jemals eine Notwendigkeit für eine "do {...} while ()" - Schleife?

Bjarne Stroustrup (C++ Creator) sagte einmal, dass er "do/while" -Schleifen vermeidet und stattdessen den Code lieber als "while" -Schleife schreibt. [Siehe Zitat weiter unten.]

Seit ich das gehört habe, habe ich festgestellt, dass dies wahr ist. Was sind deine Gedanken? Gibt es ein Beispiel, bei dem ein "do/while" viel sauberer und leichter zu verstehen ist, als wenn Sie stattdessen ein "while" verwenden würden?

Als Antwort auf einige Antworten: Ja, ich verstehe den technischen Unterschied zwischen "do/while" und "while". Dies ist eine tiefere Frage zur Lesbarkeit und zum Strukturieren von Code, der Schleifen beinhaltet.

Lassen Sie mich einen anderen Weg fragen: Nehmen Sie an, Sie durften "do/while" nicht verwenden. Gibt es ein realistisches Beispiel, bei dem Sie keine andere Wahl haben, als mit "while" unreinen Code zu schreiben?

Aus "Die Programmiersprache C++", 6.3.3:

Nach meiner Erfahrung ist die Do-Anweisung eine Quelle von Fehlern und Verwirrung. Der Grund ist, dass der Rumpf immer einmal ausgeführt wird, bevor die Bedingung bewertet wird. Damit der Körper jedoch richtig funktioniert, muss etwas, das der Bedingung sehr ähnlich ist, auch beim ersten Mal bestehen bleiben. Häufiger als ich gedacht hätte, habe ich festgestellt, dass diese Bedingung nicht wie erwartet gilt, wenn das Programm zum ersten Mal geschrieben und getestet wurde oder später, nachdem der Code vor ihm geändert wurde. Ich bevorzuge auch die Bedingung "vorne wo ich sie sehen kann". Folglich neige ich dazu, Do-Statements zu vermeiden. -Barne

65
Dustin Boswell

Ja, ich stimme zu, dass do-Schleifen zu einer while-Schleife umgeschrieben werden können, jedoch stimme ich nicht zu, dass die Verwendung einer while-Schleife immer besser ist. do, während Sie immer mindestens einmal ausgeführt werden. Dies ist eine sehr nützliche Eigenschaft (das typische 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 eine viel elegantere Lösung betrachtet.

86
David Božjak

do-while ist eine Schleife mit einer Nachbedingung. Sie benötigen es, wenn der Schleifenkörper mindestens einmal ausgeführt werden soll. Dies ist notwendig für Code, der etwas Aktion erfordert, bevor die Schleifenbedingung sinnvoll ausgewertet werden kann. Mit while-Schleife müssten Sie den Initialisierungscode von zwei Standorten aus aufrufen, mit do-while können Sie ihn nur von einem Standort aus aufrufen.

Ein anderes Beispiel ist, wenn Sie bereits über ein gültiges Objekt verfügen, wenn die erste Iteration gestartet werden soll. Sie möchten also nichts ausführen (einschließlich Schleifenbedingungsbewertung), bevor die erste Iteration beginnt. Ein Beispiel ist mit Win32-Funktionen FindFirstFile/FindNextFile: Sie rufen FindFirstFile auf, das entweder einen Fehler oder ein Such-Handle an die erste Datei zurückgibt, und dann FindNextFile, 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 ) );
}
38
sharptooth

do { ... } while (0) ist ein wichtiges Konstrukt, damit sich Makros gut verhalten.

Auch wenn es im realen Code unwichtig ist (mit dem ich nicht unbedingt einverstanden bin), ist es wichtig, um einige der Defizite des Präprozessors zu beheben.

Edit: Ich bin in eine Situation geraten, in der do/while heute in meinem eigenen Code viel sauberer war. Ich machte eine plattformübergreifende Abstraktion der paarweisen LL/SC -Anweisungen. Diese müssen wie folgt in einer Schleife verwendet werden:

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

(Experten werden möglicherweise erkennen, dass oldvalue in einer SC -Implementierung nicht verwendet wird, sie 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 wesentlich sauberer ist als die entsprechende 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 entfernen möchte .

17
Dan Olson

Dies ist nützlich, wenn Sie "etwas" tun wollen, bis "eine Bedingung erfüllt ist".

Es kann in einer while-Schleife wie folgt gefälscht werden:

while(true)
{

    // .... code .....

    if(condition_satisfied) 
        break;
}
7
hasen

Die folgende allgemeine Redewendung erscheint mir sehr einfach:

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

Die Umschreibung zur Vermeidung von 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 true ergibt. Mit anderen Worten, der Test ist beim ersten Mal immer überflüssig. Wenn dieser überflüssige Test nicht da wäre, könnte man auch die Erstzuteilung weglassen. Dieser Code vermittelt den Eindruck, dass er sich selbst bekämpft.

In solchen Fällen ist das Konstrukt do eine sehr nützliche Option.

6
Svante

(Vorausgesetzt, Sie kennen den Unterschied zwischen den beiden)

Do/While ist gut für das Bootstrapping/Vorinitialisieren von Code, bevor Ihre Bedingung geprüft und die while-Schleife ausgeführt wird.

6
Suvesh Pratapa

In unseren Codierungskonventionen

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

Wir haben also fast nie eine do {} while(xx) Denn:

int main() {
    char c;

    do {
        printf("enter a number");
        scanf("%c", &c);

    } while (c < '0' ||  c > '9'); 
}

wird neu geschrieben 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 neu geschrieben in:

Params params(xxx);
Handle handle = FindFirstFile( params );
while( handle!=Error ) {
    process( params ); //process found file
    handle = FindNextFile( params );
}
6
TimW

Es geht um Lesbarkeit .
.__ lesbarer Code führt zu weniger Kopfschmerzen bei der Codewartung und verbesserter Zusammenarbeit.
Andere Überlegungen (z. B. Optimierung) sind in den meisten Fällen bei weitem weniger wichtig.
Ich werde ausführlicher darauf eingehen, da ich hier einen Kommentar erhielt:
Wenn Sie ein Code-Snippet A haben, das do { ... } while() verwendet, und es besser lesbar ist als das while() {...} -Äquivalent B, dann würde ich dafür stimmen A. Wenn Sie B bevorzugen, da Sie die Schleifenbedingung "Vorne" sehen, und Sie denken, es ist besser lesbar (und damit wartbar usw.) -, fahren Sie fort und verwenden Sie . B.
Mein Standpunkt ist: Verwenden Sie den Code, der für Ihre Augen (und für Ihre Kollegen) besser lesbar ist. Die Wahl ist natürlich subjektiv.

5
Ron Klein

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, eine do-while-Schleife zu schreiben, um den Kontext, in dem Sie sich befinden, anzupassen.

Wenn Sie sich oben die Antwort von TimW ansehen, spricht dies für sich. Der zweite mit Griff ist meiner Meinung nach besonders unordentlich.

3

Dies ist die sauberste Alternative, die ich gesehen habe. Dies ist das für Python empfohlene Idiom, das keine do-while-Schleife hat. 

Ein Nachteil ist, dass Sie keine continue im <setup code> haben können, da dies die Abbruchbedingung springt, aber keines der Beispiele, die die Vorteile des Do-while zeigen, muss vor der Bedingung fortgesetzt werden.

while (true) {
       <setup code>
       if (!condition) break;
       <loop body>
}

Hier wird es auf einige der besten Beispiele der oben genannten Do-while-Loops 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 ein Do-while-Vorgang, da die Bedingung in der Nähe des oberen Bereichs gehalten wird, da //get data normalerweise kurz ist, der //process data-Abschnitt jedoch möglicherweise lang ist.

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.
} 
3
dansalmo

Lesen Sie den strukturierten Programmsatz . Ein do {} while () kann immer in while () do {} umgeschrieben werden. Sequenz, Auswahl und Iteration sind alles, was immer benötigt wird.

Da das, was im Schleifenkörper enthalten ist, immer in eine Routine eingebettet werden kann, muss die Schmutzigkeit des Gebrauchs während () nie schlimmer werden

LoopBody()
while(cond) {
    LoopBody()
}
2
John Pirie

Eine do-while-Schleife kann immer als while-Schleife neu geschrieben werden.

Ob Sie nur while-Schleifen oder While-Do-Do-Loops oder For-Loops (oder Kombinationen davon) verwenden, hängt stark von Ihrem Geschmack an der Ästhetik und den Konventionen des Projekts ab, an dem Sie gerade arbeiten.

Persönlich bevorzuge ich while-loops, da dies das Denken über Schleifeninvarianten IMHO 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 tun 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 bei keiner konkreten Verwendung zu verletzen Fall.)

"Man soll sich an örtliche Gepflogenheiten halten."

BTW: Das Zitat, nach dem 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 ein ähnlicher Zustand wie der Endzustand im ersten Durchgang gelten. Mehr als ich erwartet hatte, waren diese Bedingungen nicht zutreffend. Dies war sowohl der Fall, als ich das betreffende Programm von Grund auf neu geschrieben und dann getestet habe, als auch nach einer Änderung des Codes. Außerdem bevorzuge ich die Bedingung "im Voraus, wo ich sie sehen kann". Ich neige daher dazu, Do-Statements zu vermeiden.

(Hinweis: Dies ist meine Übersetzung der deutschen Ausgabe. Wenn Sie die englische Ausgabe besitzen, können Sie das Zitat an seine ursprüngliche Formulierung anpassen. Leider hasst Addison-Wesley Google nicht.)

[1] B. Stroustrup: Die Programmiersprache C++. 3. Auflage. Addison-Wessley, Reading, 1997.

2
Tobias

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

Aber ich bin erstaunt, dass nach so vielen Antworten niemand nachgedacht hat, warum do-while überhaupt in der Sprache existiert. Der Grund ist die Effizienz. 

Nehmen wir an, wir haben eine do-while-Schleife mit N-Zustandsüberprüfungen, bei denen das Ergebnis der Bedingung vom Schleifenkörper abhängt. Wenn wir es dann durch eine while-Schleife ersetzen, erhalten wir stattdessen N+1-Zustandsüberprü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, dass wir dies tun

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 überflüssig: Wir wissen bereits, dass x noch nichts eingestellt ist. Warum also einen sinnlosen Overhead-Code ausführen? 

Ich verwende häufig do-while für genau diesen Zweck beim Codieren von eingebetteten Echtzeitsystemen, bei denen der Code innerhalb der Bedingung relativ langsam ist (Überprüfung der Antwort einiger langsamer Hardware-Peripheriegeräte).

2
Lundin

Ich verwende sie selten aus folgenden Gründen:

Obwohl die Schleife nach einer Nachbedingung sucht, müssen Sie immer noch nach dieser Nachbedingung in Ihrer Schleife suchen, damit Sie die Nachbedingung nicht verarbeiten können.

Nehmen Sie den Beispiel-Pseudo-Code:

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

Die zusätzliche "if" Prüfung lohnt sich einfach nicht IMO. Ich habe sehr wenige Fälle gefunden, in denen es schwieriger ist, ein Do-While statt einer While-Schleife auszuführen. YMMV.

2
rein

Auf eine Frage/Kommentar von unknown (google) zur 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 die do { ... } while(0) passiert? Es wird immer doAnother () ausgeführt.

2
Markus Schnell

Mein Problem mit do/while ist streng mit seiner Implementierung in C verbunden. Aufgrund der Wiederverwendung von while Schlüsselwort, es stört oft Menschen, weil es wie ein Fehler aussieht.

Wenn while nur für while Schleifen und do/while reserviert wurde wurde geändert in do/bis oder Wiederholung/bis, Ich glaube nicht, dass die Schleife (die sicherlich praktisch ist und die "richtige" Art, einige Schleifen zu codieren) so viel Ärger verursachen würde.

Ich habe mich schon vorher über JavaScript geärgert , das auch diese traurige Wahl von C geerbt hat.

1
Nosredna

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, wenn auch nicht unbedingt lesbar

int x;
while(x = getInput())
{
    //do stuff with input
}

Nun, wenn Sie eine andere Zahl als 0 verwenden wollten, um die Schleife zu beenden

while((x = getInput()) != 4)
{
    //do stuff with input
}

Aber auch hier geht die Lesbarkeit verloren, ganz zu schweigen davon, dass es eine schlechte Praxis ist, eine Zuweisung innerhalb einer Bedingung zu verwenden. Ich möchte nur darauf hinweisen, dass es kompaktere Möglichkeiten gibt, als einen reservierten Wert zuzuweisen zu der Schleife, die es der erste Durchlauf ist.

1
Felps

betrachten Sie so etwas wie:

int SumOfString(char* s)
{
    int res = 0;
    do
    {
        res += *s;
        ++s;
    } while (*s != '\0');
}

Es kommt vor, dass '\ 0' 0 ist, aber ich hoffe, Sie bekommen den Punkt.

1
Nefzen

Ich mag das Beispiel von David Božjak. Um den Befürworter des Teufels zu spielen, denke ich jedoch, dass Sie den Code, den Sie mindestens einmal ausführen möchten, jederzeit in einer separaten Funktion herausfiltern können, um die wahrscheinlich lesbarste Lösung zu erhalten. Zum Beispiel:

int main() {
    char c;

    do {
        printf("enter a number");
        scanf("%c", &c);

    } while (c < '0' ||  c > '9'); 
}

könnte das 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.)

0
Niko Bellic