'do ... while' vs. 'while'

83

Mögliche Duplikate:
While vs. Do While
Wann sollte ich do-while anstelle von while-Schleifen verwenden?

Ich programmiere jetzt schon eine Weile (2 Jahre Arbeit + 4,5 Jahre Abschluss + 1 Jahr Pre-College), und ich habe noch nie eine Do-While-Schleife verwendet, zu der ich im Kurs Einführung in die Programmierung nicht gezwungen wurde. Ich habe das wachsende Gefühl, dass ich falsch programmiere, wenn ich nie auf etwas so Grundlegendes stoße.

Könnte es sein, dass ich einfach nicht auf die richtigen Umstände gestoßen bin?

Was sind einige Beispiele, bei denen es notwendig wäre, ein Do-While anstelle eines While zu verwenden?

(Meine Schulausbildung war fast ausschließlich in C / C ++ und meine Arbeit ist in C #. Wenn es also eine andere Sprache gibt, in der dies absolut sinnvoll ist, weil Do-Whiles anders funktionieren, gelten diese Fragen nicht wirklich.)

Zur Verdeutlichung ... Ich kenne den Unterschied zwischen a whileund a do-while. Während prüft die Ausgangsbedingung und führt dann Aufgaben aus. do-whileführt Aufgaben aus und überprüft dann die Beendigungsbedingung.

mphair
quelle
41
Für das, was es wert ist, habe ich nach einem Jahrzehnt des Programmierens als meine Karriere ein oder zwei Mal eine Do-While-Schleife verwendet.
Matt Greer
@Matt - Dadurch fühle ich mich besser. Zumindest bedeutet das, dass ich nicht alleine bin.
mphair
2
Ich werde Matt unterstützen und dem noch ein Jahrzehnt hinzufügen. Ich benutze die do-while-Schleife auch selten.
Quentin-Starin
Viele Duplikate. Hier ist eine, die auf eine Reihe von ihnen verweist
gnovice
3
Die richtige "Hauptfrage" scheint stackoverflow.com/questions/224059/… zu sein
Donal Fellows

Antworten:

112

Wenn Sie immer möchten, dass die Schleife mindestens einmal ausgeführt wird. Es ist nicht üblich, aber ich benutze es von Zeit zu Zeit. Ein Fall, in dem Sie es möglicherweise verwenden möchten, ist der Versuch, auf eine Ressource zuzugreifen, für die möglicherweise ein erneuter Versuch erforderlich ist, z

do
{
   try to access resource...
   put up message box with retry option

} while (user says retry);
Bob Moore
quelle
3
Ich denke, dies würde unter dem Motto "Ich bin diesem Umstand nicht begegnet" folgen.
mphair
6
Ehrlich gesagt, ich stoße sehr selten auf einen Umstand, wenn ich nur ein Do verwenden muss ... währenddessen ...
Stan R.
1
Ich habe sie auch von Zeit zu Zeit benutzt, wenn auch selten. Nach einer Weile werden sie normalerweise auch in eine normale while-Schleife umgestaltet - nur weil sich die Bereiche um sie herum geändert haben und ein Do gemacht haben ... während sie ungeschickter waren als es zuerst war.
Joshua
2
Es ist ziemlich häufig. Aber ich verstehe nicht, warum Sie in Ihrem Beispiel keine while-Schleife verwenden würden. Und ich verstehe nicht, warum diese Antwort positiv bewertet wird.
1
@ Bob (Postleitzahl-Update): Wäre das nicht dasselbe wie pSink = m_sslSinks.GetFirstSink (); while (pSink) {// mache Sachen pSink = m_sslSinks.GetNextSink (); }
mphair
62

do-while ist besser, wenn der Compiler nicht in der Optimierung kompetent ist. do-while hat nur einen einzigen bedingten Sprung, im Gegensatz zu for und while, die einen bedingten Sprung und einen bedingungslosen Sprung haben. Bei CPUs, die über Pipelines verfügen und keine Verzweigungsvorhersage durchführen, kann dies einen großen Unterschied in der Leistung einer engen Schleife bewirken.

Da die meisten Compiler intelligent genug sind, um diese Optimierung durchzuführen, werden normalerweise alle im dekompilierten Code gefundenen Schleifen ausgeführt (wenn der Dekompiler überhaupt die Mühe hat, Schleifen aus rückwärts gerichteten lokalen Gotos zu rekonstruieren).

Ben Voigt
quelle
4
+1 zum Hinzufügen relevanter technischer Informationen zur Auswahl zwischen Schleifenformen.
Quentin-Starin
2
Nicht nur verfrüht, sondern auch nutzlos. Wenn Sie wirklich eine while-Schleife wollten, dh die Fähigkeit, 0 Mal zu iterieren, müssen Sie die Schleife in ein if einschließen, das diesen Sprung zurückbringt.
JeremyP
3
@Jeremy: Der Sprung, von dem du sprichst, befindet sich außerhalb der Schleife und wird nicht N-mal ausgeführt.
Ben Voigt
1
Berücksichtigen Sie auch die klassische Verwendung von do-while in Duffs Gerät (bei der eine while-Schleife in eine nicht gerollte do-while-Schleife mit einem größeren Schritt plus einer Sprungtabelle umgewandelt wird, um den Rest zu verarbeiten), die den Overhead der Schleife drastisch reduziert - in Anwendungen wie memset, memcmp, memcpy kann den Durchsatz um den Faktor 10 erhöhen.
Ben Voigt
@BenVoigt Ich weiß, dass Ihr Kommentar mittlerweile ein Jahrzehnt alt ist, aber Duffs Gerät sollte nicht ohne die sehr wichtige Einschränkung erwähnt werden - es ist bei modernen Compilern weniger effizient, da moderne Compiler in der Regel unkomplizierte Schleifen selbst intelligent als optimal für den Zielcomputer abrollen können , während sie im Allgemeinen keine Logik haben, um die Absicht eines Duff-Geräts herauszufinden, und es daher nicht annähernd so gut optimieren können. Dies ist eine großartige Lektion zum Optimieren von Code für einen Zielcomputer anstelle eines abstrakten "ausreichend fortgeschrittenen Optimierers".
mtraceur
11

Ich habe dies in einer TryDeleteDirectory-Funktion verwendet. Es war so etwas

do
{
    try
    {
        DisableReadOnly(directory);
        directory.Delete(true);
    }
    catch (Exception)
    {
        retryDeleteDirectoryCount++;
    }
} while (Directory.Exists(fullPath) && retryDeleteDirectoryCount < 4);
Chandam
quelle
Nett. Dies ist das erste Beispiel, das tatsächlich Sinn macht, in eine do ... while eingewickelt zu werden.
Joshua
4
Warum / wie ist das besser als ein Standard while?
Josh Stodola
Die Schleife wird immer einmal ausgeführt, daher möchten Sie möglicherweise etwas tun und es dann erneut versuchen, wenn es fehlschlägt. Wenn Sie eine normale while-Schleife verwenden, überprüfen Sie, ob sie fehlschlägt, bevor Sie sie bei der ersten Iteration ausgeführt haben.
NibblyPig
1
@SLC Aber möchten Sie nicht trotzdem überprüfen, ob das Verzeichnis bereits vorhanden ist?
Josh Stodola
1
@ Josh In diesem Beispiel ja. Wenn Sie jedoch versuchen, eine Verbindung zu einem ausgelasteten Server herzustellen, stellen Sie zuerst eine Verbindung her und prüfen, ob die Antwort "besetzt" war. Wenn ja, würden Sie es einige Male erneut versuchen, bevor Sie aufgeben.
NibblyPig
11

Do while ist nützlich, wenn Sie etwas mindestens einmal ausführen möchten. Nehmen wir als gutes Beispiel für die Verwendung von do while vs. while an, dass Sie Folgendes erstellen möchten: Ein Taschenrechner.

Sie können dies erreichen, indem Sie eine Schleife verwenden und nach jeder Berechnung prüfen, ob die Person das Programm beenden möchte. Jetzt können Sie wahrscheinlich davon ausgehen, dass die Person dies nach dem Öffnen des Programms mindestens einmal tun möchte, damit Sie Folgendes tun können:

do
{
    //do calculator logic here
    //prompt user for continue here
} while(cont==true);//cont is short for continue

quelle
2
Nun, da Sie es erwähnen, würde jede Art von konsolenbasierter menügesteuerter Oberfläche auch in einer do ... while-Schleife gut funktionieren. Sie können beispielsweise nach Optionen fragen (jeweils eine Taste) und nur dann beenden, wenn Sie fortfahren oder beenden wählen.
Joshua
7
continueist ein Schlüsselwort; D
Thomas Eding
Atrinithis: Lol ... ich bin ausgerutscht. Danke, dass du mich dort korrigiert hast.
== truewird nicht gebraucht. cont == trueWenn cont wahr ist, wird true zurückgegeben. Das ist völlig nutzlos.
Mr.DeleteMyMessages
10

Dies ist eine Art indirekte Antwort, aber diese Frage brachte mich dazu, über die Logik dahinter nachzudenken, und ich dachte, dies könnte es wert sein, geteilt zu werden.

Wie alle anderen gesagt haben, verwenden Sie eine do ... whileSchleife, wenn Sie den Body mindestens einmal ausführen möchten. Aber unter welchen Umständen möchten Sie das tun?

Nun, die offensichtlichste Klasse von Situationen, an die ich denken kann, ist, wenn der anfängliche ("nicht grundierte") Wert der Prüfbedingung der gleiche ist wie beim Beenden . Dies bedeutet, dass Sie den Schleifenkörper einmal ausführen müssen, um die Bedingung auf einen nicht existierenden Wert vorzubereiten, und dann die eigentliche Wiederholung basierend auf dieser Bedingung ausführen müssen. Da Programmierer so faul sind, hat sich jemand entschlossen, dies in eine Kontrollstruktur einzubinden.

So kann beispielsweise das Lesen von Zeichen von einer seriellen Schnittstelle mit Zeitüberschreitung die folgende Form annehmen (in Python):

response_buffer = []
char_read = port.read(1)

while char_read:
    response_buffer.append(char_read)
    char_read = port.read(1)

# When there's nothing to read after 1s, there is no more data

response = ''.join(response_buffer)

Beachten Sie die Vervielfältigung des Codes : char_read = port.read(1) . Wenn Python eine do ... whileSchleife hätte, hätte ich vielleicht verwendet:

do:
    char_read = port.read(1)
    response_buffer.append(char_read)
while char_read

Der zusätzliche Vorteil für Sprachen, die einen neuen Bereich für Schleifen erstellen: char_readVerschmutzt den Funktionsnamespace nicht. Beachten Sie aber auch, dass es einen besseren Weg gibt, dies zu tun, und zwar unter Verwendung des Python- NoneWerts:

response_buffer = []
char_read = None

while char_read != '':
    char_read = port.read(1)
    response_buffer.append(char_read)

response = ''.join(response_buffer)

Hier ist der Kern meines Punktes: In Sprachen mit nullbaren Typen tritt die Situation initial_value == exit_valueweitaus seltener auf, und das kann der Grund sein, warum Sie nicht darauf stoßen. Ich sage nicht, dass es nie passiert, weil es immer noch Zeiten gibt, in denen eine Funktion zurückkehrtNone , um eine gültige Bedingung anzuzeigen. Aber meiner eiligen und kurz überlegten Meinung nach würde dies viel mehr passieren, wenn die von Ihnen verwendeten Sprachen keinen Wert zulassen, der bedeutet: Diese Variable wurde noch nicht initialisiert.

Dies ist keine perfekte Argumentation: In der Realität bilden Nullwerte, da sie nun üblich sind, einfach ein weiteres Element der Menge gültiger Werte, die eine Variable annehmen kann. In der Praxis können Programmierer jedoch unterscheiden, ob sich eine Variable in einem vernünftigen Zustand befindet, der den Schleifenausgangszustand enthalten kann, und sich in einem nicht initialisierten Zustand befindet.

detly
quelle
Dies ist eine der interessantesten Antworten, die ich hier gelesen habe. Schöner Beitrag.
Bob Moore
Die beiden Versionen sind nicht gleichwertig. Wenn der erste Lesevorgang zurückgegeben wird None, whilefügt die Version dem Antwortpuffer korrekt nichts hinzu, aber Ihre doVersion fügt immer etwas hinzu.
David Stone
@DavidStone read()sollte niemals zurückkehren None. Wenn Sie eine Bibliothek dort verwenden, wo dies der Fall ist, gilt die Prämisse nicht (dh, dass der Sentinel-Wert nicht im Satz möglicher Prüfwerte enthalten ist).
Detly
Bei Ihrer letzten Bearbeitung sehe ich, was ich hätte sagen sollen, ''anstatt None. Eine Art Wert, der als falsch ausgewertet wird. Kreide es auf meine Unkenntnis mit Pythons readFunktion.
David Stone
@ DavidStone Ich verstehe nicht. Wenn read()zurückgegeben wird '', wird nichts an die Zeichenfolge angehängt, sie ist leer.
Detly
7

Ich habe sie ein bisschen benutzt, als ich in der Schule war, aber seitdem nicht mehr so ​​oft.

Theoretisch sind sie nützlich, wenn der Schleifenkörper vor der Prüfung der Ausgangsbedingungen einmal ausgeführt werden soll. Das Problem ist, dass ich für die wenigen Fälle, in denen ich die Prüfung nicht zuerst möchte, normalerweise die Exit-Prüfung in der Mitte des Schleifenkörpers und nicht ganz am Ende möchte . In diesem Fall bevorzuge ich es, das Bekannte for (;;)mit einem if (condition) exit;irgendwo im Körper zu verwenden.

In der Tat, wenn ich in Bezug auf die Schleifenausgangsbedingung etwas wackelig bin, finde ich es manchmal nützlich, die Schleife for (;;) {}bei Bedarf mit einer Exit-Anweisung zu schreiben , und wenn ich fertig bin, kann ich sehen, ob es möglich ist. " bereinigt "durch Verschieben von Initiierungen, Exit-Bedingungen und / oder Inkrementieren von Code in die forKlammern.

TED
quelle
5
Aus Neugier ... warum "für (;;)" statt "während (wahr)"?
mphair
4
Wahrscheinlich nur schmecken. Ich bin gewachsen, um for(;;)mit "Endlosschleife" gleichzusetzen , während while(true)ich innehalten und darüber nachdenken muss. Vielleicht liegt es daran, dass ich C codiert habe, bevor es eine gab true. Früher hatten wir nur TRUEein Makro, und wenn etwas fehlerhaft war, musste ich mich davon überzeugen, dass das Makro nicht 0irgendwie neu definiert wurde (es ist passiert!). Damit for(;;)gibt es keine Chance. Wenn Sie auf der while-Form bestehen, würde ich es fast lieber sehen while (1). Natürlich könnten sich einige fragen, ob es nicht der Buchstabe l ist ...
TED
1
@mphair: Ja, wenn Sie den Text notieren, den der Editor beim Kursivschreiben oder codeifyText einfügt , können Sie ihn einfach selbst direkt in das Kommentarfeld eingeben. Ich habe einige Leute gesehen, die Hyperlinks eingefügt haben, aber das ist mir ein bisschen zu hardcore.
TED
2
@TED: Meine bevorzugte Notation für die Schleife bis zum expliziten Beenden lautet "do {} while (1);". In der Arbeit mit eingebetteten Systemen lautet mein normales Schleifenparadigma übrigens "i = 10; do {} while (- i);". Bei engen Schleifen ist das viel schneller als bei (). Ich denke, es ist besser lesbar, dies konsistent als Schleifenmuster zu verwenden, wenn keine Aufwärtszählung erforderlich ist, als es in nicht leistungskritischen Fällen willkürlich für () zu verwenden.
Supercat
2
Zusätzlich zu while (1) und for (;;) disusion habe ich gesehen, wie Code für (EVER) verwendet wurde, wobei EVER als definiert wurde ;;
Asr
6

Eine Situation, in der Sie einen Code immer einmal und je nach Ergebnis möglicherweise mehrmals ausführen müssen. Das gleiche kann auch mit einer regulären whileSchleife erzeugt werden.

rc = get_something();
while (rc == wrong_stuff)
{
    rc = get_something();
}

do
{
    rc = get_something();
}
while (rc == wrong_stuff);
Edward Leno
quelle
6

do whileist, wenn Sie den Codeblock mindestens einmal ausführen möchten. whileAuf der anderen Seite wird nicht immer abhängig von den angegebenen Kriterien ausgeführt.

Spinon
quelle
5

So einfach ist das:

Vorbedingung gegen Nachbedingung

  • while (cond) {...} - Voraussetzung, führt den Code erst nach Überprüfung aus.
  • do {...} while (cond) - Nachbedingung, Code wird mindestens einmal ausgeführt.

Jetzt wo du das Geheimnis kennst ... benutze sie mit Bedacht :)

Ioan Paul Pirau
quelle
1
Wie gesagt, ich weiß, wie sie funktionieren, ich war mir einfach nicht sicher, in welchem ​​Fall einer Sinn über den anderen macht.
mphair
Es ist logisch, dass Sie do {} verwenden, während Sie sicher sind, dass Sie mindestens einmal die Schleife durchlaufen müssen. Wie zum Beispiel, wenn Sie eine Funktion haben, die ein Element in einem Array findet, und zuvor ein if-Array eingefügt haben.IsEmpty (), dann return; . Wenn diese Prüfung bestanden wurde, können Sie do {} while sicher verwenden. do-while ist auch leicht als sich wiederholende Schleife zu verstehen: "Ich esse, bis ich zufrieden bin" bedeutet do {eat ();} while (! zufrieden ();). Das Testen des Zustands vor der Schleife wird als sicherer angesehen. Deshalb wird es häufiger verwendet.
Ioan Paul Pirau
4

Ich sehe, dass diese Frage angemessen beantwortet wurde, möchte aber dieses sehr spezifische Anwendungsfallszenario hinzufügen. Möglicherweise verwenden Sie do ... häufiger.

do
{
   ...
} while (0)

wird häufig für mehrzeilige #defines verwendet. Beispielsweise:

#define compute_values     \
   area = pi * r * r;      \
   volume = area * h

Dies funktioniert in Ordnung für:

r = 4;
h = 3;
compute_values;

-aber- es gibt ein gotcha für:

if (shape == circle)  compute_values;

da dies erweitert zu:

if (shape == circle) area = pi *r * r;
volume = area * h;

Wenn Sie es in eine do ... while (0) -Schleife einschließen, wird es ordnungsgemäß zu einem einzelnen Block erweitert:

if (shape == circle)
  do
  {
    area = pi * r * r;
    volume = area * h;
  } while (0);
Brandon Horsley
quelle
2

Die bisherigen Antworten fassen die allgemeine Verwendung für Do-While zusammen. Das OP hat jedoch nach einem Beispiel gefragt. Hier ist eines: Benutzereingaben abrufen. Die Benutzereingabe ist jedoch möglicherweise ungültig. Sie fordern daher eine Eingabe an, validieren sie, fahren fort, wenn sie gültig ist, oder wiederholen sie.

Mit do-while erhalten Sie die Eingabe, während die Eingabe ungültig ist. Mit einer regulären while-Schleife erhalten Sie die Eingabe einmal, aber wenn sie ungültig ist, erhalten Sie sie immer wieder, bis sie gültig ist. Es ist nicht schwer zu erkennen, dass Ersteres kürzer, eleganter und einfacher zu warten ist, wenn der Körper der Schleife komplexer wird.


quelle
Ich denke, der Grund, warum ich sie nie dafür verwendet habe, ist, dass ich dem Benutzer normalerweise erzähle, was mit ihrer Eingabe falsch war. Das heißt, die Bedingung wäre in der Mitte einer Schleife und ich würde nur, breakwenn die Eingabe korrekt ist.
mphair
@mphair: Eigentlich neige ich in solchen Situationen manchmal eher dazu, ein "do {} while (0)" zu verwenden. Schleife mit "continue;" wenn die Benutzereingabe nicht akzeptabel ist. Wenn es nur ein Problem und eine Meldung gibt, verwenden Sie "do {} while (1);" und "Pause"; funktioniert gut, aber wenn es mehrere Gründe gibt, einen Wiedereintritt zu fordern, wird das "Weiter" angezeigt. Konstrukt funktioniert besser.
Supercat
2

Ich habe es für einen Leser verwendet, der dieselbe Struktur mehrmals liest.

using(IDataReader reader = connection.ExecuteReader())
{
    do
    {
        while(reader.Read())
        {
            //Read record
        }
    } while(reader.NextResult());
}
Gordon Tucker
quelle
1

Hier ist meine Theorie, warum die meisten Leute (einschließlich mir) while () {} Schleifen bevorzugen, um {} while () zu tun: Eine while () {} Schleife kann leicht angepasst werden, um wie eine do..while () Schleife zu funktionieren, während das Gegenteil der Fall ist ist nicht wahr. Eine while-Schleife ist in gewisser Weise "allgemeiner". Auch Programmierer mögen leicht zu erfassende Muster. Eine while-Schleife sagt gleich zu Beginn, was ihre Invariante ist, und das ist eine schöne Sache.

Folgendes meine ich mit der "allgemeineren" Sache. Nehmen Sie diese do..while-Schleife:

do {
 A;
 if (condition) INV=false; 
 B;
} while(INV);

Die Umwandlung in eine while-Schleife ist unkompliziert:

INV=true;
while(INV) {
 A;
 if (condition) INV=false; 
 B;
}

Nun nehmen wir eine Modell-while-Schleife:

while(INV) {
     A;
     if (condition) INV=false; 
     B;
}

Und verwandeln Sie dies in eine do..while-Schleife, die diese Monstrosität ergibt:

if (INV) {
 do
 {
         A;
         if (condition) INV=false; 
         B;

 } while(INV)
}

Jetzt haben wir zwei Überprüfungen an entgegengesetzten Enden und wenn sich die Invariante ändert, müssen Sie sie an zwei Stellen aktualisieren. In gewisser Weise tun .. währenddessen wie die speziellen Schraubendreher im Werkzeugkasten, die Sie nie verwenden, weil der Standard-Schraubendreher alles tut, was Sie brauchen.

Nordischer Mainframe
quelle
1
Ersetzen eines "do {} while (x);" Schleife in ein "while (x);" Die Schleife erfordert, dass X entweder eine Bedingung ist, die leicht "vorbereitet" werden kann, oder dass sie beim ersten Durchgang "natürlich" wahr ist. Priming-Anweisungen vor einer Schleife bedeuten für mich, dass etwas in der Schleife vorbereitet werden muss, und eine while (x) {}; "- Schleife impliziert, dass die Schleife null Mal ausgeführt werden kann. Wenn ich eine Schleife möchte, die mindestens einmal ausgeführt wird, I. Verwenden Sie eine "do {} while (x);" - Schleife.
Supercat
1

Ich programmiere ungefähr 12 Jahre und erst vor 3 Monaten bin ich auf eine Situation gestoßen, in der es sehr praktisch war, do-while zu verwenden, da immer eine Iteration erforderlich war, bevor eine Bedingung überprüft wurde. Also schätze, deine große Zeit steht vor der Tür :).

Narek
quelle
1
Fast jedes Mal, wenn ich ein Do gesehen habe ... während es wegen eines Unverständnisses für das Problem war und einen schrecklichen Hack beinhaltete. Es gibt Ausnahmen, aber im Allgemeinen sollten Sie überdenken, was Sie tun, wenn Sie etwas zu tun haben ... :-)
Brian Knoblauch
2
Wenn Sie Recht hätten, wäre do-while von allen Programmiersprachen ausgeschlossen, um Programmierer nicht zu verwirren :).
Narek
@Narek: Es ist fast sieben Jahre her, seit du das geschrieben hast. Hattest du mehr Umstände, unter denen do whileeine bessere Lösung ohne Reue war?
Chqrlie
Ja, wahrscheinlich noch einmal: D
Narek
1

Ich kann mir nicht vorstellen, wie lange Sie ohne do...whileSchleife gegangen sind .

Momentan befindet sich einer auf einem anderen Monitor und es gibt mehrere solcher Schleifen in diesem Programm. Sie haben alle die Form:

do
{
    GetProspectiveResult();
}
while (!ProspectIsGood());
Loren Pechtel
quelle
1

Dies ist meine persönliche Meinung, aber diese Frage bittet um eine erfahrungsbasierte Antwort:

  • Ich programmiere seit 36 ​​Jahren in C und verwende do/ whileloop nie in normalem Code.

  • Die einzige überzeugende Verwendung für dieses Konstrukt sind Makros, in denen mehrere Anweisungen über a in eine einzelne Anweisung eingeschlossen werden können do { multiple statements } while (0)

  • Ich habe unzählige Beispiele für do/ whileSchleifen mit falscher Fehlererkennung oder redundanten Funktionsaufrufen gesehen.

  • Meine Erklärung für diese Beobachtung ist, dass Programmierer dazu neigen, Probleme falsch zu modellieren, wenn sie in do/ whileSchleifen denken . Sie verpassen entweder eine wichtige Endbedingung oder sie vermissen das mögliche Versagen der Anfangsbedingung, die sie zum Ende verschieben.

Aus diesen Gründen bin ich zu der Überzeugung gelangt, dass dortdowhile , wo es eine / -Schleife gibt, ein Fehler vorliegt , und fordere regelmäßig Programmieranfänger auf, mir ein do/ zu zeigenwhile gelangt Schleife bei der ich keinen Fehler in der Nähe erkennen kann.

Diese Art von Schleife kann leicht vermieden werden: Verwenden Sie a for (;;) { ... }und fügen Sie die erforderlichen Terminierungstests hinzu, wo sie angemessen sind. Es ist durchaus üblich, dass es mehr als einen solchen Test geben muss.

Hier ist ein klassisches Beispiel:

/* skip the line */
do {
    c = getc(fp);
} while (c != '\n');

Dies schlägt fehl, wenn die Datei nicht mit einem Zeilenumbruch endet. Ein triviales Beispiel für eine solche Datei ist die leere Datei.

Eine bessere Version ist diese:

int c;  // another classic bug is to define c as char.
while ((c = getc(fp)) != EOF && c != '\n')
    continue;

Alternativ verbirgt diese Version auch die cVariable:

for (;;) {
    int c = getc(fp);
    if (c == EOF || c == '\n')
        break;
}

Versuchen Sie zu suchen while (c != '\n'); in einer Suchmaschine suchen, und Sie werden Fehler wie diesen finden (abgerufen am 24. Juni 2017):

In ftp://ftp.dante.de/tex-archive/biblio/tib/src/streams.c hat die Funktion getword(stream,p,ignore)ein do/ whileund sicher genug mindestens 2 Fehler:

  • cist definiert als charund
  • Es gibt eine mögliche Endlosschleife while (c!='\n') c=getc(stream);

Fazit: Vermeiden Sie do/ whileSchleifen und suchen Sie nach Fehlern, wenn Sie einen sehen.

chqrlie
quelle
1
Ich bevorzuge es, while() {}Schleifen zu vermeiden , weil sie in Bezug auf anfängliche Überprüfungen sicherer sind, so dass Sie dazu neigen, "Gehirn aus" zu machen, faul über einen besseren Algorithmus nachzudenken, eine Menge unnötiger Fehlerprüfungen durchzuführen usw. "Mangel an do{}while()Verwendung" ist ein Zeichen von schlechtem (hirnlosem) oder neuem Programmierer, produzieren weniger Qualität und langsameren Code. Also frage ich mich zuerst: "Ist es ein strikter while () {} Fall?" Wenn nicht - ich werde nur versuchen, do while-Schleife zu schreiben, obwohl dies mehr Nachdenken und Genauigkeit der Eingabedaten erfordern könnte
xakepp35
0

whileSchleifen prüfen den Zustand vor der Schleife, do...whileSchleifen prüfen den Zustand nach der Schleife. Dies ist nützlich, wenn Sie die Bedingung auf Nebenwirkungen der laufenden Schleife stützen möchten oder, wie andere Poster sagten, wenn die Schleife mindestens einmal ausgeführt werden soll.

Ich verstehe, woher du kommst, aber das do-whileist etwas, das die meisten selten benutzen, und ich habe mich selbst nie benutzt. Du machst es nicht falsch.

Du machst es nicht falsch. Das ist so, als würde man sagen, jemand macht es falsch, weil er das bytePrimitiv nie benutzt hat . Es ist einfach nicht so häufig verwendet.

Rafe Kettler
quelle
0

Ich habe a verwendet, do whilewenn ich einen Sentinel-Wert am Anfang einer Datei lese, aber ansonsten finde ich es nicht ungewöhnlich, dass diese Struktur nicht allzu häufig verwendet wird - do-whiles sind wirklich situativ.

-- file --
5
Joe
Bob
Jake
Sarah
Sue

-- code --
int MAX;
int count = 0;
do {
MAX = a.readLine();
k[count] = a.readLine();
count++;
} while(count <= MAX)
Danyim
quelle
0

Das häufigste Szenario, in dem ich eine do/ while-Schleife verwende, ist ein kleines Konsolenprogramm, das auf der Grundlage einiger Eingaben ausgeführt wird und so oft wiederholt wird, wie der Benutzer möchte. Offensichtlich macht es keinen Sinn, dass ein Konsolenprogramm keine Zeit ausgeführt wird. aber über das erste Mal hinaus liegt es am Benutzer - daher do/ whilestatt nur while.

Auf diese Weise kann der Benutzer bei Bedarf verschiedene Eingaben ausprobieren.

do
{
   int input = GetInt("Enter any integer");
   // Do something with input.
}
while (GetBool("Go again?"));

Ich vermute, dass Softwareentwickler heutzutage do/ whileimmer weniger verwenden, da praktisch jedes Programm unter der Sonne eine Art GUI hat. Bei Konsolen-Apps ist dies sinnvoller, da die Ausgabe ständig aktualisiert werden muss, um Anweisungen bereitzustellen oder den Benutzer mit neuen Informationen aufzufordern. Im Gegensatz dazu kann bei einer grafischen Benutzeroberfläche der Text, der dem Benutzer diese Informationen zur Verfügung stellt, einfach auf einem Formular sitzen und muss niemals programmgesteuert wiederholt werden.

Dan Tao
quelle
0

Ich verwende ständig Do-While-Schleifen, wenn ich Dateien einlese. Ich arbeite mit vielen Textdateien, die Kommentare in der Kopfzeile enthalten:

# some comments
# some more comments
column1 column2
  1.234   5.678
  9.012   3.456
    ...     ...

Ich werde eine do-while-Schleife verwenden, um bis zur Zeile "column1 column2" zu lesen, damit ich nach der interessierenden Spalte suchen kann. Hier ist der Pseudocode:

do {
    line = read_line();
} while ( line[0] == '#');
/* parse line */

Dann mache ich eine while-Schleife, um den Rest der Datei durchzulesen.

fliegt
quelle
Die Unterstützung von Kommentaren an einer beliebigen Stelle in der Datei wäre trivial (und vereinfacht den Code insgesamt), wenn Sie sie if (line[0]=='#') continue;direkt nach dem read_lineAufruf in Ihre Haupt-while-Schleife
einfügen
Ich kann nicht sehen, wie ich Ihren Vorschlag umsetzen würde, einen Header zu finden. (Mist, ich kann nicht herausfinden, wie man Code in den Kommentaren formatiert, bitte helfen!) header = false; while (! header) {line = read_line (); if (Zeile [0] == '#') weiter; Header = wahr; } das ist dir klarer / einfacher?
fliegt
0

Als Geezer-Programmierer verwendeten viele meiner Schulprogrammierungsprojekte textmenügesteuerte Interaktionen. Praktisch alle verwendeten so etwas wie die folgende Logik für das Hauptverfahren:

do
    display options
    get choice
    perform action appropriate to choice
while choice is something other than exit

Seit der Schulzeit habe ich festgestellt, dass ich die while-Schleife häufiger benutze.

GreenMatt
quelle
0

Eine der Anwendungen, die ich gesehen habe, ist in Oracle, wenn wir uns Ergebnismengen ansehen.

Sobald Sie eine Ergebnismenge haben, rufen Sie diese zuerst ab (do) und von diesem Punkt an. Überprüfen Sie, ob der Abruf ein Element zurückgibt oder nicht (während das Element gefunden wurde.). Dasselbe gilt möglicherweise für alle anderen " Abrufähnliche "Implementierungen.

Rajesh Chamarthi
quelle
0

Ich habe es in einer Funktion verwendet, die die nächste Zeichenposition in einer utf-8-Zeichenfolge zurückgegeben hat:

char *next_utf8_character(const char *txt)
{
    if (!txt || *txt == '\0')
        return txt;

    do {
        txt++;
    } while (((signed char) *txt) < 0 && (((unsigned char) *txt) & 0xc0) == 0xc0)

    return (char *)txt;
}

Beachten Sie, dass diese Funktion aus dem Kopf geschrieben und nicht getestet wurde. Der Punkt ist, dass Sie den ersten Schritt sowieso tun müssen und Sie müssen es tun, bevor Sie den Zustand bewerten können.

Quinmars
quelle
Das ist eine böse Art zu schreiben *(unsigned char *)txt-0x80U<0x40.
R .. GitHub STOP HELPING ICE
0

Jede Art von Konsoleneingabe funktioniert gut mit do-while, da Sie das erste Mal dazu auffordern und erneut auffordern, wenn die Eingabevalidierung fehlschlägt.

Lotus Notes
quelle
0

Es ist eine weit verbreitete Struktur in einem Server / Verbraucher:

DOWHILE (no shutdown requested)
   determine timeout
   wait for work(timeout)
   IF (there is work)
      REPEAT
          process
      UNTIL(wait for work(0 timeout) indicates no work)
      do what is supposed to be done at end of busy period.
   ENDIF
ENDDO

das REPEAT UNTIL(cond)Wesen ado {...} while(!cond)

Manchmal kann das Warten auf Arbeit (0) in Bezug auf die CPU günstiger sein (selbst das Eliminieren der Timeout-Berechnung kann bei sehr hohen Ankunftsraten eine Verbesserung darstellen). Darüber hinaus gibt es viele Ergebnisse der Warteschlangentheorie, die die in einer geschäftigen Zeit zugestellte Nummer zu einer wichtigen Statistik machen. (Siehe zum Beispiel Kleinrock - Vol 1.)

Ähnlich:

DOWHILE (no shutdown requested)
   determine timeout
   wait for work(timeout)
   IF (there is work)
      set throttle
      REPEAT
          process
      UNTIL(--throttle<0 **OR** wait for work(0 timeout) indicates no work)
   ENDIF
   check for and do other (perhaps polled) work.
ENDDO

wo check for and do other workes exorbitant teuer sein kann, in die Hauptschleife zu setzen, oder vielleicht ein Kernel, der eine effiziente waitany(waitcontrol*,n)Operation nicht unterstützt, oder vielleicht eine Situation, in der eine priorisierte Warteschlange die andere Arbeit verhungern lässt und Gas als Hungerkontrolle verwendet wird.

Diese Art des Balancierens kann wie ein Hack erscheinen, kann aber notwendig sein. Die blinde Verwendung von Thread-Pools würde die Leistungsvorteile der Verwendung eines Caretaker-Threads mit einer privaten Warteschlange für eine komplizierte Datenstruktur mit hoher Aktualisierungsrate vollständig zunichte machen, da die Verwendung eines Thread-Pools anstelle eines Caretaker-Threads eine thread-sichere Implementierung erfordern würde.

Ich möchte wirklich nicht in eine Debatte über den Pseudocode (zum Beispiel, ob das angeforderte Herunterfahren in UNTIL getestet werden soll) oder über Caretaker-Threads im Vergleich zu Thread-Pools geraten - dies soll nur einen Eindruck von einem bestimmten Anwendungsfall vermitteln die Kontrollflussstruktur.

pgast
quelle
-1

Ich bin darauf gestoßen, als ich nach der richtigen Schleife für eine Situation gesucht habe, die ich habe. Ich glaube, dies wird eine häufige Situation vollständig befriedigen, in der eine do .. while-Schleife eine bessere Implementierung ist als eine while-Schleife (C # -Sprache, da Sie angegeben haben, dass dies Ihre primäre Aufgabe für die Arbeit ist).

Ich generiere eine Liste von Zeichenfolgen basierend auf den Ergebnissen einer SQL-Abfrage. Das von meiner Abfrage zurückgegebene Objekt ist ein SQLDataReader. Dieses Objekt verfügt über eine Funktion namens Read (), die das Objekt zur nächsten Datenzeile weiterleitet und true zurückgibt, wenn eine andere Zeile vorhanden war. Es wird false zurückgegeben, wenn keine weitere Zeile vorhanden ist.

Mit diesen Informationen möchte ich jede Zeile an eine Liste zurückgeben und dann anhalten, wenn keine Daten mehr zurückgegeben werden müssen. Eine Do ... While-Schleife funktioniert in dieser Situation am besten, da sie sicherstellt, dass ein Element zur Liste hinzugefügt wird, bevor überprüft wird, ob eine weitere Zeile vorhanden ist. Der Grund, warum dies getan werden muss, bevor das while (Bedingung) überprüft wird, ist, dass es bei der Überprüfung auch voranschreitet. Die Verwendung einer while-Schleife in dieser Situation würde dazu führen, dass die erste Zeile aufgrund der Art dieser bestimmten Funktion umgangen wird.

Zusamenfassend:

Das wird in meiner Situation nicht funktionieren.

    //This will skip the first row because Read() returns true after advancing.
    while (_read.NextResult())
           {
               list.Add(_read.GetValue(0).ToString());
           }

   return list;

Dieser Wille.

    //This will make sure the currently read row is added before advancing.
    do
        {
            list.Add(_read.GetValue(0).ToString());
        } 
        while (_read.NextResult());

    return list;
Mikey Awbrey
quelle
Die Verwendung von SQLDataReader wäre eine verschachtelte Schleife. Der innere Aufruf ruft auf Read()und sollte eine while-Schleife sein, da der erste Read()Aufruf auf die erste Zeile zugreift. Der äußere ruft auf NextResult()und sollte eine Pause sein, da der erste Ergebnisdatensatz vor dem ersten NextResult()Aufruf aktiv ist. Die Codefragmente sind wenig sinnvoll, insbesondere weil die Kommentare nicht mit dem Code übereinstimmen und falsch sind.
Ben Voigt
-1
Console.WriteLine("hoeveel keer moet je de zin schrijven?");
        int aantal = Convert.ToInt32(Console.ReadLine());
        int counter = 0;

        while ( counter <= aantal)
        {
            Console.WriteLine("Ik mag geen stiften gooien");
            counter = counter + 1;
Andreas VanDenAbele
quelle
Sie sollten Ihre eigene Frage beantworten, als würden Sie die einer anderen Person beantworten. Enthält eine Erklärung sowie Code
Simon