Il modo consigliato per uscire da un loop

2

Occasionalmente, ma in modo ricorrente, affronto il seguente problema di ciclo:

CodeSnippet1
DO WHILE LoopCondition   //LoopCondition depends on some pre-calculation from CodeSnippet1
    CodeSnippet2         //CodeSnippet2 relies on LoopConditon=true
    CodeSnippet1
LOOP

Il problema che vedo qui è la duplicazione del codice di CodeSnippet1.

A prima vista, sembra che dovrei spostare la condizione fino alla fine:

DO
    CodeSnippet1
    CodeSnippet2
LOOP WHILE LoopCondition

Questo non funzionerà poiché CodeSnippet2 presuppone LoopCondition = true. La prossima prova sarebbe:

DO
    CodeSnippet1
    IF LoopCondition THEN CodeSnippet2
LOOP WHILE LoopCondition

Di nuovo lontano dall'eleganza.

Nei linguaggi di programmazione C-like, si può fare quanto segue:

while(complicated-expression-containing-CodeSnippet1-and-LoopCondition) {
    CodeSnippet2
}

Ma

a) questo può diventare difficile da capire e

b) non tutti i linguaggi di programmazione possono eseguire calcoli arbitrari, in particolare assunzioni variabili, durante la valutazione di alcune condizioni del ciclo.

Recentemente, ho trovato il seguente modo:

DO //endless loop
    CodeSnippet1

    IF NOT LoopCondition THEN EXIT LOOP

    CodeSnippet2
LOOP 

Questo tende a condurre una programmazione strutturata con loop ad absurdum / mi ricorda un po 'di stile di programmazione GoTo.

La mia domanda:

Ci sono (più o meno) ragioni oggettive per preferire uno degli stili di programmazione del ciclo menzionati sopra gli altri?

Modifica

So che in molte occasioni CodeSnippet1 insieme a LoopCondition può essere incapsulato in una funzione restituendo la condizione del ciclo e abbiamo finito. Sono d'accordo. Ma questo non vale in generale. Sopra in b) Ho cercato di suggerire "assegnazioni di variabili". Qui in dettaglio:

Il mio esempio preferito, minimale ma reale: utilizzo di BufferedReader in Java:

line = bufferedReader.readLine();
while(line != null) {
    //CodeSnippet2
    line = bufferedReader.readLine();
}

vs.

while((line = bufferedReader.readLine()) != null) {
    //CodeSnippet2
}

Entrambe le varianti non sono soddisfacenti. Factoring out (line = bufferedReader.readLine()) != null in una funzione che restituisce un valore booleano ha un nuovo problema: come recuperare line ? Usando una variabile globale / membro della classe solo per questo scopo?

    
posta fjf2002 26.02.2015 - 00:35
fonte

3 risposte

12

Congratulazioni! Hai appena reinventato la struttura ad anello di Ada . Jean Ichbiah probabilmente ha reinventato la precedente arte di qualcun altro.

Questo è di fatto possibile in molte lingue, dove puoi creare loop infiniti con uscite programmate. Ad esempio, in Python:

while True:
    do_stuff()
    if not loop_continues:
       break
    do_more_stuff()

Il motivo per cui molte lingue hanno un ciclo test-at-top ( while ), un ciclo test-at-bottom ( do until ) e un ciclo di conteggio / iterazione ( for ) è che quelle sono i modelli più comuni e molto probabilmente necessari. Ma con le moderne opzioni break ("tirami fuori da questo giro!") E continue ("siamo pronti per l'iterazione successiva!"), Non ci sono davvero così tante necessità per la sintassi del loop su misura coprire tutte le opzioni. Python, ad esempio, funziona solo con for e while varianti.

Le operazioni di early-exit ( break ) e early-continue ( continue ) sono ben strutturate, con solo pochi posti ben progettati che possono portarti (ovvero verso il basso / l'uscita del ciclo, o all'inizio del loop). Non sono il "posso saltare a praticamente qualsiasi linea!" i caratteri jolly di olden GOTO days, e quindi possono farti ottenere (e qualunque altro ottimizzatore venga usato dal tuo compilatore) in molte meno situazioni appiccicose.

Dato che ora abbiamo (praticamente indipendentemente dal linguaggio utilizzato) buoni mezzi strutturati per testare e gestire il comportamento del loop, non sono chiaro perché qualcuno avrebbe una strong preferenza. Verifica dove ha senso l'algoritmo coinvolto: in alto, in basso o in un punto intermedio.

    
risposta data 26.02.2015 - 01:01
fonte
0

Perché anche dei commenti di colombi:

Wrap codesnippet1 e la funzione loopcondition in una funzione, perché apparentemente la condizione di loop è impostata da codesnippet1. Che poi restituisce la condizione di loop quindi ottieni:

DO WHILE CodeSnippet1AndLoopConditionFunction   
  CodeSnippet2    
LOOP
    
risposta data 26.02.2015 - 01:28
fonte
0

Mi è stato detto a scuola che sfuggire a un loop usando GOTO o equivalente (come "break" in C) è un cattivo stile di programmazione. Un metodo approvato dalla scuola consiste nell'introdurre una variabile come un interruttore che una volta attivo causerà l'uscita di un ciclo.

In C, questo è un modo approvato dalla scuola per scrivere un ciclo:

int bool=0;
int count=0;
while (count < 100 && bool==0){
    count++;
    if (count == 10){
        bool=1;
    }
}

Questo non è un modo approvato dalla scuola per scrivere un ciclo:

int count=0;
while (count < 100){
    count++;
    if (count == 10){
        break;
    }
}

Tuttavia, la modalità non approvata dalla scuola utilizza pochi byte in meno dello spazio di stack (perché un numero intero in meno è riservato per il programma).

    
risposta data 27.02.2015 - 05:21
fonte

Leggi altre domande sui tag