Comment sortir des boucles imbriquées?


96

Si j'utilise une breakinstruction, cela ne cassera que la boucle interne et je dois utiliser un indicateur pour rompre la boucle externe. Mais s'il y a de nombreuses boucles imbriquées, le code n'aura pas l'air bien.

Existe-t-il un autre moyen de rompre toutes les boucles? (Veuillez ne pas utiliser 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
vous pouvez essayer int i et int j avant le début de la boucle, puis à la condition de les rendre 1001, la boucle n'itérera pas la suivante.
Khurram Ijaz

Réponses:


43

Utilisation:

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

49
Fonctionne, mais laid et pas général. Que faire si quelqu'un change la limite à 2000 (supposons que le code soit plus long, donc vous ne le remarquez pas immédiatement)?
ugoren

1
@ugoren C'est aussi si simple alors. Et si vous utilisiez un const int count =1000 , dans l'initialisation globale. ou sous forme de #definemacro.
Laksith

4
Comme le souligne @ugoren, ce n'est pas une solution générale. Comme il s'agit du premier hit Google pour cette question, ce serait bien si la solution générale avait été sélectionnée. De toute façon, les gens ont l'habitude de vérifier le n ° 2.
BeeOnRope

1
Je suppose que j'ai seulement besoin de i = 1000?
Peter Wu

189

Non, ne gâchez pas le plaisir avec un break. Ceci est la dernière utilisation valide restante de goto;)

Sinon, vous pouvez utiliser des indicateurs pour sortir des boucles imbriquées profondes.

Une autre approche pour sortir d'une boucle imbriquée consiste à factoriser les deux boucles dans une fonction distincte et à revenir de cette fonction lorsque vous souhaitez quitter.

Résumé - pour sortir des boucles imbriquées:

  1. utilisation goto
  2. utiliser des drapeaux
  3. factoriser les boucles en appels de fonction séparés

Je n'ai pas pu résister à l'inclusion de xkcd ici :)

entrez la description de l'image ici

la source

Les Goto sont considérés comme nuisibles, mais comme de nombreuses personnes dans les commentaires le suggèrent S'il est utilisé judicieusement, il peut être un excellent outil. Tout ce qui est utilisé avec modération est amusant.


29
Goto est à peu près aussi clair que vous y arriverez, ouais. Définir la variable de sortie sur 1000 est encore plus compliqué.
correnos

3
Je voudrais ajouter que les gotos ne sont pas explicitement mauvais, ils peuvent juste être utilisés pour le mal. Je trouve qu'il y a pas mal de cas, par exemple celui-ci, où ils sont la meilleure solution. «N'utilisez pas de gotos» est un bon début, mais je pense que la prochaine étape de la compétence vous permet de «ne pas utiliser de gotos à longue portée».
Aatch

1
Je ne suis pas d'accord avec ceci: "La création d'une fonction entraîne des quantités exponentielles d'ajout / de soustraction du pointeur de pile". S'il y a une fonction locale (statique) qui est appelée à un seul point dans le déroulement du programme, tout compilateur à moitié décent la mettra en ligne, et le code résultant est essentiellement le même que celui de goto. C'est probablement le cas d'optimisation le plus simple pour n'importe quel compilateur.
DrV

1
La refactorisation est généralement la solution la plus propre. Cependant, si des variables hors boucle sont modifiées pendant la boucle interne, les choses se compliquent. Une possibilité est de passer la variable à la fonction interne par référence (pointeur), mais cela peut perturber l'optimisation du compilateur et produire du code supplémentaire inutile. Une autre possibilité est de rendre ces variables statiques au niveau du module, mais ce n'est pas très beau non plus. Il manque malheureusement des fonctions imbriquées à C, car elles résoudraient ce problème - à moins que vous ne souhaitiez vous engager à utiliser gcc qui fournit une extension.
DrV

1
+1. Et la programmation structurée de Donald E. Knuth avec go to Statements ( wiki.c2.com/?StructuredProgrammingWithGoToStatements ) est un article intéressant pour équilibrer celui de 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 solution incrémente toujours les deux variables de une en cas de pause, ce qui peut causer des problèmes
TheSola10

7
On peut définir "stop = true;" puis "break;". Ensuite, juste après la fin de la boucle intérieure "for", faites "if (stop) break;".
Jeff Grigg

34

Une façon est de mettre toutes les boucles imbriquées dans une fonction et de revenir de la boucle la plus interne en cas de besoin de sortir de toutes les boucles.

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

1
semble la meilleure solution pour moi
Luca Steeb

20

Je pense que ça gotorésoudra le problème

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

end:
stmt2 

@chikuba J'ai eu une réponse de cprogramming.com/tutorial/goto.html et votre réponse n'est pas publiée quand je fais la même chose, c'est pourquoi je ne vois pas votre message
Renjith KN

15

Vous aurez besoin d'une variable booléenne, si vous voulez qu'elle soit lisible:

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;
}

Si vous le souhaitez moins lisible, vous pouvez rejoindre l'évaluation booléenne:

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

Comme moyen ultime, vous pouvez invalider la boucle initiale:

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


4

Attention: cette réponse montre une construction vraiment obscure.

Si vous utilisez GCC, consultez cette bibliothèque . Comme en PHP, breakpeut accepter le nombre de boucles imbriquées que vous souhaitez quitter. Vous pouvez écrire quelque chose comme ceci:

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

Et sous le capot, il utilisegoto effectivement :)
jacobq

@ iX3 Je peux utiliser l'assembleur en ligne et l'instruction jmp si cela aide.
DaBler

@DaBler, je ne savais pas que vous étiez l'auteur de cette bibliothèque. Mon commentaire ne se voulait pas un retour d'information mais plutôt que cette bibliothèque utilise la même méthode que la réponse acceptée . J'espère que votre commentaire a été conçu comme une blague car je pense que l'utilisation d'une fonctionnalité de langage (même goto) est de loin préférable à asm en ligne (spécifique à la machine, plus facile à faire une erreur, plus difficile à lire, ...).
jacobq

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

end:

3

Si vous avez besoin des valeurs de i et j, cela devrait fonctionner mais avec moins de performances que les autres

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

Notez que si la condition dépend de, jla valeur de la condition doit être stockée d'une manière ou d'une autre pour que cela fonctionne toujours.
SuperBeasedMan

1
Vous avez raison, mais après une pause , la valeur de j ne change pas, tout comme la valeur de la condition.
Ali Eren Çelik

C'est une solution cassée et non valable en général. Soit j n'est pas défini en dehors de sa boucle, soit 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; ... }il sort toujours de la boucle externe après la première itération de la boucle interne.
Chai T. Rex le

-3
int i = 0, j= 0;

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

Va casser les deux boucles.


-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;
}

Donc, fondamentalement, vous dites qu'il devrait (1) faire un tas d'appels de fonctions supplémentaires, puis (2) tourner le reste du temps quand il conditiondevient faux. Oh, et la deuxième boucle fonctionnera pour toujours parce qu'elle incrémente iau lieu de j, oups ...
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);
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.