Come strutturare un ciclo che si ripete fino al successo e gestisce i fallimenti

8

Sono un programmatore autodidatta. Ho iniziato a programmare circa 1,5 anni fa. Ora ho iniziato ad avere lezioni di programmazione a scuola. Abbiamo avuto lezioni di programmazione per 1/2 anni e ne avremo un'altra 1/2 adesso.

Nelle classi stiamo imparando a programmare in C ++ (che è un linguaggio che già sapevo di usare abbastanza bene prima di iniziare).

Non ho avuto alcuna difficoltà durante questa lezione, ma c'è un problema ricorrente a cui non sono stato in grado di trovare una soluzione chiara.

Il problema è come questo (in Pseudocode):

 do something
 if something failed:
     handle the error
     try something (line 1) again
 else:
     we are done!

Ecco un esempio in C ++

Il codice richiede all'utente di inserire un numero e lo fa fino a quando l'input non è valido. Usa cin.fail() per verificare se l'input non è valido. Quando cin.fail() è true Devo chiamare cin.clear() e cin.ignore() per poter continuare a ricevere input dal flusso.

I am aware that this code does not check of EOF. The programs we have written are not expected to do that.

Ecco come ho scritto il codice in uno dei miei compiti a scuola:

for (;;) {
    cout << ": ";
    cin >> input;
    if (cin.fail()) {
        cin.clear();
        cin.ignore(512, '\n');
        continue;
    }
    break;
}

Il mio insegnante ha detto che non dovrei usare break e continue come questo. Ha suggerito che dovrei usare un normale while o do ... while loop invece.

Mi sembra che usare break e continue sia il modo più semplice per rappresentare questo tipo di loop. Ci ho pensato per un po ', ma non ho trovato una soluzione più chiara.

Penso che volesse che facessi qualcosa del genere:

do {
    cout << ": ";
    cin >> input;
    bool fail = cin.fail();
    if (fail) {
        cin.clear();
        cin.ignore(512, '\n');
    }
} while (fail);

Per me questa versione sembra molto più complessa poiché ora abbiamo anche una variabile chiamata fail per tenere traccia di e il controllo per l'errore di input è stato eseguito due volte invece di una sola volta.

Ho anche immaginato di poter scrivere il codice in questo modo (abusando della valutazione del cortocircuito):

do {
    cout << ": ";
    cin >> input;
    if (fail) {
        cin.clear();
        cin.ignore(512, '\n');
    }
} while (cin.fail() && (cin.clear(), cin.ignore(512, '\n', true);

Questa versione funziona esattamente come le altre. Non usa break o continue e il test cin.fail() viene eseguito una sola volta. Tuttavia non mi sembra giusto abusare della "regola di valutazione del cortocircuito" come questa. Non credo nemmeno che il mio insegnante lo gradirebbe.

Questo problema non si applica solo al controllo cin.fail() . Ho usato break e continue come questo per molti altri casi che comportano la ripetizione di un set di codice finché non viene soddisfatta una condizione in cui deve essere fatto qualcosa anche se la condizione non è soddisfatta (come chiamare cin.clear() e cin.ignore(...) dall'esempio cin.fail() ).

Ho continuato a utilizzare break e continue durante il corso e ora il mio insegnante ha ora smesso di lamentarsi.

Quali sono le tue opinioni su questo?

Pensi che il mio insegnante abbia ragione?

Conosci un modo migliore per rappresentare questo tipo di problema?

    
posta wefwefa3 08.01.2015 - 22:47
fonte

5 risposte

15

Vorrei scrivere un'istruzione if leggermente diversa, quindi viene presa quando l'input ha esito positivo.

for (;;) {
    cout << ": ";
    if (cin >> input)
        break;
    cin.clear();
    cin.ignore(512, '\n');
}

È anche più corto.

Che suggerisce un modo più breve che potrebbe piacere al tuo insegnante:

cout << ": ";
while (!(cin >> input)) {
    cin.clear();
    cin.ignore(512, '\n');
    cout << ": ";
}
    
risposta data 09.01.2015 - 00:58
fonte
15

Ciò che devi cercare è evitare loop grezzi .

Sposta la logica complessa in una funzione di aiuto e improvvisamente le cose sono molto più chiare:

bool getValidUserInput(string & input)
{
    cout << ": ";
    cin >> input;
    if (cin.fail()) {
        cin.clear();
        cin.ignore(512, '\n');
        return false;
    }
    return true;
}

int main() {
    string input;
    while (!getValidUserInput(input)) { 
        // We wait for a valid user input...
    }
}
    
risposta data 09.01.2015 - 18:52
fonte
9

Non è tanto che for(;;) sia cattivo. Non è chiaro come modelli come:

while (cin.fail()) {
    ...
}

O come dice Sjoerd:

while (!(cin >> input)) {
    ...
}

Consideriamo il pubblico principale per questa roba come i tuoi colleghi programmatori, inclusa la versione futura di te stesso che non ricorda più perché hai interrotto la pausa alla fine in quel modo, o hai saltato la pausa con il continuare. Questo modello dal tuo esempio:

for (;;) {
    ...
    if (blah) {
        continue;
    }
    break;
}

... richiede un paio di secondi di pensiero extra per simulare rispetto ad altri modelli. Non è una regola dura e veloce, ma saltare la frase break con il continuare si sente disordinato, o intelligente senza essere utile, e mettere l'istruzione break alla fine del ciclo, anche se funziona, è insolito. Normalmente sia break che continue vengono utilizzati per prematuramente per evacuare il ciclo o l'iterazione del ciclo, quindi vedere uno di questi alla fine si sente un po 'strano anche se non lo è.

Oltre a questo, roba buona!

    
risposta data 09.01.2015 - 05:18
fonte
3

Il mio istruttore logico a scuola diceva sempre, e mi batteva nel cervello, che dovevano esserci solo un'entrata e un'uscita ai circuiti. In caso contrario, inizi a ricevere il codice spaghetti e non sapere dove sta andando il codice durante il debug.

Nella vita reale, ritengo che la leggibilità del codice sia davvero importante in modo che se qualcun altro debba risolvere o eseguire il debug di un problema con il tuo codice, è facile per loro.

Inoltre, se si tratta di un problema di prestazioni perché stai attraversando questo aspetto milioni di volte il ciclo for potrebbe essere più veloce. I test risponderebbero a questa domanda.

Non conosco il C ++ ma ho completamente compreso i primi due esempi di codice. Suppongo che continui fino alla fine del ciclo for e lo ritorni di nuovo. L'esempio while l'ho capito completamente ma per me è stato più facile da capire rispetto al tuo per (;;) esempio. Il terzo dovrei fare qualche lavoro di riflessione per capirlo.

Quindi, per me è stato più facile leggere (non conoscendo c ++) e quello che il mio istruttore di logica ha detto che avrei usato il ciclo while.

    
risposta data 08.01.2015 - 23:11
fonte
-2

Per me, la traduzione C più diretta dello pseudocodice è

do
    {
    success = something();
    if (success == FALSE)
        {
        handle_the_error();
        }
    } while (success == FALSE)
\ moving on ...

Non capisco perché questa traduzione ovvia sia un problema.

forse questo:

while (!something())
    {
    handle_the_error();
    }

sembra più semplice.

    
risposta data 09.01.2015 - 19:43
fonte

Leggi altre domande sui tag