web-dev-qa-db-fra.com

Est-ce une mauvaise pratique d’utiliser break dans une boucle?

Est-ce une mauvaise pratique d'utiliser break statement à l'intérieur d'une boucle for?

Dis, je cherche une valeur dans un tableau. Comparez dans une boucle for et quand 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 vFound et la définir sur true lorsque la valeur est trouvée et vérifier vFound dans la condition d'instruction for. Mais est-il nécessaire de créer une nouvelle variable uniquement à cette fin?

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

P.S: Les directives de codage MISRA déconseillent l’utilisation de break.

115
kiki

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

La plupart des "dangers" associés à l'utilisation de break ou continue dans une boucle for sont annulés si vous écrivez des boucles ordonnées et faciles à lire. Si le corps de votre boucle couvre plusieurs longueurs d'écran et comporte plusieurs sous-blocs imbriqués, vous pouvez facilement oublier que certains codes ne seront pas exécutés après la coupure. Si, toutefois, la boucle est courte et pertinente, l'objectif de l'instruction break devrait être évident.

Si une boucle devient trop grosse, 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.

107
e.James

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.

132
smirkingman

Vous pouvez trouver toutes sortes de codes professionnels avec des déclarations "break". Il est parfaitement logique de l'utiliser chaque fois que nécessaire. Dans votre cas, cette option est préférable à la création d’une variable distincte dans le seul but de sortir de la boucle.

50
Amit S

Utiliser break ainsi que continue dans une boucle for convient parfaitement.

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

46
user151323

Loin des mauvaises pratiques, Python (et d’autres langages?) A étendu la boucle for) afin qu’elle en fasse partie seulement être exécuté si la boucle ne fonctionne pas break.

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

Sortie:

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

Code équivalent sans break et ce 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.')
21
Nick T

Règle générale: si suivre une règle exige que vous fassiez quelque chose de plus maladroit et difficile à lire, enfreignez la règle, enfreignez-la.

Dans le cas de la mise en boucle jusqu'à ce que vous trouviez quelque chose, vous rencontrez le problème de la distinction entre trouver et non trouvé 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" sur 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 aide également à désencombrer le code. Dans la ligne principale, "trouver" devient une seule déclaration et, lorsque les conditions sont complexes, elles ne sont écrites qu'une seule fois.

14
Jay

Il n’ya rien de mal à utiliser une instruction break, mais les boucles imbriquées peuvent être source de confusion. Pour améliorer la lisibilité, de nombreuses langues ( au moins Java fait )] prennent en charge la rupture des é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 déclarations break (et return) augmentent souvent complexité cyclomatique , ce qui complique la tâche de prouver que le code agit correctement dans tous les cas.

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

14
cyber-monk

pause est une déclaration tout à fait acceptable (tout comme continue, btw). Tout est une question de lisibilité du code - tant que vous n'avez pas de boucles compliquées et autres, c'est bien.

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

13
riviera

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 lorsque vous parcourez un tableau:

for element in bigList: ...

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

13
eumiro

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

  • une ressource acquise en haut du bloc peut être libérée en bas (ceci est vrai même pour les blocs à l'intérieur de boucles for Instruction break (en C++ "moderne", "RAII" est utilisé pour le gérer 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 sortie d'une étendue)

  • quelqu'un peut changer le test conditionnel dans l'instruction for sans s'apercevoir qu'il existe d'autres conditions de sortie délocalisées

  • la réponse de ndim indique que certaines personnes peuvent éviter breaks de conserver une exécution de boucle relativement cohérente, mais vous compariez break à l'utilisation d'une variable de contrôle booléenne à sortie anticipée qui ne tient pas

De temps en temps, les personnes qui observent de tels bugs réalisent qu'elles peuvent être empêchées/atténuées par cette règle "sans interruption" ... en fait, il existe toute une stratégie connexe pour une programmation "plus sûre" appelée "programmation structurée", où chaque fonction est supposée avoir une seule entrée et un point de sortie aussi (c.-à-d. pas de goto, pas de retour anticipé). Cela éliminera peut-être certains bugs, mais en introduira 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 l'expérience dans un tel cadre, ou
  • ils sont juste des idiots dictatoriaux, ou
  • l’un des éléments ci-dessus + inertie historique (en ce sens que les justifications s’appliquent davantage au C qu'au C++ moderne).
7
Tony Delroy

Dans votre exemple, vous ne connaissez pas le nombre d'itérations pour la boucle for. Pourquoi ne pas utiliser la boucle while, qui permet au nombre d'itérations d'être indéterminé au début?

Il n'est donc pas nécessaire d'utiliser déclaration de rupture en général, car la boucle peut être mieux indiquée sous la forme d'une boucle while.

7
Schedler

Il est tout à fait correct d'utiliser break - comme d'autres l'ont fait remarquer, ce n'est nulle part dans la même ligue que goto.

Bien que vous souhaitiez peut-être utiliser la variable vFound pour vérifier en dehors de la boucle si la valeur a été trouvée dans le tableau. Également du point de vue de la maintenabilité, il pourrait être utile de disposer d’un indicateur commun indiquant les critères de sortie.

5
Ace

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

4

Dans le monde intégré, de nombreux codes utilisent 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 ceci comme code standard, j'essaie simplement d'illustrer un exemple.

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

4
Nate

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

J'ai trouvé seulement 22 déclarations break, parmi celles-ci:

  • 19 ont été utilisés dans les instructions switch (nous n'avons que 3 instructions de commutateur au total!).
  • 2 ont été utilisés dans des boucles for - un code que j’ai immédiatement classé comme devant être refactorisé en fonctions distinctes et remplacé par une instruction return.
  • Quant à la dernière boucle break à l'intérieur de la boucle while ... j'ai couru git blame pour voir qui a écrit cette merde!

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

J'ai également recherché des déclarations continue. Aucun trouvé.

4
Rene Saarsoo

Cela dépend de votre cas d'utilisation. Il existe des applications dans lesquelles le temps d'exécution d'une boucle for doit être constant (par exemple, pour satisfaire à certaines contraintes de temps ou pour masquer les données internes de votre machine contre les attaques basées sur le temps).

Dans ces cas, il sera même logique de définir un indicateur et de ne vérifier que sa valeur APRÈS que toutes les itérations de la boucle for se soient réellement déroulées. Bien sûr, toutes les itérations de boucle for doivent exécuter un 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.

3
ndim

Sur MISRA 98 règles utilisées par ma société dans C dev, l'instruction break n'est pas utilisée ...

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

2
Benoît

Je ne suis pas d'accord!

Pourquoi voudriez-vous ignorer 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 contrôles en haut de votre boucle comme si

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 transférer ce code!

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

Un "code professionnel" qui est semé d'embûches ne me ressemble pas vraiment. Cela ressemble à du codage paresseux;)

1
Biff MaGriff

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

0
gautamlakum