do {…} while (false)


125

Stavo guardando un codice da un individuo e ho notato che sembra avere uno schema nelle sue funzioni:

<return-type> function(<params>)
{
 <initialization>

 do
 {
   <main code for function>
 }
 while(false);

 <tidy-up & return>
}

Non è male , più peculiare (il codice effettivo è abbastanza pulito e non sorprende). Non è qualcosa che ho visto prima e mi chiedevo se qualcuno possa pensare a qualche logica dietro di esso - forse sfondo in una lingua diversa?


2
Hai provato a cambiarlo nella versione "normale" e vedere se si compila ancora? Se non riesce a compilare quando viene scritto normalmente, l'errore del compilatore potrebbe fornire un indizio sul motivo per cui è stato fatto.
Mark Byers

11
Perché non chiedere all '"individuo" invece di chiederci ipotesi sulla sua intenzione?

1
Forse il suo professore ha richiesto che usasse un do...while()e da allora ha usato quel codice come modello di funzione.
Hogan

6
@ Neil ... Voglio evitare di sembrare stupido quando lo confronto e lui spiega l'uso super avanzato del C ++ a cui non avevo mai pensato.
Mr. Boy

6
Sembra che ci siano già alcune risposte diverse, puoi chiedere all'autore originale quali erano le sue intenzioni e postare di nuovo?
nevets1219

Risposte:


175

Puoi breakuscire do{...}while(false).


95
+1 perché è probabilmente l'intento del codice, ma fare qualcosa di simile è solo un goto mascherato da idioti. Se pensi che un goto sia lo strumento giusto per il lavoro, dovresti semplicemente usare un # $ (* # @ goto.
dsimcha

56
È più di un goto mascherato. È un goto limitato (strutturato).
Thomas Eding,

19
In che modo è "limitato"? Solo saltare in avanti non è certo una "restrizione". Un goto è un goto, e vestirlo per far sembrare che non lo sia è peggio che usare solo un goto in primo luogo.
Anon.

45
@Anon .: Saltare in avanti è una restrizione per un goto, e saltare fuori è sicuramente una restrizione. Il vero problema con gotos è il codice spaghetti, e un salto in avanti e in uscita lo limita notevolmente.
David Thornley,

33
Un ciclo effettivo non è semanticamente un goto. Un condizionale non è semanticamente un goto. "Vai alla fine della funzione e fai il codice di pulizia" è semanticamente un goto. Usa gotos quando si applica la semantica, non vestire qualcosa di semanticamente diverso perché ne hai paura.
Anon.

126

Molte persone sottolineano che è spesso usato con break come un modo scomodo di scrivere "goto". Probabilmente è vero se è scritto direttamente nella funzione.

In una macro, OTOH, do { something; } while (false)è un modo conveniente per FORZARE un punto e virgola dopo il richiamo della macro, assolutamente nessun altro token può seguire.

E un'altra possibilità è che una volta c'era un loop lì o si prevede che l'iterazione verrà aggiunta in futuro (ad es. Nello sviluppo basato sui test, l'iterazione non era necessaria per superare i test, ma logicamente avrebbe senso eseguire il loop lì se la funzione doveva essere un po 'più generale di quella attualmente richiesta)


18
+1 per aver menzionato l'utilità di questo nelle macro; Sono sorpreso che nessun altro lo abbia menzionato!
Nick Meyer

7
Sì, la cosa macro è in realtà un uso perfettamente valido di questo. Ovviamente, al di fuori delle macro, è semplicemente stupido ...;)
jalf

12
Non è scomodo, è utile, perché è un goto con Scoped : significa che tutte le variabili che dichiari nel ciclo do vengono distrutte, mentre goto non lo fa.
Ana Betts,

9
@Paul: Niente ti impedisce di aggiungere parentesi graffe attorno a un gruppo di istruzioni per forzare questo comportamento con goto.
erikkallen

5
@ Paul: goto out of a block fa sì che le variabili locali vengano distrutte in C ++, come break. E in C le variabili non vengono realmente distrutte, il loro spazio di stack viene semplicemente recuperato.
Ben Voigt

25

L'interruzione come goto è probabilmente la risposta, ma proporrò un'altra idea.

Forse voleva avere variabili definite localmente e ha usato questo costrutto per ottenere un nuovo ambito.

Ricorda che mentre il recente C ++ consente {...}ovunque, non è sempre stato così.


30
Avrebbe potuto usare solo le parentesi graffe in quel caso.
Nemanja Trifunovic

2
@Nemanja, rimarrai sorpreso da quanti sviluppatori non lo sanno e
proverai

8
@Polaris @Nemanja, non è stato fino a quando ho programmato in C / C ++ come 4 anni che ho capito che puoi creare un nuovo ambito locale {} ovunque .. Questo è particolarmente utile nel switch-casecodice
Earlz

1
@Nemanja: non so esattamente quando, ma sono sicuro che {...}ovunque sia uno sviluppo più moderno in C ++ (ricorda che il primo C ++ che ho usato è stato implementato con un pre-processore e questo non ha permesso questo uso moderno). l'autore era semplicemente vecchia scuola.
Hogan

2
Quando non ha consentito l'uso di parentesi graffe ovunque? Ho iniziato a programmare come 15 anni fa, e allora era permesso (sia nel mio libro di testo che in ogni compilatore che ho provato).
erikkallen

20

L'ho visto usato come un modello utile quando ci sono molti potenziali punti di uscita per la funzione, ma lo stesso codice di pulizia è sempre richiesto indipendentemente da come la funzione esce.

Può rendere un faticoso albero if / else-if molto più facile da leggere, semplicemente dovendo rompere ogni volta che viene raggiunto un punto di uscita, con il resto della logica in linea in seguito.

Questo modello è utile anche nelle lingue che non dispongono di un'istruzione goto. Forse è lì che il programmatore originale ha imparato il modello.


15
Quindi usa un goto onesto e diretto invece di un goto sottilmente camuffato.
dsimcha

4
Mi piace di più così. È facile da leggere e non porta lo stigma di un goto.
Cameron

8
gotos sono perfettamente facili da leggere se usati con parsimonia e localmente. Hanno ottenuto il loro stigma da quando erano la principale forma di controllo del flusso e stavano saltando attraverso centinaia di linee.
dsimcha

13
Non usare goto a causa di uno "stigma" è un segno sicuro di programmazione cargo-cult.
Anon.

6
Per non parlare del fatto che una massa di codice di pulizia è un cattivo odore. Essendo C ++, il codice di pulizia dovrebbe normalmente trovarsi nei distruttori chiamati durante l'elaborazione RAII.
David Thornley,

12

Ho visto codice del genere, quindi puoi usarlo breakcome una gotosorta di.


10

Questa è solo una perversione di whileottenere la sematica di goto tidy-upsenza usare la parola goto.

E 'di cattivo gusto, perché quando si utilizzano altri loop all'interno del esterna whiledel breaksdiventano ambigue al lettore. "Questo dovrebbe andare a uscire? O è destinato solo a rompere il ciclo interno?"


10

Penso che sia più conveniente scrivere breakinvece di goto end. Non devi nemmeno pensare a un nome per l'etichetta che renda più chiara l'intenzione: non vuoi saltare a un'etichetta con un nome specifico. Vuoi uscire di qui.

Inoltre è probabile che tu abbia comunque bisogno delle parentesi graffe. Quindi questa è la do{...}while(false);versione:

do {
   // code
   if (condition) break; // or continue
   // more code
} while(false);

E questo è il modo in cui dovresti esprimerlo se volessi usare goto:

{
   // code
   if (condition) goto end;
   // more code
}
end:

Penso che il significato della prima versione sia molto più facile da capire. Inoltre è più facile scrivere, più facile da estendere, più facile da tradurre in una lingua che non supporta goto, ecc.


La preoccupazione più frequentemente menzionata riguardo all'uso di breakè che è un male mascherato goto. Ma in realtà breakha più somiglianza con return: Entrambe le istruzioni saltano fuori da un blocco di codice che è più o meno strutturato rispetto a goto. Tuttavia entrambe le istruzioni consentono più punti di uscita in un blocco di codice che a volte può creare confusione. Dopotutto, cercherò di trovare la soluzione più chiara, qualunque cosa sia nella situazione specifica.


Oh, ho appena notato che non ho capito completamente la domanda prima di rispondere. Ho pensato che riguardasse l'uso do{ ... }while(false);in generale. Ma in realtà si tratta di usarlo per emulare una sorta di try{ ... }finally{ ... }.
Robert

Bella l'osservazione del parallelo tra pausa e ritorno piuttosto che goto. D'accordo che la soluzione più chiara è la migliore. In molti casi è probabilmente più chiaro incapsulare codice come questo nella propria funzione separata che utilizza return piuttosto che break e quindi esegue la pulizia nella funzione chiamante.
persiflage

7

Questo trucco è usato dai programmatori che sono troppo timidi per usare un esplicito gotonel loro codice. L'autore del codice precedente voleva avere la possibilità di passare direttamente al punto "pulizia e restituzione" dalla metà del codice. Ma non volevano usare un'etichetta ed esplicito goto. Invece, possono utilizzare un breakall'interno del corpo del ciclo "falso" di cui sopra per ottenere lo stesso effetto.


7

È una pratica molto comune. In C . Cerco di pensarlo come se volessi mentire a te stesso in un modo "non sto usando un goto". A pensarci bene, non ci sarebbe nulla di sbagliato in un gotousato allo stesso modo. Infatti ridurrebbe anche il livello di indentazione.

Detto questo, però, ho notato, molto spesso questo do..whileloop tende a crescere. E poi ottengono ifs e elses all'interno, rendendo il codice in realtà non molto leggibile, figuriamoci testabile.

Quelli do..whilesono normalmente destinati a fare una pulizia . Con tutti i mezzi possibili preferirei usare RAII e tornare presto da una breve funzione. D'altra parte, il C non ti offre tante comodità come il C ++ , rendendo do..whileuno dei migliori approcci per fare una pulizia.


6

Sembra un programmatore C. In C ++, le variabili automatiche hanno distruttori che usi per ripulire, quindi non dovrebbe esserci nulla di necessario per riordinare prima del ritorno. In C, non avevi questo idioma RAII , quindi se hai un codice di ripulitura comune, gotolo o usi un ciclo una tantum come sopra.

Il suo principale svantaggio rispetto all'idioma C ++ è che non si riordina se viene lanciata un'eccezione nel corpo. Il C non aveva eccezioni, quindi questo non era un problema, ma lo rende una cattiva abitudine in C ++.


4

Diverse spiegazioni. Il primo è generale, il secondo è specifico per le macro del preprocessore C con parametri:

Controllo del flusso

L'ho visto usato nel semplice codice C. Fondamentalmente, è una versione più sicura di goto, poiché puoi uscirne e tutta la memoria viene ripulita correttamente.

Perché qualcosa di gotosimile dovrebbe essere buono? Bene, se hai un codice in cui praticamente ogni riga può restituire un errore, ma devi reagire a tutti allo stesso modo (ad esempio consegnando l'errore al chiamante dopo la pulizia), di solito è più leggibile evitare un if( error ) { /* cleanup and error string generation and return here */ }as evita la duplicazione del codice di pulizia.

Tuttavia, in C ++ hai eccezioni + RAII esattamente per questo scopo, quindi lo considererei un cattivo stile di codifica.

Controllo punto e virgola

Se dimentichi il punto e virgola dopo un'invocazione di una macro simile a una funzione, gli argomenti potrebbero contrarsi in modo indesiderato e compilarsi in una sintassi valida. Immagina la macro

#define PRINT_IF_DEBUGMODE_ON(msg) if( gDebugModeOn ) printf("foo");

Questo viene accidentalmente chiamato come

if( foo )
    PRINT_IF_DEBUGMODE_ON("Hullo\n")
else
    doSomethingElse();

L '"altro" sarà considerato associato a gDebugModeOn, quindi quando fooè false, accadrà l'esatto contrario di ciò che era previsto.

Fornire un ambito per le variabili temporanee.

Poiché il do / while ha le parentesi graffe, le variabili temporanee hanno un ambito chiaramente definito da cui non possono sfuggire.

Evitare gli avvisi di "punto e virgola eventualmente indesiderato"

Alcune macro vengono attivate solo nelle build di debug. Li definisci come:

#if DEBUG
#define DBG_PRINT_NUM(n) printf("%d\n",n);
#else
#define DBG_PRINT_NUM(n) 
#endif

Ora, se lo usi in una build di rilascio all'interno di un condizionale, si compila in

if( foo )
    ;

Molti compilatori lo vedono come lo stesso di

if( foo );

Che è spesso scritto accidentalmente. Quindi ricevi un avviso. Il do {} while (false) lo nasconde al compilatore e viene accettato da esso come un'indicazione che non si vuole fare nulla qui.

Evitare l'acquisizione di linee da condizionali

Macro dell'esempio precedente:

if( foo )
    DBG_PRINT_NUM(42)
doSomething();

Ora, in una build di debug, poiché abitualmente includiamo anche il punto e virgola, viene compilato perfettamente. Tuttavia, nella build di rilascio questo si trasforma improvvisamente in:

if( foo )

doSomething();

O più chiaramente formattato

if( foo )
    doSomething();

Che non è affatto ciò che era previsto. L'aggiunta di un do {...} while (false) attorno alla macro trasforma il punto e virgola mancante in un errore di compilazione.

Cosa significa per l'OP?

In generale, si desidera utilizzare le eccezioni in C ++ per la gestione degli errori e i modelli invece delle macro. Tuttavia, nel caso molto raro in cui hai ancora bisogno di macro (ad es. Quando si generano nomi di classi usando il token paste) o sono limitati al semplice C, questo è un modello utile.


Per quanto riguarda lo scoping, non è necessario l'apparato loop; un blocco "disadorno" è legale: x =1; y = 2; { int tmp = y; y = x; x = tmp; }.
chepner

Un blocco disadorno non impone tuttavia la presenza di un punto e virgola mancante, il che potrebbe avere effetti collaterali indesiderati. Fare durante(); RICHIEDE il punto e virgola e quindi, ad esempio, non fa sì che le seguenti istruzioni vengano inserite in un "se" se la macro non definisce nulla in una build di rilascio, come il mio esempio sopra.
uli testimone

Non potrebbe essere evitato fornendo semplicemente una migliore definizione per la macro? #define DBG_PRINT_NUM(n) {}.
chepner

1
No, perché {} è un'istruzione completa, vale a dire equivalente a un ";". Quindi se scrivi if(a) DBG_PRINT_NUM(n); else other();non verrà compilato. Solo if(a) {} else other();o if(a) ; else other();è valido, ma if(a) {}; else other();non lo è, perché ciò fa sì che la clausola "if" sia composta da due istruzioni.
testimone

2

Forse è usato in modo che breakpossa essere usato all'interno per interrompere l'esecuzione di ulteriore codice in qualsiasi momento:

do {
    if (!condition1) break;
    some_code;
    if (!condition2) break;
    some_further_code;
    // …
} while(false);

7
In questo modo sembra un tentativo di evitare di utilizzare gotosolo perché qualcuno ha sentito che è "cattivo".
Anon.

1
Forse, ma il C ++ ha delle eccezioni proprio per questo motivo.
TED

2

Penso che questo sia fatto per usare breako continuedichiarazioni. Una sorta di logica del codice "goto".


2

È semplice: a quanto pare puoi uscire dal falso loop in qualsiasi momento usando l' breakaffermazione. Inoltre, il doblocco è un ambito separato (che potrebbe anche essere ottenuto { ... }solo con ).

In una situazione del genere, potrebbe essere un'idea migliore usare RAII (oggetti che si distruggono automaticamente correttamente quando la funzione termina). Un altro costrutto simile è l'uso di goto- sì, lo so che è malvagio , ma può essere usato per avere un codice di pulizia comune in questo modo:

<return-type> function(<params>)
{
 <initialization>

 <main code for function using "goto error;" if something goes wrong>

 <tidy-up in success case & return>

 error:

 <commmon tidy-up actions for error case & return error code or throw exception>
}

(Per inciso: il costrutto do-while-false è usato in Lua per trovare l' continueaffermazione mancante .)


2

Molti rispondenti hanno spiegato il motivo do{(...)break;}while(false). Vorrei completare l'immagine con un altro esempio di vita reale.

Nel codice seguente ho dovuto impostare l'enumeratore in operationbase all'indirizzo puntato dal datapuntatore. Poiché uno switch-case può essere utilizzato solo su tipi scalari prima l'ho fatto in modo inefficiente in questo modo

if (data == &array[o1])
    operation = O1;
else if (data == &array[o2])
    operation = O2;
else if (data == &array[on])
    operation = ON;

Log("operation:",operation);

Ma poiché Log () e il resto del codice si ripetono per qualsiasi valore di operazione scelto, stavo vagando su come saltare il resto dei confronti quando l'indirizzo è stato già scoperto. Ed è qui che do{(...)break;}while(false)torna utile.

do {
    if (data == &array[o1]) {
        operation = O1;
        break;
    }
    if (data == &array[o2]) {
        operation = O2;
        break;
    }
    if (data == &array[on]) {
        operation = ON;
        break;
    }
} while (false);

Log("operation:",operation);

Ci si potrebbe chiedere perché non ha potuto fare lo stesso con breakuna ifdichiarazione, come:

if (data == &array[o1])
{
    operation = O1;
    break;
}
else if (...)

breakinteragisce esclusivamente con il loop o l'interruttore che lo racchiude più vicino , sia esso a for, whileo do .. whiletype, quindi sfortunatamente non funzionerà.


1

Quanti anni aveva l'autore?

Lo chiedo perché una volta mi sono imbattuto in un codice Fortran in tempo reale che lo ha fatto, alla fine degli anni '80. Si scopre che è un ottimo modo per simulare i thread su un sistema operativo che non li ha. Devi solo mettere l'intero programma (il tuo scheduler) in un ciclo e chiamare le tue routine "thread" "una per una. Le routine dei thread stesse sono cicli che ripetono fino a quando non si verifica una delle numerose condizioni (spesso una è una certa quantità di tempo è passato). È "multitasking cooperativo", in quanto spetta ai singoli thread rinunciare alla CPU ogni tanto in modo che gli altri non vengano affamati. Puoi annidare le chiamate di sottoprogramma in loop per simulare la priorità del thread bande.



0

Sono d'accordo con la maggior parte dei poster sull'utilizzo come goto sottilmente camuffato. Le macro sono state anche menzionate come potenziale motivazione per scrivere codice nello stile.

Ho anche visto questo costrutto utilizzato in ambienti misti C / C ++ come un'eccezione per i poveri. Il "do {} while (false)" con un "break" può essere usato per saltare alla fine del blocco di codice nel caso in cui qualcosa che normalmente dovrebbe giustificare un'eccezione si incontra nel ciclo.

Ho anche sentito questo costrutto utilizzato nei negozi dove viene applicata l'ideologia del "rendimento unico per funzione". Ancora una volta, questo è al posto di un "goto" esplicito, ma la motivazione è di evitare più punti di ritorno, non di "saltare" il codice e continuare l'esecuzione effettiva all'interno di quella funzione.


0

Lavoro con Adobe InDesign SDK e gli esempi di InDesign SDK hanno quasi tutte le funzioni scritte in questo modo. È dovuto al fatto che le funzioni sono solitamente molto lunghe. Dove è necessario eseguire QueryInterface (...) per ottenere qualsiasi cosa dal modello a oggetti dell'applicazione. Quindi di solito ogni QueryInterface è seguita da ifnot gone well, break.


0

Molti hanno già affermato la somiglianza tra questo costrutto e a goto, e hanno espresso una preferenza per il goto. Forse il background di questa persona includeva un ambiente in cui i goto erano severamente vietati dalle linee guida di codifica?


0

L'altro motivo che mi viene in mente è che decora le parentesi graffe, mentre io credo in un nuovo standard C ++ le parentesi graffe nude non vanno bene (ISO C non le piacciono). Altrimenti per silenziare un analizzatore statico come lanugine.

Non sono sicuro del motivo per cui li vorresti, forse portata variabile o vantaggio con un debugger.

Vedere Trivial Do While loop e Braces are Good from C2.

Per chiarire la mia terminologia (che credo segua l'uso standard):

Bretelle nude :

init();
...
{
c = NULL;
mkwidget(&c);
finishwidget(&c);
}
shutdown();

Parentesi graffe vuote (NOP):

{}

per esempio

while (1)
   {}  /* Do nothing, endless loop */

Blocco :

if (finished)
{
     closewindows(&windows);
     freememory(&cache);
}

che diventerebbe

if (finished)
     closewindows(&windows);
freememory(&cache);

se le parentesi graffe vengono rimosse, alterando così il flusso di esecuzione, non solo l'ambito delle variabili locali. Quindi non "indipendente" o "nudo".

Le parentesi graffe nude o un blocco possono essere utilizzati per indicare qualsiasi sezione di codice che potrebbe essere un potenziale per una funzione (inline) che si desidera contrassegnare, ma non refactoring in quel momento.


Veramente? È un peccato. Una delle poche cose che mi sono piaciute di C è stata la possibilità di dichiarare un nuovo ambito annidato praticamente ovunque.
TED

1
Le parentesi graffe nude sono occasionalmente utili per correggere strani arresti anomali. Vedi blogs.msdn.com/oldnewthing/archive/2004/05/20/135841.aspx .
Brian,

1
In C ++, un blocco (cioè le tue "parentesi graffe nude") può essere utilizzato ovunque sia consentita una singola istruzione.
Ben Voigt

@BenVoigt Parentesi graffe vuote, ovvero un NOP è diverso da una "parentesi graffa nuda" che è un blocco aggiunto attorno a una sequenza lineare di istruzioni. Ad esempio `printf (" Hello "); {putchar (','); putchar (0x20); } printf ("world! \ n"); `dove le parentesi graffe non fanno parte del controllo del ciclo o del ramo.
mctylr

@ mctylr: non stavo parlando di parentesi graffe vuote.
Ben Voigt

0

È un modo artificioso per emulare un GOTOdato che questi due sono praticamente identici:

// NOTE: This is discouraged!
do {
    if (someCondition) break;
    // some code be here
} while (false);
// more code be here

e:

// NOTE: This is discouraged, too!
if (someCondition) goto marker;
// some code be here
marker:
// more code be here

D'altra parte, entrambi dovrebbero essere fatti con ifs:

if (!someCondition) {
    // some code be here
}
// more code be here

Anche se l'annidamento può diventare un po 'brutto se si trasforma semplicemente una lunga stringa di forward GOTOin ifs annidati . La vera risposta è un corretto refactoring, non imitando i costrutti linguistici arcaici.

Se stavi cercando disperatamente di traslitterare un algoritmo contenente GOTOs, probabilmente potresti farlo con questo idioma. Tuttavia, è certamente non standard e un buon indicatore del fatto che non stai aderendo strettamente agli idiomi previsti per la lingua.

Non sono a conoscenza di alcun linguaggio simile al C in cui do / while è una soluzione idiomatica per qualsiasi cosa, in realtà.

Probabilmente potresti rifattorizzare l'intero pasticcio in qualcosa di più sensato per renderlo più idiomatico e molto più leggibile.


1
Alan, nondo..while(false) è per correre un numero "indefinito" di volte, è per correre una volta .
Dmitry

2
Viene eseguito un numero indefinito di volte se hai continueun'istruzione condizionale alla fine come ho mostrato. Con undefinedintendo semplicemente che non si sa se è in esecuzione più di una volta a meno che non si può prevedere se le condizioni saranno soddisfatte in una specifica iterazione. Senza alcun continues in esso, do..while(false)verrà eseguito una volta, ma anche senza alcun breaks in esso, while(true)verrà eseguito per sempre, quindi il "comportamento predefinito" non è realmente rilevante per capire cosa si può fare con i loop.
Alan Plum,

È utile quando si definisce una macro con più istruzioni. Se li racchiudi in un do / while (falso) puoi usare la macro in un'istruzione if / else come qualsiasi altra chiamata di funzione. vedi noveltheory.com/TechPapers/while.htm per una spiegazione
John Paquin

5
Qualcuno non capisce la semantica di continue. continueNON ripete il ciclo. "L' continueistruzione deve verificarsi solo in un'istruzione di iterazione e fa sì che il controllo passi alla parte di continuazione del ciclo della più piccola istruzione di iterazione che lo racchiude , cioè alla fine del ciclo."
Ben Voigt

-1, le prime due affermazioni non sono identiche per i motivi evidenziati da @BenVoigt.
cmh

0

Alcuni programmatori preferiscono avere una sola uscita / ritorno dalle loro funzioni. L'uso di un dummy do {....} while (false); ti permette di "rompere" il ciclo fittizio una volta che hai finito e avere ancora un singolo ritorno.

Sono un programmatore Java, quindi il mio esempio sarebbe qualcosa di simile

import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class p45
{
    static List<String> cakeNames = Arrays.asList("schwarzwald torte", "princess", "icecream");
    static Set<Integer> forbidden = Stream.of(0, 2).collect(Collectors.toSet());

    public static  void main(String[] argv)
    {
        for (int i = 0; i < 4; i++)
        {
            System.out.println(String.format("cake(%d)=\"%s\"", i, describeCake(i)));
        }
    }


    static String describeCake(int typeOfCake)
    {
        String result = "unknown";
        do {
            // ensure type of cake is valid
            if (typeOfCake < 0 || typeOfCake >= cakeNames.size()) break;

            if (forbidden.contains(typeOfCake)) {
                result = "not for you!!";
                break;
            }

            result = cakeNames.get(typeOfCake);
        } while (false);
        return result;
    }
}


-1

Questo è divertente. Probabilmente ci sono interruzioni all'interno del ciclo, come altri hanno detto. Lo avrei fatto in questo modo:

while(true)
{
   <main code for function>
   break; // at the end.
}

2
Con un potenziale di loop per sempre? do..while(false)esce sempre, while(true)è più rischioso.
Dmitry

1
while(true)è l'idioma corretto nella maggior parte delle lingue. Lo troverai spesso nelle applicazioni GUI come ciclo principale del programma. Poiché in pratica stai assumendo che il programma non dovrebbe morire fino a quando non gli viene detto di farlo, do..while(false)ciò causerebbe ogni sorta di logica artificiosa. Questo approccio può essere più audace da un POV perfezionista, ma è semanticamente più facile e quindi meno soggetto a errori per i programmatori umani (scusate, Skynet).
Alan Plum

2
@dmitry do{...}while(false)è esattamente lo stesso diwhile(true){ .. break;}
N 1.1

4
@ N1.1: Non in presenza di continue, non sono la stessa cosa.
Ben Voigt
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.