È possibile uscire per la prima volta in C++, se una conclusione condizione è raggiunto?

StackOverflow https://stackoverflow.com/questions/416464

  •  03-07-2019
  •  | 
  •  

Domanda

Vorrei sapere se è possibile per la fine di un ciclo for in C++ quando una conclusione condizione (diverso dal reacheing numero di iterazioni) è verificata.Per esempio:

for (int i = 0; i < maxi; ++i)
    for (int j = 0; j < maxj; ++j)
        // But if i == 4 < maxi AND j == 3 < maxj, 
        // then jump out of the two nested loops.

So che questo è possibile in Perl con l'ETICHETTA successiva o all'ultima ETICHETTA chiamate ed etichettato blocchi, è possibile farlo in C++ o dovrei usare un ciclo while?

Grazie.

È stato utile?

Soluzione

È possibile utilizzare la parola chiave return: spostare il ciclo nidificato in una subroutine, richiamare la subroutine per eseguire i loop nidificati e "tornare" dalla subroutine per uscire da [tutti] i loop.

Altri suggerimenti

Nonostante il " goto considerato dannoso " argomenti, questo sembra il posto perfetto per <=>. Questo è essenzialmente quello che stai facendo in Perl. Scherzi a parte ... considera le alternative:

Variabili di stato extra


for (int i=0; i<maxi; ++i) {
    bool leaveLoop = false;
    for (int j=0; j<maxj; ++j) {
        if (i == 4 && j == 3) {
            leaveLoop = true;
            break; // leave the inner loop
        }
    }
    if (leaveLoop) {
        break; // leave the outside loop
    }
}

Invia per eccezione


try {
    for (int i=0; i<maxi; ++i) {
        for (int j=0; j<maxj; ++j) {
            if (i == 4 && j == 3) {
                throw leave_loop();
            }
        }
    }
} catch (leave_loop const&) {
}

Logica complessa


int j = 0;
for (int i=0; i<maxi && !(i==4 && j==3); ++i) {
    for (j=0; j<maxj && !(i==4 && j==3); ++j) {
        // inner loop
    }
}

<=>


for (int i=0; i<maxi; ++i) {
    for (int j=0; j<maxj; ++j) {
        if (i==4 && j==3) {
            goto leave_loop;
        }
    }
}
leave_loop:

L'ultimo è meno chiaro? Non ci credo. È più fragile? IMHO, gli altri sono piuttosto inclini agli errori e fragili rispetto alla versione <=>. Mi dispiace essere qui sulla soapbox ma questo è qualcosa che mi ha infastidito per un po ';)

L'unica cosa di cui devi essere consapevole è che <=> e le eccezioni sono abbastanza simili. Entrambi aprono l'opportunità di perdere risorse e cosa non le tratta con cura.

Lasciami dire questo in modo enfatico (ma educatamente ;-) come posso: Il for costrutto in un linguaggio simile al c non riguarda il conteggio.

L'espressione di test che determina se continuare può essere qualsiasi cosa sia rilevante per lo scopo del loop; l'espressione di aggiornamento non deve essere " aggiungine una a un contatore " ;.

for (int i = 0, j = 0; i < maxi && j < maxj && i != 4 && j != 3;) {
    if (j < maxj) {
        ++j;
    } else {
        j = 0;
        ++i;
    }
}

sarebbe un modo (abbastanza arbitrario) di riscrivere.

Il punto è che se stabilire una condizione è il punto di un'interazione, di solito è possibile scrivere un ciclo (usando while o <=>) in un modo che dichiari la condizione di continuazione / fine in modo più esplicito.

(Se potessi pubblicare una descrizione di ciò che sta realmente accadendo, probabilmente scriverebbe qualcosa che non sembra arbitrario come sopra.)

Non puoi saltare da due loop con una singola istruzione break, ma puoi usare un goto per saltare dal loop interno proprio all'esterno.

Se il goto è localizzato e significa che c'è meno logica di quanto altrimenti penso sia un codice perfettamente accettabile. Avere variabili flag aggiuntive o sollevare la variabile iteratore dal ciclo interno in modo da poterlo confrontare nel ciclo esterno non semplifica la comprensione del codice IMHO.

Da tutti i suggerimenti di cui sopra eviterei di usare il meccanismo try / catch perché le eccezioni dovrebbero essere riservate a circostanze eccezionali e non al normale flusso di controllo.

L'uso di due interruzioni va bene se puoi creare la seconda condizione in modo appropriato. Anche usare un booleano a questo scopo sarebbe buono e potresti persino collegarlo alla condizione di ciascuno per il ciclo. Ad esempio:

bool exit_loops = false;
for (int a = 0; a < A && !exit_loops; ++a)
{
    for (int b = 0; b < B && !exit_loops; ++b)
    {
        if (some_condition) exit_loops = true;
    }
}

Anche se stai usando più di due loop, potrebbe essere più appropriato avvolgerli in una funzione e usare semplicemente return per uscire dalla funzione (e anche tutti i loop). Quindi, potresti rifattorizzare il codice in modo da eliminare tutti i loop tranne uno, sia chiamando una funzione per eseguire il codice di loop interno, ecc.

Infine, non aver paura di usare un goto in questa circostanza, di solito i goto sono una cattiva programmazione non strutturata, ma in alcuni casi (come questo) sono molto utili.

bool done = false;

for (int i = 0; i < maxi && !done; ++i)
    for (int j = 0; j < maxj && !done; ++j)
        if (i == 4 && i < maxi && j == 3 && j < maxj )
             done = true;
        else {
        }

O potresti semplicemente andare. O no :-)

Non puoi saltare così in C / C ++:

for (...)
{
  for (...)
  {
    // from here...
  }
}
// ...to here

senza l'uso di goto. Hai bisogno di un costrutto come:

for (...)
{
  bool
    exit = false;

  for (...)
  {
    if (do_exit)
    {
      exit = true; // or set outer loop counter to end value
      break;
    }
  }
  if (exit)
  {
    break;
  }
}

In alternativa, usa il tiro e cattura - ma non è eccezionale dato che il tiro dovrebbe davvero essere usato per eccezioni e non per il controllo del flusso.

Un modo chiaro è quello di rendere una funzione il ciclo interno:

bool F ()
{
  if inner loop terminates, return false else return true
}

void G ()
{
  for (...)
  {
    if (!F ())
    {
      break;
    }
  }
}
for (int i = 0; i < maxi; ++i)
{
    int j = 0;
    for (j = 0; j < maxj; ++j)
    {
         if (i == 4 && j == 3) // i < maxi and j < maxj otherwise we would not be here
             break; // exit inner loop
    }
    if (i == 4 && j == 3) // i < maxi and j < maxj otherwise we would not be here
        break; // exit outer loop
}

Potresti usare una vai a dichiarazione , ma è generalmente considerata una cattiva pratica.

L'altra opzione è fare qualcosa del genere

int i;
int j = 0;
for (i = 0; i < maxi && !(i==4 && j==3); ++i)
    for (j = 0; j < maxj && !(i==4 && j==3); ++j)

Leggere il codice non dovrebbe essere come la lettura di un fumetto poliziesco(che devono sempre essere capito)...

un esempio:

Java:

iterate_rows:
for (int i = 0; i < maxi; ++i)
{       
    for (int j = 0; j < maxj; ++j)
    {
        if (i == 4 < maxi && j == 3 < maxj) 
            break iterate_rows;
        else
            continue iterate_rows;
    }   
}

Non c'è bisogno di capire a che rompere iterate_rows fare, basta leggerlo.

C++:

//iterate_rows:
for (int i = 0; i < maxi; ++i)
{
    for (int j = 0; j < maxj; ++j)
    {
        if (i == 4 < maxi && j == 3 < maxj) 
            goto break_iterate_rows;
        else
            goto continue_iterate_rows;
    }

continue_iterate_rows:;
}
break_iterate_rows:;

goto break_iterate_rows è solo un visibile la versione di pausa iterate_rows

Se si confinare il vostro uso di goto e le etichette su questo tipo di codice, non sarà capire l'intento.Limitando l'utilizzo di goto e le etichette su questo tipo di codice vi farà basta leggere il codice, non l'analisi o capire.Potrai non essere accusato di essere un male programmatore.

E se davvero limitare il goto in questo tipo di codice, sarete in grado di sviluppare l'abitudine di non aver bisogno di capire a che quelli maledettamente goto fare nel tuo codice.Vantaggio è che non è necessario introdurre booleane e tenere traccia di loro(che, imho, si porta a rilevare il codice, rendendo un po ' illeggibile, che sconfigge lo scopo di evitare goto)

P. S.

Coppia di quelle etichette con i commenti(prima del ciclo), con il tempo di leggere al di là di queste linee con istruzione goto si conosce già l'intento di chi goto

Ho alcuni suggerimenti:

  1. lancio .... inserisci i due loop all'interno di un " prova {} " e quindi " cattura " il " lancio " a condizione.

  2. inserisce i due loop in un metodo e restituisce la condizione.

  3. Goto non è malvagio, è anche l'uso che la gente mette anche tu .... Puoi usare " vai a " può essere il codice più chiaro, soprattutto durante la gestione degli errori. Non ne uso uno da 20 anni.

Tony

Ho sempre cercato di stare alla larga dalle goto dichiarazioni (per qualche motivo veniva sempre guardato male a scuola e al mio lavoro). Vorrei usare qualcosa di simile a quello che Daemin ha suggerito.

Puoi usare etichette, qualcosa del tipo:

Outer:
for(...)
{
    Inner:
    for(...)
    {
    if(condition)
        {
        goto End;
        }
    }
}
End:

In Java puoi passare etichette per rompere, penso?

Modifica: il goto è stato modificato in End anziché in Esterno, tuttavia non credo che il rappresentante negativo sia giustificato. Questa risposta offre il modo più semplice per farlo.

Un altro motivo per riconsiderare interamente il costrutto for è che il suo ambito impedisce l'accesso alle variabili controllate dopo che il ciclo è terminato. Il valore (i) della (e) variabile (e) mutata (i) nel ciclo può essere utile per una varietà di ragioni (ad esempio per distinguere il successo dal fallimento in una ricerca) che altrimenti richiederebbero variabili extra per conservare tali informazioni dopo l'uscita dall'ambito. Ecco un piccolo esempio che cerca in un array quadrato chiamato a un valore target (supponendo che SIZE sia diverso da zero, altrimenti non è necessaria alcuna ricerca!):

int i = 0;
int j = 0;
while (i < SIZE && a[i][j] != target) { // still in array but not at target
    if (SIZE <= ++j) {                  // fallen off the end of a row
        j = 0;
        ++i;
    }
}

Il codice successivo può utilizzare i < SIZE per determinare se è stato individuato il valore desiderato.

Un altro vantaggio di quanto sopra è la flessibilità. Supponiamo che ora siamo informati che i valori nelle righe di <=> sono in ordine crescente, quindi il resto di una riga è irrilevante se viene rilevato un valore maggiore di <=>. È facile sapere esattamente quale modifica apportare e dove effettuarla. Poiché queste nuove informazioni ci consentono di abbandonare la riga corrente, solo la decisione interiore è influenzata, diventando:

    if (target < a[i][j] || SIZE <= ++j) { // can't be in row or fallen off end
    ...

Sto vedendo più linguaggi più recenti (specialmente quelli orientati al funzionamento) che abbandonano il vecchio " contando " costrutto loop; questa è probabilmente una buona cosa, poiché ci incoraggia a pensare al significato del ciclo, piuttosto che semplicemente contare.

Il modo migliore che ho visto coinvolge macro e goto, ma in realtà è abbastanza bello (il link al post inizia parlando di Perl, ma l'ultimo paragrafo circa introduce le macro).

Ti permette di scrivere codice come:

named (LOOPS) for (i=1; i<10; i++) {
    for (j=1; j<10; j++) {
        for (j=1; j<10; j++) {
            /* Process data[i][j][k] here */
            if (data[i][j][k] < threshold) break(LOOPS);
        }
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top