Est-ce une mauvaise pratique d'utiliser le break in a for loop? [fermé]

123

Est-ce une mauvaise pratique d'utiliser une breakinstruction dans une forboucle ?

Dites, je recherche une valeur dans un tableau. Comparez à l'intérieur d'une boucle for et lorsque la valeur est trouvée, break;pour quitter la boucle for.

Est-ce une mauvaise pratique? J'ai vu l'alternative utilisée: définir une variable vFoundet la définir sur true lorsque la valeur est trouvée et vérifier vFoundla forcondition de l' instruction. Mais est-il nécessaire de créer une nouvelle variable uniquement dans ce but?

Je demande dans le contexte d'une boucle for normale en C ou C ++.

PS: Les directives de codage MISRA déconseillent d'utiliser break.

kiki
la source
28
Ne vous placez pas breakdans la même ligue que goto:)
BoltClock
2
Euh, je ne les ai pas placés dans la même ligue que pour aller à ... Les règles de la MISRA spécifient la pause, si je me souviens bien.
kiki du
1
La seule raison à laquelle je peux penser pour ne pas utiliser de pause dans une boucle est lorsque vous devez encore traiter plus d'éléments qui pourraient changer le résultat de ce que vous faites ...
Younes
4
Les règles de la MISRA ont été assouplies: misra.org.uk/forum/viewtopic.php?f=75&t=298
Johan Kotlinski

Réponses:

122

Beaucoup de réponses ici, mais je n'ai pas encore vu cela mentionné:

La plupart des «dangers» associés à l'utilisation breakou continuedans une boucle for sont annulés si vous écrivez des boucles ordonnées et facilement lisibles. Si le corps de votre boucle s'étend sur plusieurs longueurs d'écran et a plusieurs sous-blocs imbriqués, oui, vous pourriez facilement oublier que du code ne sera pas exécuté après la pause. Si, cependant, la boucle est courte et précise, le but de l'instruction break doit être évident.

Si une boucle devient trop grande, utilisez plutôt un ou plusieurs appels de fonction bien nommés dans la boucle. La seule vraie raison d'éviter de le faire est le traitement des goulots d'étranglement.

e.James
la source
11
Plutôt vrai. Bien sûr, si la boucle est si grande et complexe qu'il est difficile de voir ce qui se passe à l'intérieur, c'est un problème que vous ayez une pause ou non.
Jay
1
Un autre problème est que la pause et la poursuite causent des problèmes de refactorisation. Cela pourrait être un signe que c'est une mauvaise pratique. L'intention du code est également plus claire lors de l'utilisation d'instructions if.
Ed Greaves
Ces «appels de fonction bien nommés» peuvent être des fonctions lambda définies localement au lieu de fonctions privées définies en externe qui ne seront pas utilisées ailleurs.
DavidRR
135

Non, la pause est la bonne solution.

L'ajout d'une variable booléenne rend le code plus difficile à lire et ajoute une source potentielle d'erreurs.

homme souriant
la source
2
D'accord. Surtout si vous cherchez à sortir de la boucle sous plusieurs conditions. Le booléen pour laisser une boucle peut alors devenir vraiment déroutant.
Rontologist
2
C'est pourquoi j'utilise gotopour sortir des boucles imbriquées de niveau 2+ - car il n'y en a pasbreak(level)
Петър Петров
3
Je préfère mettre le code de la boucle dans une fonction et revenir simplement. Cela évite le goto et divise votre code en plus petits morceaux.
Dave Branton
53

Vous pouvez trouver toutes sortes de codes professionnels avec des instructions «break». Il est parfaitement logique de l'utiliser chaque fois que cela est nécessaire. Dans votre cas, cette option est meilleure que de créer une variable distincte dans le seul but de sortir de la boucle.

Amit S
la source
47

Utiliser breakaussi bien que continuedans une forboucle est parfaitement bien.

Il simplifie le code et améliore sa lisibilité.


la source
Oui ... Pendant un certain temps, je n'ai pas aimé l'idée et j'ai codé autour d'elle, mais il n'a pas fallu longtemps avant que je réalise que ... "la solution de contournement" était souvent un cauchemar de maintenance. Eh bien, pas un cauchemar, mais plus sujet aux erreurs.
MetalMikester
24

Loin d'être une mauvaise pratique, Python (et d'autres langages?) A étendu la structure de la forboucle de sorte qu'une partie de celle-ci ne sera exécutée que si la boucle ne le fait pasbreak .

for n in range(5):
    for m in range(3):
        if m >= n:
            print('stop!')
            break
        print(m, end=' ')
    else:
        print('finished.')

Production:

stop!
0 stop!
0 1 stop!
0 1 2 finished.
0 1 2 finished.

Code équivalent sans breaket que pratique else:

for n in range(5):
    aborted = False
    for m in range(3):
        if not aborted:
            if m >= n:
                print('stop!')
                aborted = True
            else:            
                print(m, end=' ')
    if not aborted:
        print('finished.')
Nick T
la source
2
Oooh, j'aime ça et je l'ai parfois souhaité dans la syntaxe C. Nice aussi, qui pourrait être adaptée dans un futur langage C-like sans ambiguïté.
supercat
"Langage de type C": P
nirvanaswap
15

Il n'y a rien de mal en soi à utiliser une instruction break, mais les boucles imbriquées peuvent prêter à confusion. Pour améliorer la lisibilité, de nombreux langages ( du moins Java le fait ) prennent en charge le passage aux étiquettes, ce qui améliorera considérablement la lisibilité.

int[] iArray = new int[]{0,1,2,3,4,5,6,7,8,9};
int[] jArray = new int[]{0,1,2,3,4,5,6,7,8,9};

// label for i loop
iLoop: for (int i = 0; i < iArray.length; i++) {

    // label for j loop
    jLoop: for (int j = 0; j < jArray.length; j++) {

        if(iArray[i] < jArray[j]){
            // break i and j loops
            break iLoop;
        } else if (iArray[i] > jArray[j]){  
            // breaks only j loop
            break jLoop;
        } else {
            // unclear which loop is ending
            // (breaks only the j loop)
            break;
        }
    }
}

Je dirai que les instructions break (et return) augmentent souvent la complexité cyclomatique, ce qui rend plus difficile la preuve que le code fait la bonne chose dans tous les cas.

Si vous envisagez d'utiliser une pause lors de l'itération sur une séquence pour un élément particulier, vous voudrez peut-être reconsidérer la structure de données utilisée pour contenir vos données. L'utilisation de quelque chose comme un ensemble ou une carte peut fournir de meilleurs résultats.

cyber-moine
la source
4
+1 pour la complexité cyclomatique.
sp00m
Les programmeurs .NET peuvent souhaiter explorer l'utilisation de LINQ comme alternative potentielle aux forboucles complexes .
DavidRR
14

break est une instruction tout à fait acceptable à utiliser (ainsi est continue , btw). Tout est question de lisibilité du code - tant que vous n'avez pas de boucles trop compliquées, c'est bien.

Ce n'est pas comme s'ils étaient de la même ligue que goto . :)

riviera
la source
Je l'ai vu affirmer qu'une déclaration break est effectivement un goto .
glenatron
3
Le problème avec goto est que vous pouvez le pointer n'importe où. Les deux casser et continuer préservent la modularité du code. Cet argument est au même niveau que d'avoir un seul point de sortie pour chaque fonction.
Zachary Yates
1
J'ai entendu dire qu'avoir plusieurs points de sortie pour une fonction est effectivement un goto . ; D
glenatron
2
@glenatron Le problème avec goto n'est pas l'instruction goto, c'est l'introduction du label auquel le goto saute est le problème. Lorsque vous lisez une déclaration goto, il n'y a aucune incertitude sur le flux de contrôle. Lorsque vous lisez une étiquette, il y a beaucoup d'incertitude sur le flux de contrôle (où sont tous les gotos qui transfèrent le contrôle à cette étiquette?). Une instruction break n'introduit pas d'étiquette et n'introduit donc pas de nouvelles complications.
Stephen C. Steel
1
Le "break" est un goto spécial qui ne peut laisser que des blocs. Les problèmes historiques avec "goto" étaient (1) qu'il pouvait être, et était souvent, utilisé pour construire des blocs de boucle chevauchants d'une manière qui n'est pas possible avec des structures de contrôle appropriées; (2) une manière courante de faire une construction "si-alors" qui était généralement "fausse" était de ramener le cas vrai à un endroit non lié dans le code, puis de revenir en arrière. Cela coûterait deux branches dans le cas rare, et zéro dans le cas courant, mais cela rendait le code hideux.
supercat du
14

Règle générale: si suivre une règle vous oblige à faire quelque chose de plus maladroit et difficile à lire, alors enfreindre la règle, alors enfreignez la règle.

Dans le cas d'une boucle jusqu'à ce que vous trouviez quelque chose, vous rencontrez le problème de distinguer trouvé par rapport à introuvable lorsque vous sortez. C'est:

for (int x=0;x<fooCount;++x)
{
  Foo foo=getFooSomehow(x);
  if (foo.bar==42)
    break;
}
// So when we get here, did we find one, or did we fall out the bottom?

Alors d'accord, vous pouvez définir un indicateur ou initialiser une valeur "trouvée" à null. Mais

C'est pourquoi en général je préfère pousser mes recherches dans des fonctions:

Foo findFoo(int wantBar)
{
  for (int x=0;x<fooCount;++x)
  {
    Foo foo=getFooSomehow(x);
    if (foo.bar==wantBar)
      return foo;
  }
  // Not found
  return null;
}

Cela permet également de désencombrer le code. Dans la ligne principale, "find" devient une seule instruction, et lorsque les conditions sont complexes, elles ne sont écrites qu'une seule fois.

Geai
la source
1
Exactement ce que je voulais dire. Souvent, lorsque je me retrouve à utiliser break, j'essaie de trouver un moyen de refactoriser le code en une fonction, afin que je puisse l'utiliser à la returnplace.
Rene Saarsoo
Je suis d'accord à 100% avec vous, il n'y a rien de mal en soi à utiliser break. Cependant, cela indique généralement que le code dans lequel il se trouve peut devoir être extrait dans une fonction où break serait remplacé par return. Cela devrait être considéré comme une «odeur de code» plutôt que comme une erreur pure et simple.
vascowhite
Votre réponse pourrait inciter certains à explorer l'opportunité d'utiliser plusieurs déclarations de retour .
DavidRR
13

Cela dépend de la langue. Bien que vous puissiez éventuellement vérifier une variable booléenne ici:

for (int i = 0; i < 100 && stayInLoop; i++) { ... }

il n'est pas possible de le faire lors d'une itération sur un tableau:

for element in bigList: ...

Quoi qu'il en soit, breakcela rendrait les deux codes plus lisibles.

eumiro
la source
7

Dans votre exemple, vous ne connaissez pas le nombre d'itérations pour la boucle for . Pourquoi ne pas utiliser en place la boucle, ce qui permet le nombre d'itérations à durée indéterminée au début?

Il est donc pas nécessaire d'utiliser pause statemement en général, la boucle peut être mieux comme indiqué en boucle.

Planificateur
la source
1
Une boucle «for» est souvent bonne s'il existe un nombre maximal d'itérations connu. Cela dit, une boucle while (1) est parfois meilleure dans un scénario où l'on recherche un élément et envisage de le créer s'il n'existe pas. Dans ce cas, si le code trouve l'élément, il effectue une rupture directe, et s'il atteint la fin du tableau, il crée un nouvel élément, puis effectue une rupture.
supercat du
2
Dans l'exemple donné - en recherchant une clé dans un tableau, la boucle for est la construction idiomatique appropriée. Vous n'utilisez pas de boucle while pour parcourir un tableau. Vous utilisez une boucle for. (ou une boucle for-each si disponible). Vous faites cela par courtoisie envers les autres programmeurs, car ils reconnaissent immédiatement la construction "for (int i = 0; i <ra.length; i ++) {}" comme "Parcourez le tableau" Ajouter une rupture, à cette construction est simplement une déclaration «Arrêtez tôt si vous le pouvez».
Chris Cudmore
7

Je suis d'accord avec les autres qui recommandent d'utiliser break. La question évidente qui en découle est de savoir pourquoi quelqu'un recommanderait le contraire? Eh bien ... lorsque vous utilisez break, vous ignorez le reste du code dans le bloc et les itérations restantes. Parfois, cela provoque des bogues, par exemple:

  • une ressource acquise en haut du bloc peut être libérée en bas (cela est vrai même pour les blocs à l'intérieur des forboucles), mais cette étape de libération peut être sautée accidentellement lorsqu'une sortie "prématurée" est provoquée par une breakinstruction (en "moderne" C ++, "RAII" est utilisé pour gérer cela de manière fiable et sans risque d'exception: en gros, les destructeurs d'objets libèrent des ressources de manière fiable, quelle que soit la façon dont une portée est sortie)

  • quelqu'un peut changer le test conditionnel dans l' forinstruction sans remarquer qu'il existe d'autres conditions de sortie délocalisées

  • La réponse de ndim observe que certaines personnes peuvent éviter breaks pour maintenir un temps d'exécution de boucle relativement cohérent, mais vous compariez breakà l'utilisation d'une variable de contrôle booléenne de sortie précoce où cela ne tient pas

De temps en temps, les gens qui observent de tels bogues se rendent compte qu'ils peuvent être évités / atténués par cette règle "sans interruption" ... en effet, il existe toute une stratégie connexe pour une programmation "plus sûre" appelée "programmation structurée", où chaque fonction est censée avoir un point d'entrée et de sortie unique aussi (c'est-à-dire pas de goto, pas de retour anticipé). Il peut éliminer certains bogues, mais il en introduit sans doute d'autres. Pourquoi font-ils cela?

  • ils ont un cadre de développement qui encourage un style particulier de programmation / code, et ils ont des preuves statistiques que cela produit un avantage net dans ce cadre limité, ou
  • ils ont été influencés par les directives de programmation ou par l'expérience dans un tel cadre, ou
  • ce ne sont que des idiots dictatoriaux, ou
  • n'importe laquelle de l'inertie historique ci-dessus + (pertinente en ce que les justifications sont plus applicables au C qu'au C ++ moderne).
Tony Delroy
la source
Eh bien, oui, mais là encore, j'ai vu des bogues dans le code par des gens qui ont essayé d'éviter religieusement break. Ils l'ont évité mais n'ont pas réfléchi aux conditions qui ont remplacé l'utilisation de la pause.
Tomáš Zato - Réintégrer Monica
5

Son utilisation est parfaitement valable break- comme d'autres l'ont souligné, ce n'est nulle part dans la même ligue que goto.

Bien que vous souhaitiez peut-être utiliser la vFoundvariable lorsque vous souhaitez vérifier en dehors de la boucle si la valeur a été trouvée dans le tableau. Du point de vue de la maintenabilité également, il pourrait être utile d'avoir un indicateur commun signalant les critères de sortie.

As
la source
5

J'ai fait quelques analyses sur la base de code sur laquelle je travaille actuellement (40 000 lignes de JavaScript).

Je n'ai trouvé que 22 breakdéclarations, parmi lesquelles:

  • 19 ont été utilisées à l'intérieur des switchinstructions (nous n'avons que 3 instructions switch au total!).
  • 2 ont été utilisés à l'intérieur de forboucles - un code que j'ai immédiatement classé comme devant être refactoré en fonctions distinctes et remplacé par returninstruction.
  • Quant à la dernière boucle breakintérieure while... j'ai couru git blamevoir qui a écrit cette merde!

Donc selon mes statistiques: si breakest utilisé en dehors de switch, c'est une odeur de code.

J'ai également recherché des continuedéclarations. Aucun trouvé.

René Saarsoo
la source
4

Je ne vois aucune raison pour laquelle ce serait une mauvaise pratique à condition que vous souhaitiez terminer le traitement STOP à ce stade.

Cornelius J. van Dyk
la source
4

Dans le monde embarqué, il existe de nombreux codes utilisant la construction suivante:

    while(1)
    { 
         if (RCIF)
           gx();
         if (command_received == command_we_are_waiting_on)
           break;
         else if ((num_attempts > MAX_ATTEMPTS) || (TickGet() - BaseTick > MAX_TIMEOUT))
           return ERROR;
         num_attempts++;
    }
    if (call_some_bool_returning_function())
      return TRUE;
    else
      return FALSE;

C'est un exemple très générique, beaucoup de choses se passent derrière le rideau, des interruptions en particulier. N'utilisez pas cela comme code standard, j'essaie juste d'illustrer un exemple.

Mon opinion personnelle est qu'il n'y a rien de mal à écrire une boucle de cette manière tant que des précautions appropriées sont prises pour éviter de rester indéfiniment dans la boucle.

Nate
la source
1
Pouvez-vous développer sur ce sujet? Il me semble que la boucle ne fait absolument rien ... à moins qu'il y ait des continueinstructions dans la logique de l'application. Sont là?
Rene Saarsoo
1
Les instructions continues ne sont pas nécessaires car vous êtes déjà lié par une boucle infinie. Vous exécutez essentiellement la logique nécessaire pour atteindre un objectif et si cet objectif n'est pas atteint en n nombre de tentatives ou si une condition de délai d'expiration expire, vous quittez la boucle via une construction break et prenez des mesures correctives ou informez le code appelant d'une erreur. Je vois souvent ce type de code dans les uC qui communiquent via un bus commun (RS-485, etc.) Vous essayez essentiellement de saisir la ligne et de communiquer sans collisions, si des collisions se produisent, vous attendez une durée déterminée par un nombre aléatoire et essayez encore.
Nate
1
À l'inverse, si les choses se passent bien, vous utilisez également l'instruction break pour quitter une fois la condition remplie. Dans ce cas, le code suivant la boucle est exécuté et tout le monde est content.
Nate
"if (call_some_bool_returning_function ()) return TRUE; else return FALSE;" pourrait simplement être "return call_some_bool_returning_function ()"
frankster
3

Dépend de votre cas d'utilisation. Il existe des applications où le temps d'exécution d'une boucle for doit être constant (par exemple pour satisfaire certaines contraintes de temps, ou pour cacher vos données internes aux attaques basées sur le temps).

Dans ces cas, il sera même judicieux de définir un indicateur et de ne vérifier la valeur de l'indicateur qu'APRÈS que toutes les foritérations de boucle se sont réellement déroulées. Bien sûr, toutes les itérations de la boucle for doivent exécuter du code qui prend toujours à peu près le même temps.

Si vous ne vous souciez pas du temps d'exécution ... utilisez break;et continue;pour rendre le code plus facile à lire.

ndim
la source
1
Si le timing est important, vous feriez peut-être mieux de faire quelques mises en veille et d'économiser les ressources système que de laisser le programme faire des opérations réputées inutiles.
Bgs
2

Sur les règles MISRA 98, qui sont utilisées sur mon entreprise en C dev, l'instruction break ne doit pas être utilisée ...

Edit: la pause est autorisée dans MISRA '04

Benoît
la source
2
Exactement pourquoi j'ai posé cette question! Je ne trouve aucune bonne raison pour laquelle une telle règle existe en tant que directive de codage. Aussi, comme tout le monde l'a dit ici, faites une pause; regarde mieux.
kiki
1
Je ne sais pas exactement pourquoi c'est interdit (personne plus intelligente que moi n'y pense ...) mais le break (et les retours multiples) sont meilleurs pour la lisibilité (à mon avis, mais mes avis ne sont pas des règles corporatives ... )
Benoît
1
Euh, les règles MISRA autorisent l'utilisation des instructions break: misra.org.uk/forum/viewtopic.php?f=75&t=298
luis.espinal
Nous avons utilisé les règles de MISRA 98 ... Il est exact que MISRA 2004 change les règles
Benoît
0

Bien sûr, break;c'est la solution pour arrêter la boucle for ou foreach. Je l'ai utilisé en php dans foreach et for loop et j'ai trouvé que cela fonctionnait.

Gautamlakum
la source
0

Je ne suis pas d'accord!

Pourquoi ignoreriez-vous la fonctionnalité intégrée d'une boucle for pour créer la vôtre? Vous n'avez pas besoin de réinventer la roue.

Je pense qu'il est plus logique d'avoir vos chèques en haut de votre boucle for comme ça

for(int i = 0; i < myCollection.Length && myCollection[i].SomeValue != "Break Condition"; i++)
{
//loop body
}

ou si vous devez d'abord traiter la ligne

for(int i = 0; i < myCollection.Length && (i == 0 ? true : myCollection[i-1].SomeValue != "Break Condition"); i++)
{
//loop body
}

De cette façon, vous pouvez écrire une fonction pour tout exécuter et créer un code beaucoup plus propre.

for(int i = 0; i < myCollection.Length && (i == 0 ? true : myCollection[i-1].SomeValue != "Break Condition"); i++)
{
    DoAllThatCrazyStuff(myCollection[i]);
}

Ou si votre condition est compliquée, vous pouvez également déplacer ce code!

for(int i = 0; i < myCollection.Length && BreakFunctionCheck(i, myCollection); i++)
{
    DoAllThatCrazyStuff(myCollection[i]);
}

«Code professionnel» qui est criblé de pauses ne me semble pas vraiment un code professionnel. Cela ressemble à du codage paresseux;)

Biff MaGriff
la source
4
le codage paresseux est un code professionnel :)
Jason
Seulement si ce n'est pas une douleur dans le cul à maintenir :)
Biff MaGriff
2
J'ai tendance à pratiquer "GTFO ASAP". Autrement dit, si je peux quitter une structure proprement, je devrais le faire dès que possible, et aussi près de la condition qui spécifie la sortie.
Chris Cudmore
Eh bien, cela fonctionne aussi. Je pense que l'argument que j'essaie de faire passer est que si vous utilisez une boucle for, cela ne fait pas de mal d'utiliser sa fonctionnalité intégrée pour rendre votre code lisible et modulaire. Si vous avez absolument besoin d'avoir des déclarations de rupture à l'intérieur de votre boucle, je vais m'asseoir et réfléchir aux raisons pour lesquelles ce niveau de complexité est nécessaire.
Biff MaGriff du
1
Haha. Considérez le fait que la plupart des gens qui découragent breaksoutiennent que c'est pour la lisibilité du code. Maintenant, regardez à nouveau la folle condition de 5 miles de long dans les boucles ci-dessus ...
Tomáš Zato - Réintégrer Monica