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.
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.
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.
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.
Utiliser break
ainsi que continue
dans une boucle for
convient parfaitement.
Cela simplifie le code et améliore sa lisibilité.
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.')
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.
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.
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. :)
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.
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 break
s 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?
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.
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.
Je ne vois aucune raison pour laquelle ce serait une mauvaise pratique à condition que vous souhaitiez terminer le traitement STOP à ce stade.
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.
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:
switch
(nous n'avons que 3 instructions de commutateur au total!).for
- un code que j’ai immédiatement classé comme devant être refactorisé en fonctions distinctes et remplacé par une instruction return
.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é.
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.
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
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;)
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.