Puis-je utiliser break pour quitter plusieurs boucles imbriquées 'for'?

304

Est-il possible d'utiliser la breakfonction pour quitter plusieurs forboucles imbriquées ?

Si oui, comment feriez-vous cela? Pouvez-vous également contrôler le nombre de boucles des breaksorties?

Faken
la source
1
Au lieu d'utiliser break ou goto pour quitter plusieurs boucles imbriquées, vous pouvez inclure cette logique particulière dans une fonction et utiliser return pour quitter plusieurs boucles imbriquées. Cela préservera l'esthétique de votre code et vous empêchera d'utiliser goto, ce qui est une mauvaise pratique de programmation.
Rishab Shinghal

Réponses:

239

AFAIK, C ++ ne prend pas en charge les boucles de dénomination, comme Java et d'autres langages. Vous pouvez utiliser un goto ou créer une valeur d'indicateur que vous utilisez. À la fin de chaque boucle, vérifiez la valeur du drapeau. S'il est défini sur true, vous pouvez sortir de cette itération.

Cullen Walsh
la source
317
N'ayez pas peur d'utiliser un gotosi c'est la meilleure option.
jkeys
18
Je suis un nouveau programmeur C ++ (et un sans aucune formation en programmation formelle) donc après avoir lu sur les diatribes des gens sur goto. J'hésite à l'utiliser, de peur que mon programme n'explose et ne me tue. En dehors de cela, lorsque j'écrivais des programmes sur mon ti-83 (dans une classe de mathématiques ennuyeuse bien sûr), les fonctions fournies par l'éditeur de base nécessitaient l'utilisation de goto.
Faken
26
@Faken: Deux types de programmeurs utilisent goto: les mauvais programmeurs et les programmeurs pragmatiques. Les premiers sont explicites. Ce dernier, dans lequel vous vous situeriez si vous choisissez de bien les utiliser, utilise un concept dit "maléfique" lorsqu'il s'agit du moindre de (deux) maux. Lisez ceci pour une meilleure compréhension de certains concepts C ++ que vous devrez peut-être utiliser de temps en temps (macros, goto, préprocesseur, tableaux): parashift.com/c++-faq-lite/big-picture.html#faq-6.15
jkeys
41
@Faken: Il n'y a rien de mal à utiliser goto. C'est abuser d' un goto qui est gênant.
Tout
28
@Hooked: C'est vrai, sauf que l'utilisation est gotorarement la meilleure option. Pourquoi ne pas mettre les boucles dans leur propre fonction ( inline, si vous êtes préoccupé par la vitesse) et à returnpartir de là?
sbi
265

Non, ne le gâte pas avec un break. Il s'agit du dernier bastion restant à l'usage de goto.

Henk Holterman
la source
19
La norme de codage MISRA C ++ permet l'utilisation de goto pour couvrir ce type de situation exacte.
Richard Corden
11
Les vrais programmeurs n'ont pas peur d'utiliser goto. Cela a été fait pendant 25 ans - aucun regret - a économisé une tonne de temps de développement.
Doug Null
1
Je suis d'accord. les gotos sont indispensables si vous n'avez pas 8K pixels de diamètre et devez appeler une séquence de 30 appels Windows OS.
Michaël Roy
Ou un simple "retour" en mettant le code dans une fonction.
Tara
68

Juste pour ajouter une réponse explicite en utilisant lambdas:

  for (int i = 0; i < n1; ++i) {
    [&] {
      for (int j = 0; j < n2; ++j) {
        for (int k = 0; k < n3; ++k) {
          return; // yay we're breaking out of 2 loops here
        }
      }
    }();
  }

Bien sûr, ce modèle a certaines limites et évidemment C ++ 11 uniquement, mais je pense qu'il est très utile.

Predelnik
la source
7
Cela pourrait amener le lecteur à penser que le retour provoque le retour de la fonction dans laquelle se trouve le lambda, et non le lambda lui-même.
Xunie
3
@Xunie: Si votre boucle est si compliquée, que vous oubliez que vous êtes dans un lambda, il est temps de les mettre dans une fonction différente, mais pour les cas simples, cela devrait fonctionner assez bien.
MikeMB
17
Je pense que cette solution est belle
tuket
Vous pouvez capturer le lambda dans un modèle RIIA qui lui donne un nom et l'appelle dans dtor (aka scope gaurd). Cela n'ajouterait aucun gain de performances, mais peut améliorer la lisibilité et réduire le risque de manquer la parenthèse d'appel de fonction.
Red.Wave
56

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

Bien sûr, cela soulève l'autre argument de savoir si vous devez jamais explicitement à returnpartir d'une fonction ailleurs qu'à la fin.

Greg Hewgill
la source
7
C'est un problème C. Avec RIAA, le retour anticipé n'est pas un problème car tous les problèmes associés au retour précoce sont correctement traités.
Martin York
4
Je comprends qu'une bonne application de RIAA peut résoudre le problème de nettoyage des ressources en C ++, mais j'ai vu l'argument philosophique contre le retour précoce se poursuivre dans d'autres environnements et langages. Un système sur lequel j'ai travaillé où la norme de codage interdisait le retour rapide avait des fonctions jonchées de variables booléennes (avec des noms comme continue_processing) qui contrôlaient l'exécution de blocs de code plus bas dans la fonction.
Greg Hewgill
21
Qu'est-ce que RIAA? Est-ce que ça ressemble à RAII? = D
jkeys
1
Cela dépend du nombre de boucles qu'il a et de la profondeur du nid ... voulez-vous la pilule bleue ou la pilule rouge?
Matt
34

Pause ne quittera que la boucle la plus intérieure qui le contient.

Vous pouvez utiliser goto pour sortir de n'importe quel nombre de boucles.

Bien sûr, goto est souvent considéré comme nuisible .

est-il approprié d'utiliser la fonction break [...]?

L'utilisation de break and goto peut rendre plus difficile de raisonner sur l'exactitude d'un programme. Voir ici pour une discussion à ce sujet: Dijkstra n'était pas fou .

Karl Voigtland
la source
16
Une bonne réponse en ce sens qu'elle explique que le mème "goto is nocif" est fortement lié à la déclaration plus générale "l'interruption du flux de contrôle est nuisible". Il est insensé de dire "goto est nocif", puis de faire demi-tour et de recommander d'utiliser breakou return.
Pavel Minaev,
6
@Pavel: breaket vous returnavez l'avantage de gotone pas avoir à chercher une étiquette pour trouver où ils vont. Oui, en dessous, ils sont en quelque sorte goto, mais très restreints. Ils sont beaucoup plus faciles à déchiffrer par le cerveau d'appariement de motifs d'un programmeur que par le libre goto. Donc, l'OMI, ils sont préférables.
sbi
@sbi: C'est vrai, mais break ne fait toujours pas partie de la programmation structurée. C'est juste mieux toléré qu'un goto.
jkeys
2
@KarlVoigtland le lien Dijkstra est obsolète; cela semble fonctionner: blog.plover.com/2009/07
Aaron Brager
3
Il n'y a absolument rien de mal à utiliser goto dans cette situation. Un goto bien placé est meilleur et plus lisible que la plupart des solutions tordues proposées par ailleurs.
James
22

Bien que cette réponse ait déjà été présentée, je pense qu'une bonne approche consiste à procéder comme suit:

for(unsigned int z = 0; z < z_max; z++)
{
    bool gotoMainLoop = false;
    for(unsigned int y = 0; y < y_max && !gotoMainLoop; y++)
    {
        for(unsigned int x = 0; x < x_max && !gotoMainLoop; x++)
        {
                          //do your stuff
                          if(condition)
                            gotoMainLoop = true;
        }
    }

}
scigor
la source
5
ce qui est bon mais toujours pas si lisible, je préfère goto dans ce cas
Петър Петров
2
cela rend votre code "assez" lent car il gotoMainLoopest vérifié à chaque cycle
Thomas
1
Dans ce cas, l'utilisation du réel gotorend le noyau plus lisible et plus performant.
Петър Петров
20

Que dis-tu de ça?

for(unsigned int i=0; i < 50; i++)
{
    for(unsigned int j=0; j < 50; j++)
    {
        for(unsigned int k=0; k < 50; k++)
        {
            //Some statement
            if (condition)
            {
                j=50;
                k=50;
            }
        }
    }
}
jebeaudet
la source
2
approche intéressante mais j'aime vraiment la façon dont ered @ inf.ig.sh le gère. for (unsigned int y = 0; y <y_max &&! gotoMainLoop; y ++).
Andy
15

Un exemple de code utilisant gotoet une étiquette pour sortir d'une boucle imbriquée:

for (;;)
  for (;;)
    goto theEnd;
theEnd:
Helio Santos
la source
11

Une bonne façon de sortir de plusieurs boucles imbriquées est de refactoriser votre code en fonction:

void foo()
{
    for(unsigned int i=0; i < 50; i++)
    {
        for(unsigned int j=0; j < 50; j++)
        {
            for(unsigned int k=0; k < 50; k++)
            {
                // If condition is true
                return;
            }
        }
    }
}
Deqing
la source
4
... ce qui n'est pas une option si nous devons passer 10-20 variables pour la pile encadrant cette fonction.
Петър Петров
1
@ ПетърПетров alors optez pour un lambda qui est aussi meilleur car vous pouvez le définir exactement où vous en avez besoin.
DarioP
+1 pour les lambdas mais refonte du noyau du moteur de jeu où même un cadre de pile est toujours un goulot d'étranglement. Désolé de le dire, mais les lambdas ne sont pas si légers au moins dans MSVC 2010.
Петър Петров
@ ПетърПетров Ensuite, changez la paire de fonctions en classe et les variables de pile en membres privés.
Arthur Tacca
Cela ne fait que compliquer le code déjà complexe :) Parfois, goto est la seule solution. Ou si vous écrivez des automates complexes avec la documentation "goto state X", alors goto fait lire le code tel qu'il est écrit dans le document. En outre, C # et go all ont tous deux un but: aucun langage n'est complet sans un goto, et les gotos sont souvent les meilleurs outils utilisés pour écrire un traducteur intermédiaire ou du code de type assembleur.
Петър Петров
5

goto peut être très utile pour casser des boucles imbriquées

for (i = 0; i < 1000; i++) {
    for (j = 0; j < 1000; j++) {
        for (k = 0; k < 1000; k++) {
             for (l = 0; l < 1000; l++){
                ....
                if (condition)
                    goto break_me_here;
                ....
            }
        }
    }
}

break_me_here:
// Statements to be executed after code breaks at if condition
Azeemali Hashmani
la source
3

La breakdéclaration met fin à l'exécution du englobante le plus proche do, for, switchou whiledéclaration dans laquelle il apparaît. Le contrôle passe à l'instruction qui suit l'instruction terminée.

de msdn .

Rob
la source
3

Je pense qu'un gotoest valable dans cette circonstance:

Pour simuler un break/ continue, vous souhaitez:

Pause

for ( ;  ;  ) {
    for ( ;  ;  ) {
        /*Code here*/
        if (condition) {
            goto theEnd;
        }
    }
}
theEnd:

Continuer

for ( ;  ; ) {
    for ( ;  ;  ) {
        /*Code here*/
        if (condition) {
            i++;
            goto multiCont;
        }
    }
    multiCont:
}
JuliusAlphonso
la source
"Continuer" ne fonctionnera pas là-bas, car l' expression d'itération de la première boucle ne sera pas exécutée
Fabio A.
Je suppose que l'itérateur de la première boucle est i. D'où i++avant le goto
JuliusAlphonso
0

D'autres langages tels que PHP acceptent un paramètre pour break (c'est-à-dire break 2;) pour spécifier la quantité de niveaux de boucles imbriquées dont vous voulez sortir, mais C ++ ne le fait pas. Vous devrez le résoudre en utilisant un booléen que vous définissez sur false avant la boucle, défini sur true dans la boucle si vous voulez rompre, plus une pause conditionnelle après la boucle imbriquée, vérifiant si le booléen a été défini sur true et casse si oui.

Patrick Glandien
la source
0

Je sais que c'est un ancien poste. Mais je proposerais une réponse un peu logique et plus simple.

for(unsigned int i=0; i < 50; i++)
    {
        for(unsigned int j=0; j < conditionj; j++)
        {
            for(unsigned int k=0; k< conditionk ; k++)
            {
                // If condition is true

                j= conditionj;
               break;
            }
        }
    }
Aditya Jagtap
la source
3
Ce n'est pas une solution très évolutive car j = conditionjcela ne fonctionnera pas si vous avez un prédicat complexe à la place de j < conditionj.
Sergey
0

Brisez n'importe quel nombre de boucles par une seule boolvariable, voir ci-dessous:

bool check = true;

for (unsigned int i = 0; i < 50; i++)
{
    for (unsigned int j = 0; j < 50; j++)
    {
        for (unsigned int k = 0; k < 50; k++)
        {
            //Some statement
            if (condition)
            {
                check = false;
                break;
            }
        }
        if (!check)
        {
            break;
        }
    }
    if (!check)
    {
        break;
    }
}

Dans ce code, nous avons break;toutes les boucles.

vikas bansal
la source
0

Je ne sais pas si cela en vaut la peine, mais vous pouvez émuler les boucles nommées de Java avec quelques macros simples:

#define LOOP_NAME(name) \
    if ([[maybe_unused]] constexpr bool _namedloop_InvalidBreakOrContinue = false) \
    { \
        [[maybe_unused]] CAT(_namedloop_break_,name): break; \
        [[maybe_unused]] CAT(_namedloop_continue_,name): continue; \
    } \
    else

#define BREAK(name) goto CAT(_namedloop_break_,name)
#define CONTINUE(name) goto CAT(_namedloop_continue_,name)

#define CAT(x,y) CAT_(x,y)
#define CAT_(x,y) x##y

Exemple d'utilisation:

#include <iostream>

int main()
{
    // Prints:
    // 0 0
    // 0 1
    // 0 2
    // 1 0
    // 1 1

    for (int i = 0; i < 3; i++) LOOP_NAME(foo)
    {
        for (int j = 0; j < 3; j++)
        {
            std::cout << i << ' ' << j << '\n';
            if (i == 1 && j == 1)
                BREAK(foo);
        }
    }
}

Un autre exemple:

#include <iostream>

int main()
{
    // Prints: 
    // 0
    // 1
    // 0
    // 1
    // 0
    // 1

    int count = 3;
    do LOOP_NAME(foo)
    {
        for (int j = 0; j < 3; j++)
        {
            std::cout << ' ' << j << '\n';
            if (j == 1)
                CONTINUE(foo);
        }
    }
    while(count-- > 1);
}
HolyBlackCat
la source
-1
  while (i<n) {
    bool shouldBreakOuter = false;
    for (int j=i + 1; j<n; ++j) {
      if (someCondition) {
          shouldBreakOuter = true;
      }
    }

    if (shouldBreakOuter == true)
      break;

  }
MEnnabah
la source
-3

Vous pouvez utiliser try ... catch.

try {
    for(int i=0; i<10; ++i) {
        for(int j=0; j<10; ++j) {
            if(i*j == 42)
                throw 0; // this is something like "break 2"
        }
    }
}
catch(int e) {} // just do nothing
// just continue with other code

Si vous devez sortir de plusieurs boucles à la fois, c'est souvent une exception de toute façon.

lawilog
la source
1
Je voudrais savoir pourquoi cette réponse obtient autant de votes négatifs.
hkBattousai
6
@hkBattousai La solution a des votes négatifs car elle utilise une exception pour contrôler le flux d'exécution. Comme son nom l'indique, les exceptions ne doivent être utilisées que dans des cas exceptionnels.
Helio Santos
4
@HelioSantos N'est-ce pas une situation exceptionnelle pour laquelle la langue n'apporte pas de solution adéquate?
hkBattousai
8
Les exceptions sont lentes.
Gordon
2
L'impact sur les performances du lancer est énorme pour quelque chose qui n'est pas une erreur irrécupérable 99% du temps.
Michaël Roy
-4

Sortir d'une boucle for est un peu étrange pour moi, car la sémantique d'une boucle for indique généralement qu'elle s'exécutera un nombre spécifié de fois. Cependant, ce n'est pas mauvais dans tous les cas; si vous recherchez quelque chose dans une collection et que vous souhaitez vous casser après l'avoir trouvé, c'est utile. Il n'est cependant pas possible de sortir des boucles imbriquées en C ++; c'est dans d'autres langues grâce à l'utilisation d'une pause étiquetée. Vous pouvez utiliser une étiquette et un goto, mais cela pourrait vous donner des brûlures d'estomac la nuit ..? Cela semble être la meilleure option.

Nick Lewis
la source
11
Ce n'est pas étrange du tout. Si vous parcourez une collection pour rechercher quelque chose (et n'avez pas de méthode de recherche plus rapide), il est inutile de terminer la boucle. (à titre d'exemple)
Joe