Come rompere i loop annidati?


96

Se uso breakun'istruzione, interromperà solo il ciclo interno e ho bisogno di usare un flag per interrompere il ciclo esterno. Ma se ci sono molti cicli annidati, il codice non avrà un bell'aspetto.

C'è un altro modo per interrompere tutti i loop? (Per favore, non usare goto stmt.)

for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; j++) {
       if(condition) {
            // both of the loops need to break and control will go to stmt2
       }
   }

}

stmt2

2
si può provare int i e int j prima che il ciclo inizi e poi a condizione di renderli 1001 il ciclo non itererà il successivo.
Khurram Ijaz

Risposte:


43

Uso:

if (condition) {
    i = j = 1000;
    break;
}

49
Funziona, ma brutto e non generale. E se qualcuno cambia il limite a 2000 (supponiamo che il codice sia più lungo, quindi non te ne accorgi immediatamente)?
ugoren

1
@ugoren È anche così semplice allora. cosa succede se si utilizza a const int count =1000 , nell'inizializzazione globale. o come #definemacro.
Laksith

4
Come sottolinea @ugoren, non è una soluzione generale. Poiché questo è il primo successo di Google per questa domanda, sarebbe bello se fosse stata selezionata la soluzione generale. Beh, le persone sono abituate a controllare comunque il n. 2.
BeeOnRope

1
Immagino che sia necessario solo i = 1000?
Peter Wu

189

No, non rovinare il divertimento con un file break. Questo è l'ultimo uso valido rimanente di goto;)

In caso contrario, è possibile utilizzare i flag per rompere i loop nidificati profondi.

Un altro approccio per interrompere un ciclo annidato consiste nel fattorizzare entrambi i cicli in una funzione separata e tornare da quella funzione quando si desidera uscire.

Riepilogo: per rompere i loop annidati:

  1. uso goto
  2. usa le bandiere
  3. scomporre i cicli in chiamate di funzioni separate

Non ho potuto resistere a includere xkcd qui :)

inserisci qui la descrizione dell'immagine

fonte

I Goto sono considerati dannosi ma come molte persone nei commenti suggeriscono che non è necessario. Se usato con giudizio può essere un ottimo strumento. Qualsiasi cosa usata con moderazione è divertente.


29
Goto è chiaro come arriverai qui, sì. L'impostazione della variabile di uscita su 1000 è ancora più complicata.
correnos

3
Vorrei aggiungere che i gotos non sono esplicitamente malvagi, possono essere usati solo per il male. Trovo che ci siano parecchi casi, ad esempio questo, in cui sono la soluzione migliore. "Non usare gotos" è un buon inizio, ma penso che il passaggio successivo nell'abilità ti consenta di "Non usare gotos a lungo raggio".
Aatch

1
Vorrei non essere d'accordo con questo: "La creazione di una funzione risulta in quantità esponenziali di addizione / sottrazione del puntatore allo stack". Se esiste una funzione locale (statica) che viene chiamata solo in un punto del flusso del programma, qualsiasi compilatore semi-decente la inserirà in linea e il codice risultante è essenzialmente lo stesso di goto. Questo è forse il caso di ottimizzazione più semplice per qualsiasi compilatore.
DrV

1
Il refactoring è solitamente la soluzione più pulita. Tuttavia, se durante il ciclo interno vengono modificate variabili esterne al ciclo, le cose si complicano. Una possibilità è passare la variabile alla funzione interna tramite riferimento (puntatore), ma ciò potrebbe confondere l'ottimizzazione del compilatore e produrre codice aggiuntivo non necessario. Un'altra possibilità è rendere statiche tali variabili a livello di modulo, ma neanche questo è molto bello. Sfortunatamente al C mancano le funzioni annidate, poiché risolverebbero questo problema, a meno che tu non sia disposto a vincolarti all'uso di gcc che fornisce un'estensione.
DrV

1
+1. E la programmazione strutturata di Donald E. Knuth con go to Statements ( wiki.c2.com/?StructuredProgrammingWithGoToStatements ) è un articolo interessante per bilanciare Dijkstra.
kmkaplan

40
bool stop = false;
for (int i = 0; (i < 1000) && !stop; i++)
{
    for (int j = 0; (j < 1000) && !stop; j++)
    {
        if (condition)
            stop = true;
    }
}

La soluzione aumenta ancora entrambe le variabili di una in pausa, il che può causare problemi
TheSola10

7
Si può impostare "stop = true;" e poi "break;". Quindi, subito dopo la fine del ciclo interno "for", fai "if (stop) break;".
Jeff Grigg

34

Un modo è mettere tutti i loop annidati in una funzione e tornare dal loop più interno in caso di necessità di rompere tutti i loop.

function() 
{    
  for(int i=0; i<1000; i++)
  {
   for(int j=0; j<1000;j++)
   {
      if (condition)
        return;
   }
  }    
}

1
sembra la soluzione migliore per me
Luca Steeb

20

Penso gotoche risolverà il problema

for(int i = 0; i < 1000; i++) {
    for(int j = 0; j < 1000; i++) {
        if (condition) {
            goto end;
        }
    }
}

end:
stmt2 

@chikuba ho ricevuto risposta da cprogramming.com/tutorial/goto.html e la tua risposta non viene pubblicata quando sto facendo la stessa cosa, ecco perché non vedo il tuo post
Renjith KN

15

Avrai bisogno di una variabile booleana, se vuoi che sia leggibile:

bool broke = false;
for(int i = 0; i < 1000; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      broke = true;
      break;
    }
  }
  if (broke)
    break;
}

Se vuoi che sia meno leggibile puoi unirti alla valutazione booleana:

bool broke = false;
for(int i = 0; i < 1000 && !broke; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      broke = true;
      break;
    }
  }
}

Come modo definitivo puoi invalidare il ciclo iniziale:

for(int i = 0; i < size; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      i = size;
      break;
    }
  }
}


4

Attenzione: questa risposta mostra una costruzione davvero oscura.

Se stai usando GCC, controlla questa libreria . Come in PHP, breakpuò accettare il numero di cicli annidati da cui si desidera uscire. Puoi scrivere qualcosa del genere:

for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; j++) {
       if(condition) {
            // break two nested enclosing loops
            break(2);
       }
   }
}

E sotto il cofano sta effettivamente usandogoto :)
jacobq

@ iX3 Posso usare l'assemblatore in linea e le istruzioni jmp se questo aiuta.
DaBler

@ DaBler, non sapevo che tu fossi l'autore di quella libreria. Il mio commento non è stato inteso come feedback, ma piuttosto notare che questa libreria utilizza lo stesso metodo della risposta accettata . Si spera che il tuo commento sia stato inteso come uno scherzo perché penso che l'uso di una funzionalità del linguaggio (anche goto) sia di gran lunga preferibile inline asm (specifico della macchina, più facile commettere un errore, più difficile da leggere, ...).
jacobq

3
for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; i++) {
       if(condition) {
            goto end;
   }
} 

end:

3

Se hai bisogno dei valori di i e j, dovrebbe funzionare ma con prestazioni inferiori rispetto ad altri

for(i;i< 1000; i++){    
    for(j; j< 1000; j++){
        if(condition)
            break;
    }
    if(condition) //the same condition
        break;
}

Si noti che se la condizione dipende da, jil valore della condizione deve essere memorizzato in qualche modo affinché funzioni ancora.
SuperBeddingMan

1
Hai ragione, ma dopo l' interruzione , il valore di j non cambia e così è il valore della condizione.
Ali Eren Çelik

Questa è una soluzione non funzionante e non valida in generale. O j non è definito fuori del suo ciclo o for (int i = 0; i < 1000; i++) { for (int j = 0; j < 1000; j++) { if (workComplete[i][j]) break; /* do work */ workComplete[i][j] = true; } if (workComplete[i][j]) break; ... }sta per sempre uscire del ciclo esterno dopo la prima iterazione del ciclo interno.
Chai T. Rex

-3
int i = 0, j= 0;

for(i;i< 1000; i++){    
    for(j; j< 1000; j++){
        if(condition){
            i = j = 1001;
            break;
        }
    }
}

Spezzerà entrambi gli anelli.


-3
for(int i = 0; i < 1000; i++) {
    for(int j = 0; j < 1000; i++) {
       if(condition) {
          func(para1, para2...);
          return;
       }
    }
}

func(para1, para2...) {
    stmt2;
}

Quindi in pratica stai dicendo che dovrebbe (1) fare un mucchio di chiamate di funzioni extra e poi (2) girare per il resto del tempo quando conditiondiventa falso. Oh, e il secondo ciclo verrà eseguito per sempre perché aumenta iinvece di j, whoops ...
jacobq

-4
i = 0;

do
{
  for (int j = 0; j < 1000; j++) // by the way, your code uses i++ here!
  {
     if (condition)
     {
       break;
     }
  }

  ++i;

} while ((i < 1000) && !condition);
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.