"fare ... while" vs. "while"


86

Possibili duplicati:
While vs. Do While
Quando dovrei usare do-while invece di while loops?

Sto programmando da un po 'di tempo (2 anni di lavoro + 4,5 anni di laurea + 1 anno di pre-college) e non ho mai usato un ciclo di do-while a meno di essere costretto a farlo nel corso di Introduzione alla programmazione. Ho la sensazione crescente di sbagliare la programmazione se non mi imbatto mai in qualcosa di così fondamentale.

Potrebbe essere che non mi sia imbattuto nelle circostanze corrette?

Quali sono alcuni esempi in cui sarebbe necessario utilizzare un do-while invece di un po '?

(La mia scuola era quasi tutta in C / C ++ e il mio lavoro è in C #, quindi se c'è un altro linguaggio in cui ha assolutamente senso perché i do-mentre funzionano in modo diverso, allora queste domande non si applicano realmente.)

Per chiarire ... Conosco la differenza tra a whilee a do-while. Mentre controlla la condizione di uscita e quindi esegue le attività. do-whileesegue le attività e quindi controlla le condizioni di uscita.


43
Per quel che vale, dopo un decennio di programmazione come la mia carriera, ho usato un ciclo do-while come una o due volte.
Matt Greer

@ Matt - Questo mi fa sentire meglio. Almeno significa che non sono solo.
mphair

2
Secondo Matt e aggiungerò un altro decennio circa a questo. Neanche io uso raramente il ciclo do-while.
quentin-starin

MOLTI duplicati. Qui è uno che i link a un gruppo di loro: stackoverflow.com/questions/3094972/...
gnovice

3
La "domanda principale" corretta sembra essere stackoverflow.com/questions/224059/…
Donal Fellows

Risposte:


116

Se vuoi sempre che il ciclo venga eseguito almeno una volta. Non è comune, ma di tanto in tanto lo uso. Un caso in cui potresti volerlo utilizzare è il tentativo di accedere a una risorsa che potrebbe richiedere un nuovo tentativo, ad es

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

} while (user says retry);

3
Quindi immagino che questo sarebbe seguito dal "Non mi sono imbattuto in quella circostanza".
mphair

7
onestamente, molto raramente mi imbatto in una circostanza in cui ho solo bisogno di usare un do .. nel frattempo ..
Stan R.

1
Forse un po 'corto, ma Bob ha ragione. Do / while deve essere utilizzato quando si desidera eseguire il blocco di codice almeno una volta . Dovresti usare un ciclo while quando non sai se vuoi eseguirlo una sola volta. Detto questo, posso dirti che con circa 15 anni di esperienza nello sviluppo, ho usato molte volte più cicli while che cicli do / while.
ConsultUtah

1
Li ho usati anche di tanto in tanto, anche se raramente. Dopo un po 'di solito vengono anche rifattorizzati in un normale ciclo while - solo perché le aree intorno sono cambiate e hanno funzionato ... anche se più goffi di prima.
Joshua

2
È abbastanza comune. Ma non vedo perché nel tuo esempio non useresti un ciclo while. E non vedo perché questa risposta sia stata votata.

65

do-while è meglio se il compilatore non è competente per l'ottimizzazione. do-while ha un solo salto condizionale, al contrario di for e while che hanno un salto condizionale e un salto incondizionato. Per le CPU che sono pipeline e non eseguono la previsione dei rami, questo può fare una grande differenza nelle prestazioni di un loop stretto.

Inoltre, poiché la maggior parte dei compilatori è abbastanza intelligente da eseguire questa ottimizzazione, tutti i loop trovati nel codice decompilato saranno solitamente do-while (se il decompilatore si preoccupa anche di ricostruire i loop da gotos locali all'indietro).


5
+1 per l'aggiunta di informazioni tecniche rilevanti alla scelta tra le forme di loop.
quentin-starin

2
Non solo prematuro ma inutile. Se vuoi davvero un ciclo while, ovvero la possibilità di iterare 0 volte, devi racchiudere il ciclo in un if che riporta il salto.
JeremyP

3
@ Jeremy: Il salto di cui parli è fuori dal loop, non viene eseguito N volte.
Ben Voigt

1
Considera anche l'uso classico del do-while nel dispositivo di Duff (in cui un ciclo while viene trasformato in un do-while srotolato con un passo più grande più una tabella di salto per gestire il resto) che riduce drasticamente il sovraccarico del loop - in applicazioni come memset, memcmp, memcpy può aumentare il throughput di un fattore 10.
Ben Voigt

@BenVoigt So che il tuo commento è vecchio di un decennio ormai, ma il dispositivo di Duff non dovrebbe essere menzionato senza l'avvertenza molto importante: è diventato meno efficiente sui compilatori moderni, perché i compilatori moderni di solito possono srotolare in modo intelligente i loop diretti stessi come ottimali per la macchina di destinazione , mentre generalmente non hanno una logica per capire l'intento di un dispositivo di Duff e quindi non possono ottimizzarlo altrettanto bene. Che è una grande lezione sull'ottimizzazione del codice per qualche macchina di destinazione invece che per un astratto "ottimizzatore sufficientemente avanzato".
mtraceur

12

L'ho usato in una funzione TryDeleteDirectory. Era qualcosa del genere

do
{
    try
    {
        DisableReadOnly(directory);
        directory.Delete(true);
    }
    catch (Exception)
    {
        retryDeleteDirectoryCount++;
    }
} while (Directory.Exists(fullPath) && retryDeleteDirectoryCount < 4);

Bello. Questo è il primo esempio che ha davvero senso essere avvolto in un do ... mentre.
Joshua

4
Perché / come è migliore di uno standard while?
Josh Stodola

Il ciclo verrà sempre eseguito una volta, quindi potresti voler fare qualcosa, quindi riprovare se fallisce. Se usi un normale ciclo while, controllerai se fallisce prima ancora di averlo fatto alla prima iterazione.
NibblyPig

1
@SLC Ma non vorresti controllare se la directory esiste comunque?
Josh Stodola

1
@ Josh In questo esempio, sì. Ma se stavi tentando di aprire una connessione a un server occupato, dovresti prima connetterti e vedere se la risposta era "occupata". Se lo fosse, proveresti di nuovo un paio di volte prima di arrenderti.
NibblyPig

11

Do while è utile quando vuoi eseguire qualcosa almeno una volta. Per quanto riguarda un buon esempio di utilizzo di fare mentre contro mentre, diciamo che vuoi fare quanto segue: Una calcolatrice.

Puoi avvicinarti a questo utilizzando un ciclo e controllando dopo ogni calcolo se la persona vuole uscire dal programma. Ora puoi probabilmente presumere che una volta aperto il programma la persona voglia farlo almeno una volta, quindi puoi fare quanto segue:

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

2
In realtà, ora che me lo dici, qualsiasi tipo di interfaccia basata su menu basata su console andrebbe bene anche in un ciclo do ... while. Ad esempio, la richiesta di opzioni (una chiave alla volta) e l'uscita solo quando scelgono continua o esci.
Joshua

7
continueè una parola chiave; D
Thomas Eding

Atrinithis: Lol ... sono scivolato. Grazie per avermi corretto lì.

== truenon è necessario. cont == truese cont è vero restituisce vero. È totalmente inutile.
Mr.DeleteMyMessages

11

Questa è una sorta di risposta indiretta, ma questa domanda mi ha fatto riflettere sulla logica che c'è dietro e ho pensato che valesse la pena condividerla.

Come tutti gli altri hanno detto, usi un do ... whileciclo quando vuoi eseguire il corpo almeno una volta. Ma in quali circostanze vorresti farlo?

Ebbene, la classe di situazioni più ovvia a cui riesco a pensare sarebbe quando il valore iniziale ("non adescato") della condizione di controllo è lo stesso di quando vuoi uscire . Ciò significa che è necessario eseguire il corpo del ciclo una volta per innescare la condizione su un valore non esistente, quindi eseguire la ripetizione effettiva in base a quella condizione. Dato che i programmatori sono così pigri, qualcuno ha deciso di avvolgerlo in una struttura di controllo.

Quindi, ad esempio, la lettura di caratteri da una porta seriale con un timeout potrebbe assumere la forma (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)

Si noti la duplicazione del codice: char_read = port.read(1) . Se Python avesse un do ... whileciclo, avrei potuto usare:

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

Il vantaggio aggiuntivo per i linguaggi che creano un nuovo ambito per i cicli: char_readnon inquina lo spazio dei nomi della funzione. Ma nota anche che c'è un modo migliore per farlo, e cioè usando il Nonevalore di Python :

response_buffer = []
char_read = None

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

response = ''.join(response_buffer)

Quindi ecco il punto cruciale: nelle lingue con tipi nullable, la situazione si initial_value == exit_valuepresenta molto meno frequentemente, e questo potrebbe essere il motivo per cui non la incontri. Non sto dicendo che non succede mai, perché ci sono ancora momenti in cui una funzione tornerà Nonea significare una condizione valida. Ma a mio parere frettoloso e sommariamente considerato, ciò accadrebbe molto di più se le lingue che hai usato non permettessero un valore che significa: questa variabile non è stata ancora inizializzata.

Questo non è un ragionamento perfetto: in realtà, ora che i valori nulli sono comuni, formano semplicemente un elemento in più dell'insieme di valori validi che una variabile può assumere. Ma in pratica, i programmatori hanno un modo per distinguere tra una variabile che si trova in uno stato sensibile, che può includere lo stato di uscita dal loop, ed è in uno stato non inizializzato.


Questa è una delle risposte più interessanti che ho letto qui. Bel contributo.
Bob Moore

Le due versioni non sono equivalenti. Se la prima lettura ritorna None, la whileversione correttamente non aggiunge nulla al buffer di risposta, ma la tua doversione aggiunge sempre qualcosa.
David Stone

@DavidStone read()non dovrebbe mai tornare None. Se stai usando una libreria dove lo fa, la premessa non si applica (cioè che il valore sentinel non è nel set di possibili valori di controllo).
detly

Con la tua ultima modifica, vedo quello che avrei dovuto dire ''invece di None. Una sorta di valore che restituisce falso. Calcola la mia non familiarità con la readfunzione di Python .
David Stone,

@DavidStone non capisco. Se read()restituisce '', nulla verrà aggiunto alla stringa, è vuoto.
detly

7

Li ho usati un bel po 'quando ero a scuola, ma non così tanto da allora.

In teoria sono utili quando si desidera che il corpo del ciclo venga eseguito una volta prima del controllo della condizione di uscita. Il problema è che per i pochi casi in cui non voglio il controllo prima, in genere voglio il controllo di uscita nel mezzo del corpo del ciclo piuttosto che alla fine. In tal caso, preferisco usare il noto for (;;)con un if (condition) exit;da qualche parte nel corpo.

In effetti, se sono un po 'tremante nella condizione di uscita dal ciclo, a volte trovo utile iniziare a scrivere il ciclo come un for (;;) {}con un'istruzione di uscita dove necessario, e poi quando ho finito posso vedere se può essere " ripulito "spostando le inizializzazioni, le condizioni di uscita e / o il codice di incremento all'interno delle forparentesi.


5
Per curiosità ... perché "for (;;)" invece di "while (true)"?
mphair

4
Probabilmente solo gusto. Sono cresciuto fino a equiparare for(;;)con "loop infinito", mentre while(true)devo fermarmi a pensare. Forse è perché stavo codificando C prima che ci fosse un file true. In passato avevamo solo TRUEuna macro, e se qualcosa fosse difettoso dovrei convincermi che la macro non è stata ridefinita in 0qualche modo (è successo!). Con for(;;)nessuna possibilità di quello. Se insisti sul modulo while, preferirei quasi vederlo while (1). Ovviamente qualcuno potrebbe chiedersi se non è la lettera l ...
TED

1
@mphair: Sì, se prendi nota del testo che l'editor inserisce quando scrivi in corsivo o codeifytesto, puoi semplicemente digitarlo direttamente nel campo dei commenti. Ho visto alcune persone inserire collegamenti ipertestuali, ma per me è un po 'troppo hardcore.
TED

2
@TED: La mia notazione preferita per loop-until-explicit-exit è "do {} while (1);". Per inciso, nei sistemi embedded funzionano, il mio paradigma di loop normale è "i = 10; do {} while (- i);". Per loop stretti, è molto più veloce di un tipico for (); Immagino che sia più leggibile usarlo in modo coerente come modello di ciclo quando non è necessario un conteggio positivo piuttosto che utilizzare arbitrariamente for () in casi non critici per le prestazioni.
supercat

2
Aggiungendo al while (1) e for (;;) disusion, ho visto il codice che usa for (EVER) con EVER definito come ;;
asr

6

Una situazione in cui è sempre necessario eseguire un pezzo di codice una volta e, a seconda del suo risultato, forse più volte. Lo stesso può essere prodotto anche con un whileciclo regolare .

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

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

6

do whileè se vuoi eseguire il blocco di codice almeno una volta. whiled'altra parte non sempre verrà eseguito in base ai criteri specificati.


5

E 'così semplice:

precondizione vs postcondizione

  • while (cond) {...} - precondizione, esegue il codice solo dopo il controllo.
  • do {...} while (cond) - postcondition, il codice viene eseguito almeno una volta.

Ora che conosci il segreto .. usali con saggezza :)


1
Come ho detto, so come funzionano, solo che non ero sicuro in quale caso uno avesse senso rispetto all'altro.
mphair

È logico che usi il do {} mentre sai per certo che devi passare almeno una volta attraverso il ciclo. Come per esempio se hai una funzione che trova un elemento in un array e hai precedentemente messo un if array.IsEmpty () poi ritorna; . Se ha superato tale controllo, puoi tranquillamente utilizzare do {} while. do-while è anche facile da pensare, come un ciclo ripetitivo: "Mangio finché non sono soddisfatto" si traduce in do {mangia ();} mentre (! soddisfatto ();). Testare la condizione prima del ciclo è considerato più sicuro .. ed è per questo che viene utilizzato più spesso.
Ioan Paul Pirau,

4

Vedo che questa domanda ha ricevuto una risposta adeguata, ma vorrei aggiungere questo scenario di caso d'uso molto specifico. Potresti iniziare a usare do ... mentre più frequentemente.

do
{
   ...
} while (0)

è spesso usato per #defines su più righe. Per esempio:

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

Funziona bene per:

r = 4;
h = 3;
compute_values;

-ma- c'è un trucco per:

if (shape == circle)  compute_values;

poiché questo si espande a:

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

Se lo avvolgi in un ciclo do ... while (0), si espande correttamente in un singolo blocco:

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

2

Le risposte finora riassumono l'uso generale del do-while. Ma l'OP ha chiesto un esempio, quindi eccone uno: ottenere l'input dell'utente. Ma l'input dell'utente potrebbe non essere valido, quindi chiedi un input, convalidalo, procedi se è valido, altrimenti ripeti.

Con do-while, ottieni l'input mentre l'input non è valido. Con un ciclo while regolare, si ottiene l'input una volta, ma se non è valido, lo si ottiene ancora e ancora finché non è valido. Non è difficile vedere che il primo è più corto, più elegante e più semplice da mantenere se il corpo del loop diventa più complesso.


Penso che il motivo per cui non li ho mai usati per questo è che di solito finisco per dire all'utente cosa c'era di sbagliato nel loro input. Il che significa che il condizionale sarebbe al centro di un ciclo e lo farei solo breakse l'input è corretto.
mphair

@mphair: In realtà, per quel tipo di situazione a volte potrei essere più propenso a usare un "do {} while (0);" loop, utilizzando "continue;" se l'input dell'utente è inaccettabile. Se c'è un solo problema e messaggio, utilizzando "do {} while (1);" e "break;" funziona bene, ma se ci sono più motivi per chiedere il rientro, il "continua"; la costruzione funziona in modo più piacevole.
supercat

2

L'ho usato per un lettore che legge più volte la stessa struttura.

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

1

Ho usato un do whilequando leggo un valore sentinella all'inizio di un file, ma a parte questo, non penso sia anormale che questa struttura non sia usata troppo comunemente - le do-whiles sono davvero situazionali.

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

1

Ecco la mia teoria sul perché la maggior parte delle persone (me compreso) preferisce che i cicli while () {} eseguano {} while (): un ciclo while () {} può essere facilmente adattato per funzionare come un ciclo do..while () mentre il contrario non è vero. Un ciclo while è in un certo modo "più generale". Anche ai programmatori piacciono i modelli facili da capire. Un ciclo while dice all'inizio qual è la sua invariante e questa è una cosa carina.

Ecco cosa intendo per la cosa "più generale". Prendi questo ciclo do.. while:

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

Trasformare questo in un ciclo while è semplice:

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

Ora, prendiamo un modello while loop:

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

E trasformare questo in un ciclo do.. while, produce questa mostruosità:

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

 } while(INV)
}

Ora abbiamo due controlli alle estremità opposte e se l'invariante cambia devi aggiornarlo su due punti. In un certo senso fai .. mentre è come i cacciaviti specializzati nella cassetta degli attrezzi che non usi mai, perché il cacciavite standard fa tutto ciò di cui hai bisogno.


1
Sostituendo un "do {} while (x);" loop in un "while (x);" loop richiede che X sia una condizione che può essere facilmente "innescata", o che sia "naturalmente" vera al primo passaggio. Per me, le istruzioni di priming prima di un ciclo implicano che qualcosa all'interno del ciclo necessita di priming, e un ciclo while (x) {}; "implica che il ciclo può essere eseguito zero volte. Se voglio un ciclo che verrà eseguito almeno una volta, io usa un ciclo "do {} while (x);".
supercat

1

Sto programmando da circa 12 anni e solo 3 mesi fa ho riscontrato una situazione in cui era davvero conveniente usare do-while poiché era sempre necessaria un'iterazione prima di controllare una condizione. Quindi indovina il tuo grande momento è avanti :).


1
Quasi ogni volta che ho visto qualcosa ... mentre era a causa di una mancanza di comprensione del problema e comportava qualche terribile hack. Ci sono eccezioni, ma in generale , se hai qualcosa da fare ... intanto, dovresti ripensare a quello che stai facendo ... :-)
Brian Knoblauch

2
Se avessi ragione, do-while verrebbe escluso da tutti i linguaggi di programmazione per non confondere i programmatori :).
Narek

@ Narek: Sono passati quasi sette anni da quando hai scritto questo, hai avuto più circostanze in cui do whilec'era una soluzione migliore senza rimpianti?
chqrlie

Sì, probabilmente ancora una volta: D
Narek

1

Non riesco a immaginare come sei andato così a lungo senza usare un do...whileloop.

Ce n'è uno su un altro monitor in questo momento e ci sono più loop di questo tipo in quel programma. Sono tutti della forma:

do
{
    GetProspectiveResult();
}
while (!ProspectIsGood());

1

È una struttura abbastanza comune in un server / consumatore:

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

l' REPEAT UNTIL(cond)essere ado {...} while(!cond)

A volte l'attesa per il lavoro (0) può essere più economica per la CPU (anche l'eliminazione del calcolo del timeout potrebbe essere un miglioramento con tassi di arrivo molto elevati). Inoltre, ce ne sono molti risultati della teoria delle code che rendono il numero servito in un periodo frenetico una statistica importante. (Vedi ad esempio Kleinrock - Vol 1.)

Allo stesso modo:

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

dove check for and do other workpuò essere esorbitante da inserire nel ciclo principale o forse un kernel che non supporta un efficientewaitany(waitcontrol*,n) tipo o forse una situazione in cui una coda prioritaria potrebbe far morire di fame l'altro lavoro e l'acceleratore viene utilizzato come controllo della fame.

Questo tipo di bilanciamento può sembrare un trucco, ma può essere necessario. L'uso cieco dei pool di thread vanificherebbe completamente i vantaggi in termini di prestazioni dell'uso di un thread caretaker con una coda privata per una struttura dati complicata ad alta velocità di aggiornamento poiché l'uso di un pool di thread piuttosto che un thread di controllo richiederebbe un'implementazione thread-safe.

Non voglio davvero entrare in un dibattito sullo pseudo codice (ad esempio, se l'arresto richiesto deve essere testato in UNTIL) o sui thread custodi rispetto ai pool di thread - questo ha solo lo scopo di dare un sapore di un caso d'uso particolare di la struttura del flusso di controllo.


1

Questa è la mia opinione personale, ma questa domanda richiede una risposta radicata nell'esperienza:

  • Sto programmando in C da 38 anni e non uso mai do/ whileloops nel codice normale.

  • L'unico utilizzo convincente di questo costrutto è nelle macro in cui può racchiudere più istruzioni in una singola istruzione tramite un file do { multiple statements } while (0)

  • Ho visto innumerevoli esempi di do/ whileloop con rilevamento di errori fasulli o chiamate di funzioni ridondanti.

  • La mia spiegazione per questa osservazione è che i programmatori tendono a modellare i problemi in modo errato quando pensano in termini di do/ whileloop. O perdono un'importante condizione finale o perdono il possibile fallimento della condizione iniziale che spostano alla fine.

Per queste ragioni, sono arrivato a credere che dove c'è un do/ whileloop, c'è un bug , e sfido regolarmente i programmatori principianti a mostrarmi un do/ whileloop dove non riesco a individuare un bug nelle vicinanze.

Questo tipo di loop può essere facilmente evitato: utilizzare for (;;) { ... }ae aggiungere i necessari test di terminazione dove sono appropriati. È abbastanza comune che ci sia bisogno di più di uno di questi test.

Ecco un classico esempio:

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

Questo fallirà se il file non termina con una nuova riga. Un banale esempio di un tale file è il file vuoto.

Una versione migliore è questa:

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

In alternativa, questa versione nasconde anche la cvariabile:

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

Prova a cercare while (c != '\n');in qualsiasi motore di ricerca e troverai bug come questo (recuperato il 24 giugno 2017):

In ftp://ftp.dante.de/tex-archive/biblio/tib/src/streams.c , function getword(stream,p,ignore), ha una do/ whilee sicuramente almeno 2 bug:

  • cè definito come chare
  • c'è un potenziale loop infinito while (c!='\n') c=getc(stream);

Conclusione: evita do/ whileloop e cerca bug quando ne vedi uno.


1
Preferisco evitare i while() {}loop, perché sono più sicuri in termini di controlli iniziali, quindi tende a farti "fuori di testa", pigro a pensare a un algoritmo migliore, a mettere un sacco di controllo degli errori non stressante, così via .. "Mancanza di do{}while()utilizzo" è un segno di cattivo (senza cervello) o un programmatore principiante, produce codice di qualità inferiore e più lento. Quindi prima mi chiedo "È un caso strettamente while () {}?" In caso contrario, proverò a scrivere il ciclo mentre, nonostante possa richiedere più riflessione e precisione dei dati di input
xakepp35

Capisco la tua risposta ma potrei pensare a qualche possibile scenario in cui questo sarebbe effettivamente migliore. Si prega di guardare while: prnt.sc/v2zy04 o do while: prnt.sc/v2zyka . Non sono stato programmatore quanto te, ma non sono riuscito a trovare un bug lì dentro. È abbastanza chiaro che il do while è più pulito perché non richiede il controllo extra del while per entrare nel ciclo. Se stiamo parlando di ottimizzare il codice, direi che è abbastanza pulito.
Roy Berris

@RoyBerris: il tuo esempio è interessante. In un programma C, le funzioni utilizzate all'interno del ciclo richiederebbero test extra per assicurarsi che non fallissero. Ciò aggiungerebbe ulteriore disordine e / breako returndichiarazioni. Trasformare il ciclo do/ whilein un for(;;)ciclo sarà più coerente IMHO. Il mio ostracismo verso questa affermazione è simile al mio rifiuto di utilizzare strncpy() anche nei rari casi in cui sarebbe lo strumento giusto: non lasciare che i lettori occasionali immaginino che queste costruzioni siano innocue, perché nella maggior parte dei casi sono fonti di bug difficili da trovare .
chqrlie

@chqrlie visto che la risposta originale riguardava qualsiasi linguaggio C immagino che entrambe le risposte andrebbero bene. C # è molto "risparmiatore" rispetto ai loro predecessori, quindi tecnicamente sarebbe più veloce con il do while.
Roy Berris

0

while i loop controllano la condizione prima del loop, do...while loop controllano la condizione dopo il loop. Ciò è utile se si desidera basare la condizione sugli effetti collaterali del ciclo in esecuzione o, come hanno detto altri poster, se si desidera che il ciclo venga eseguito almeno una volta.

Capisco da dove vieni, ma il file do-while è qualcosa che uso raramente e non ho mai usato me stesso. Non stai sbagliando.

Non stai sbagliando. È come dire che qualcuno lo sta facendo male perché non ha mai usato il byteprimitivo. Semplicemente non è così comunemente usato.


0

Lo scenario più comune in cui mi imbatto in cui utilizzo un ciclo do/ whileè in un piccolo programma di console che viene eseguito in base a un input e si ripeterà tutte le volte che l'utente desidera. Ovviamente non ha senso che un programma per console non venga eseguito in nessun momento; ma oltre la prima volta dipende dall'utente - quindi do/ whileinvece di solo while.

Ciò consente all'utente di provare una serie di input diversi, se lo desidera.

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

Sospetto che gli sviluppatori di software usino do/ whilesempre meno in questi giorni, ora che praticamente ogni programma sotto il sole ha una GUI di qualche tipo. Ha più senso con le app della console, poiché è necessario aggiornare continuamente l'output per fornire istruzioni o richiedere all'utente nuove informazioni. Con una GUI, al contrario, il testo che fornisce tali informazioni all'utente può essere semplicemente posizionato su un modulo e non deve mai essere ripetuto a livello di programmazione.


0

Uso continuamente i cicli do-while durante la lettura dei file. Lavoro con molti file di testo che includono commenti nell'intestazione:

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

userò un ciclo do-while per leggere fino alla riga "colonna1 colonna2" in modo da poter cercare la colonna di interesse. Ecco lo pseudocodice:

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

Quindi farò un ciclo while per leggere il resto del file.


Supportare commenti ovunque nel file sarebbe banale (e semplificherebbe il codice in generale) se si inserisse if (line[0]=='#') continue;subito dopo la read_linechiamata nel ciclo while principale ...
R .. GitHub STOP HELPING ICE

Non riesco a vedere come implementerei il tuo suggerimento per trovare un'intestazione. (dang, non riesco a capire come formattare il codice nei commenti, aiuto per favore!) header = false; while (! header) {line = read_line (); if (riga [0] == '#') continue; header = true; } questo ti è più chiaro / semplice?
vola il

0

Essendo un programmatore bislacco, molti dei miei progetti di programmazione scolastica utilizzavano interazioni guidate da menu di testo. Praticamente tutti usavano qualcosa di simile alla seguente logica per la procedura principale:

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

Sin dai tempi della scuola, ho scoperto di utilizzare il ciclo while più frequentemente.


0

Una delle applicazioni che ho visto è in Oracle quando guardiamo i set di risultati.

Una volta che hai un set di risultati, per prima cosa recuperi da esso (do) e da quel punto in poi .. controlla se il fetch restituisce un elemento o meno (mentre l'elemento trovato ..) .. Lo stesso potrebbe essere applicabile a qualsiasi altro " implementazioni "fetch-like".


0

L'ho usato in una funzione che restituiva la posizione del carattere successivo in una stringa utf-8:

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

Nota che questa funzione è scritta dalla mente e non testata. Il punto è che devi comunque fare il primo passo e devi farlo prima di poter valutare la condizione.


È un brutto modo di scrivere *(unsigned char *)txt-0x80U<0x40.
R .. GitHub STOP AIUTO AL GHIACCIO

0

Qualsiasi tipo di input della console funziona bene con do-while perché viene richiesto la prima volta e viene nuovamente richiesto ogni volta che la convalida dell'input non riesce.


0

Anche se ci sono molte risposte qui è la mia opinione. Tutto si riduce all'ottimizzazione. Mostrerò due esempi in cui uno è più veloce dell'altro.

Caso 1: while

string fileName = string.Empty, fullPath = string.Empty;

while (string.IsNullOrEmpty(fileName) || File.Exists(fullPath))
{
    fileName = Guid.NewGuid().ToString() + fileExtension;
    fullPath = Path.Combine(uploadDirectory, fileName);
}

Caso 2: do while

string fileName = string.Empty, fullPath = string.Empty;

do
{
    fileName = Guid.NewGuid().ToString() + fileExtension;
    fullPath = Path.Combine(uploadDirectory, fileName);
}
while (File.Exists(fullPath));

Quindi due faranno esattamente le stesse cose. Ma c'è una differenza fondamentale ed è che il while richiede un'istruzione in più per inserire il tempo. Il che è brutto perché diciamo che ogni possibile scenario della Guidclasse è già stato preso tranne una variante. Ciò significa che dovrò fare un giro di 5,316,911,983,139,663,491,615,228,241,121,400,000volte. Ogni volta che arrivo alla fine della mia dichiarazione while dovrò fare il string.IsNullOrEmpty(fileName)controllo. Quindi questo richiederebbe un po ', una piccola frazione del lavoro della CPU. Ma fai questo compito molto piccolo moltiplicando le possibili combinazioni di fileGuid classe e parliamo di ore, giorni, mesi o tempo supplementare?

Ovviamente questo è un esempio estremo perché probabilmente non lo vedresti in produzione. Ma se pensassimo all'algoritmo di YouTube, è molto probabile che incontrino la generazione di un ID in cui sono già stati presi alcuni ID. Quindi si tratta di grandi progetti e ottimizzazione.


0

Mi piace intendere questi due come:
while-> 'ripeti fino a',
do ... while-> 'ripeti se'.


-1

Mi sono imbattuto in questo mentre cercavo il ciclo corretto da utilizzare per una situazione che ho. Credo che questo soddisferà pienamente una situazione comune in cui un ciclo do .. while è un'implementazione migliore di un ciclo while (linguaggio C #, poiché hai affermato che è il tuo principale per il lavoro).

Sto generando un elenco di stringhe in base ai risultati di una query SQL. L'oggetto restituito dalla mia query è un SQLDataReader. Questo oggetto ha una funzione chiamata Read () che fa avanzare l'oggetto alla riga di dati successiva e restituisce true se c'era un'altra riga. Restituirà false se non c'è un'altra riga.

Usando queste informazioni, voglio riportare ogni riga in un elenco, quindi fermarmi quando non ci sono più dati da restituire. Un ciclo Do ... While funziona meglio in questa situazione in quanto garantisce che l'aggiunta di un elemento all'elenco avvenga PRIMA di controllare se c'è un'altra riga. Il motivo per cui questo deve essere fatto PRIMA di controllare il while (condizione) è che quando controlla, avanza anche. L'uso di un ciclo while in questa situazione provocherebbe il bypass della prima riga a causa della natura di quella particolare funzione.

In breve:

Questo non funzionerà nella mia situazione.

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

   return list;

Questo sarà.

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

    return list;

L'utilizzo di SQLDataReader sarebbe cicli nidificati. Quella interna chiama Read()e dovrebbe essere un ciclo while, poiché la prima Read()chiamata accede alla prima riga. Quella esterna chiama NextResult()e dovrebbe essere una pratica, perché il primo set di dati dei risultati è attivo prima della prima NextResult()chiamata. Gli snippet di codice non hanno molto senso, soprattutto perché i commenti non corrispondono al codice e sono sbagliati.
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;

Dovresti rispondere alla tua domanda come se volessi rispondere a quella di qualcun altro. Inclusa una spiegazione e un codice
Simon
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.