Dans une boucle for, dois-je déplacer la condition de rupture dans le champ de condition si possible? [fermé]


48

Parfois, j'ai besoin de boucles qui nécessitent une pause comme celle-ci:

for(int i=0;i<array.length;i++){
    //some other code
    if(condition){
        break;
    }
}

Je me sens mal à l'aise avec l'écriture

if(condition){
    break;
}

car il consomme 3 lignes de code. Et j'ai trouvé que la boucle peut être réécrite comme suit:

                                   ↓
for(int i=0;i<array.length && !condition;i++){
    //some other code
}

Ma question est donc la suivante: est-ce une bonne pratique de déplacer la condition dans le champ de la condition afin de réduire les lignes de codes si possible?


7
Cela dépend de ce qui est conditionen particulier.
Bergi

4
Il y a une raison pour laquelle la loi interdit l'utilisation de "break" et de "continue" dans les boucles. Voir aussi ceci: stackoverflow.com/q/3922599/476681
Publié le

35
Je pense que le nombre de lignes n'est pas le véritable problème. Cela pourrait être écrit sur une ligne. si (condition) pause; Le problème à mon avis est la lisibilité. Personnellement, je n'aime pas rompre une boucle autrement que d'utiliser la condition de boucle.
Joe

97
parce qu'il consomme 3 lignes de code Une très, très, très mauvaise raison de ne pas aimer les styles. OMI "consomme [des] lignes de code" est entre peu pertinent et une bonne chose. Tenter de placer trop de logique sur trop peu de lignes entraîne un code illisible et des bogues difficiles à trouver.
Andrew Henle

3
Opinion possiblement impopulaire: for(int i=0;i<array.length && !condition;i++)est relativement peu commune et peut être négligée par une personne qui ne fait que survoler le code; cela pourrait être un bon cas d'utilisation pour une boucle whileou do while, qui ont plus souvent plusieurs conditions de rupture dans la définition de la boucle.
Jules

Réponses:


154

Ces deux exemples que vous avez donnés ne sont pas équivalents du point de vue fonctionnel. Dans l'original, le test de condition est effectué après la section "un autre code", alors que dans la version modifiée, il est effectué en premier, au début du corps de la boucle.

Le code ne doit jamais être réécrit dans le seul but de réduire le nombre de lignes. De toute évidence, c’est un avantage appréciable, mais cela ne devrait jamais se faire au détriment de la lisibilité ou de l’exactitude.


5
Je comprends votre sentiment, mais j'ai conservé suffisamment de code hérité dans lequel la boucle compte plusieurs centaines de lignes et comporte plusieurs interruptions conditionnelles. Il est très difficile de déboguer lorsque vous vous attendez à ce que le code atteigne la rupture conditionnelle attendue, mais que le code antérieur a été exercé avant la rupture conditionnelle. Comment gérez-vous ce code? Dans des exemples brefs comme celui-ci, il est facile d’oublier ce genre de scénario trop commun.
Berin Loritsch

84
@BerinLoritsch: La bonne façon de gérer cela est de ne pas avoir de boucles de plusieurs centaines de lignes!
Chris

27
@Walfrat: Si la réécriture n'est pas une option, il n'y a plus vraiment de question. Ceci dit, vous pouvez généralement utiliser en toute sécurité un refactor de type "Extract Method" pour déplacer des blocs de code, ce qui devrait raccourcir la boucle de votre méthode principale et rendre le flux logique plus évident. C'est vraiment une affaire au cas par cas. Je maintiendrai ma règle générale de garder les méthodes courtes et de garder définitivement la logique de boucle suivante, mais je ne veux pas essayer de dire autre chose que cela dans un sens général, si ...
Chris

4
@AndrewHenle La brièveté est la lisibilité, du moins dans le sens où augmenter la verbosité réduit toujours la lisibilité et la maintenabilité. La réduction de la LOC est obtenue en augmentant la densité et en augmentant le niveau d'abstraction, et est très étroitement corrélée à la maintenabilité, comme l'attestent d'autres paires Q / A sur ce même site.
Leushenko

5
@ Joe une construction Do-While est si rare qu'elle est encline à déclencher le développement de développeurs qui doivent maintenir ce code ultérieurement. Quelques développeurs ne les ont jamais vus! Je ne sais pas si un bricolage améliorerait la lisibilité - cela augmenterait la difficulté de comprendre le code. Par exemple, ma base de code actuelle a environ 15 ans et 2 millions de LOC. Une cinquantaine de développeurs l'ont déjà touchée. Il n'a pas exactement de boucles do-while!
T. Sar - Réintégrer Monica le

51

Je n'achète pas l'argument selon lequel "cela consomme 3 lignes de code" et est donc mauvais. Après tout, vous pouvez simplement l'écrire comme suit:

if (condition) break;

et juste consommer une ligne.

Si le ifsymbole apparaît au milieu de la boucle, il doit bien sûr exister en tant que test séparé. Toutefois, si ce test apparaît à la fin du bloc, vous avez créé une boucle comportant un test de poursuite aux deux extrémités de la boucle, ce qui ajoute éventuellement à la complexité du code. Sachez cependant que parfois le if (condition)test n'a de sens que lorsque le bloc a été exécuté.

Mais en supposant que ce ne soit pas le cas, en adoptant l’autre approche:

for(int i=0;i<array.length && !condition;i++){
    //some other code
}

les conditions de sortie sont maintenues ensemble, ce qui peut simplifier les choses (surtout si " un autre code " est long).

Il n'y a pas de bonne réponse ici bien sûr. Soyez donc conscient des idiomes de la langue, des conventions de codage de votre équipe, etc. Mais globalement, j'adopterais l'approche du test unique lorsqu'il est logique de le faire.


14
@ jpmc26, c'est mieux? N ° de style alternatif valide? Bien sûr.
David Arno

6
Mieux car il est plus difficile de gâcher quand le code est édité plus tard et de ne pas avoir d'inconvénients notables.
jpmc26

4
Les garder ensemble parce que "le contenu de la boucle peut être long" semble être un argument faible. Lorsque votre contenu for est difficile à lire, vous avez d'autres problèmes que l'enregistrement des deux lignes de code.
BlueWizard

12
@ jpmc26 Pas d'accord. Les bretelles sont un fouillis superflu dans les conditionnels les plus courts, à une doublure, selon cette réponse. Sans est plus élégant et plus facile à regarder. Ils sont comme un opérateur conditionnel ternaire. Je n’ai jamais oublié d’ajouter les accolades en les divisant en blocs de grande taille; J'ajoute déjà de nouvelles lignes après tout.
benxyzzy

3
Tout est une préférence de style, mais si l'on prétend que c'est plus sûr, je ne suis pas d'accord avec ce raisonnement. Je préfère moi-même sans accolades et sur la ligne suivante, mais cela ne signifie pas pour autant qu'il est plus ou moins valable que les autres styles
phflack

49

Ce genre de question a suscité un débat presque aussi long que la programmation a été. Pour lancer mon chapeau dans le ring, je choisirais la version avec la breakcondition et voici pourquoi (dans ce cas particulier ):

La forboucle est juste là pour spécifier les valeurs itératives dans la boucle. Le codage de la condition de rupture à l'intérieur de cela brouille la logique de l'OMI.

Le fait d’avoir un séparateur breakindique clairement que lors du traitement normal, les valeurs sont celles spécifiées dans la forboucle et que le traitement doit se poursuivre sauf là où la condition intervient.

En guise d’arrière-plan, j’ai commencé à coder a break, puis j’ai mis la condition dans la forboucle pendant des années (sous la direction d’un programmeur exceptionnel), puis je suis revenu au breakstyle car il se sentait beaucoup plus propre (et c’était le style dominant les différents volumes de code que je consommais).

C'est une autre de ces guerres saintes à la fin de la journée - certains disent que vous ne devriez jamais sortir d'une boucle artificiellement, sinon ce n'est pas une vraie boucle. Faites ce que vous jugez lisible (pour vous et pour les autres). Si réduire votre frappe est vraiment votre truc, allez jouer au golf le week-end - la bière est facultative.


3
Si la condition a un bon nom, tel que "trouvé" (par exemple, vous recherchez quelque chose dans le tableau), il est alors judicieux de la placer dans la tête de la boucle for.
user949300

1
On m'a dit que les forboucles sont utilisées lorsque le nombre d'itérations est connu (par exemple, lorsqu'il n'y a pas de condition de rupture, mais la fin du tableau / de la liste) et whilequand elles ne le sont pas. Cela semble être le cas pour une whileboucle. Si la lisibilité est le problème, l' idx+=1intérieur de la boucle est beaucoup plus propre à lire qu'un if/elsebloc
Laiv

Vous ne pouvez pas simplement mettre la condition de rupture dans la boucle si un code la suit, vous devez donc au moins écrire "if (condition) {found = true; continue;}
gnasher729 le

Nous sommes tellement habitués aux for(int i=0; i<something;i++)boucles que je suppose que la moitié des développeurs ne se souviennent pas vraiment que les forboucles peuvent être utilisées différemment, et ont donc du mal à comprendre la &&conditionpartie.
Ralf Kleberhoff

@RalfKleberhoff En effet. Beaucoup de nouveaux développeurs ont été surpris de constater que les expressions de la déclaration tripartite sont toutes facultatives. Je ne me souviens même pas de la dernière fois où j'ai vu for(;;)...
Robbie Dee

43

forles boucles sont pour une itération sur quelque chose 1 - elles ne sont pas simplement lol-tirons-quelque-chose-au-hasard-du-corps-et-les-mettent-dans-la-tête-boucle - les trois expressions ont très significations spécifiques:

  • Le premier est pour initialiser l' itérateur . Il peut s'agir d'un index, d'un pointeur, d'un objet d'itération ou autre, tant qu'il est utilisé pour l' itération .
  • La seconde est pour vérifier si nous avons atteint la fin.
  • La troisième est pour faire avancer l'itérateur.

L'idée est de séparer la gestion des itérations ( comment effectuer une itération) de la logique à l'intérieur de la boucle ( que faire à chaque itération). De nombreuses langues ont généralement une boucle for-each qui vous évite les détails de l'itération, mais même si votre langue n'a pas cette construction, ou si elle ne peut pas être utilisée dans votre scénario, vous devez toujours limiter l'en-tête de la boucle. à la manipulation d'itération.

Vous devez donc vous poser la question suivante: votre état est-il lié au traitement des itérations ou à la logique dans la boucle? Les chances sont, il s'agit de la logique à l'intérieur de la boucle - donc il devrait être vérifié à l'intérieur de la boucle plutôt que dans l'en-tête.

1 Contrairement aux autres boucles, cela "attend" que quelque chose se passe. Une boucle for/ foreachdevrait avoir le concept d'une "liste" d'éléments sur lesquels itérer, même si cette liste est paresseuse ou infinie ou abstraite.


5
Ce fut ma première pensée quand j'ai lu la question. Déçu, j'ai dû faire défiler une demi-douzaine de réponses pour voir quelqu'un le dire
Nemo

4
Umm ... toutes les boucles sont pour l'itération - c'est ce que le mot "itération" signifie :-). Je suppose que vous voulez dire quelque chose comme "itérer sur une collection" ou "itérer à l'aide d'un itérateur"?
psmears

3
@psmears OK, j'ai peut-être choisi le mauvais mot - l'anglais n'est pas ma langue maternelle et, lorsque je pense à l'itération, je pense à "itération sur quelque chose". Je vais réparer la réponse.
Idan Arye

Un cas intermédiaire est où la condition est quelque chose comme someFunction(array[i]). Maintenant, les deux parties de la condition concernent la chose sur laquelle vous effectuez une itération et il serait logique de les combiner &&dans l'en-tête de la boucle.
Barmar

Je respecte votre position, mais je ne suis pas d'accord. Je pense que les forboucles au coeur sont des compteurs, pas des boucles sur une liste, et ceci est supporté par la syntaxe de fordans les langages précédents (ces langages avaient souvent une for i = 1 to 10 step 2syntaxe, et la stepcommande - permettant même une valeur initiale qui ne soit pas l'index du élément - indique un contexte numérique, pas un contexte de liste). Le seul cas d'utilisation qui, à mon avis, forprenne en charge les boucles car il suffit d'itérer une liste, c'est la parallélisation, ce qui nécessite de toute façon une syntaxe explicite pour ne pas altérer le code, par exemple, une sorte.
Logan Pickup

8

Je recommande d'utiliser la version avec if (condition). C'est plus lisible et plus facile à déboguer. L'écriture de 3 lignes de code supplémentaires ne vous cassera pas les doigts, mais facilitera la compréhension du code par la prochaine personne.


1
Ce n'est que si (comme cela a été commenté) que le bloc de boucle n'a pas des centaines de lignes de code et que la logique à l'intérieur n'est pas sorcière. Je pense que votre argument est correct, mais il me manque quelque chose comme cela naming thingsafin de comprendre ce qui se passe et quand la condition de rupture est remplie.
Laiv

2
@Laiv Si le bloc de boucle contient des centaines de lignes de code, le problème est plus important que la question de savoir où écrire la condition de rupture.
Aleksander

C'est pourquoi j'ai suggéré de contextualiser la réponse, car ce n'est pas toujours vrai.
Laiv

8

Si vous parcourez une collection où certains éléments doivent être ignorés, je vous recommande une nouvelle option:

  • Filtrez votre collection avant d'itérer

Java et C # rendent cela relativement facile à faire. Je ne serais pas surpris si C ++ avait le moyen de créer un itérateur de fantaisie pour ignorer certains éléments qui ne s'appliquent pas. Mais ce n'est vraiment une option que si votre langue de choix le supporte, et la raison que vous utilisez breakest parce qu'il existe des conditions pour que vous traitiez un élément ou non.

Sinon, il existe une très bonne raison de faire de votre condition une partie de la boucle for - en supposant que l'évaluation initiale soit correcte.

  • Il est plus facile de voir quand une boucle se termine tôt

J'ai travaillé sur du code où votre boucle for prend quelques centaines de lignes avec plusieurs conditions et quelques calculs complexes s'y déroulant. Les problèmes que vous rencontrez avec ceci sont:

  • break les commandes enfouies dans la logique sont difficiles à trouver et parfois surprenantes
  • Le débogage nécessite de parcourir chaque itération de la boucle pour vraiment comprendre ce qui se passe
  • Une grande partie du code ne dispose pas de refactorings facilement réversibles ni de tests unitaires pour aider à l'équivalence fonctionnelle après une réécriture.

Dans cette situation, je recommande les directives suivantes par ordre de préférence:

  • Filtrer la collection pour éliminer le besoin d'un breaksi possible
  • Faire la partie conditionnelle des conditions de la boucle for
  • Placez les conditionnels avec le tout breaken haut de la boucle si possible
  • Ajoutez un commentaire sur plusieurs lignes pour attirer l’attention sur la coupure et pourquoi elle est là

Ces instructions sont toujours sujettes à l' exactitude de votre code. Choisissez l'option qui représente le mieux l'intention et améliore la clarté de votre code.


1
La plupart de cette réponse est bien. Mais ... je peux voir comment le filtrage d'une collection peut éliminer le besoin de continuedéclarations, mais je ne vois pas en quoi elles aident beaucoup break.
user949300

1
@ user949300 Un takeWhilefiltre peut remplacer des breakinstructions. Si vous en avez un - Java, par exemple, ne les obtient que lors de la Jave 9 (non que ce soit si difficile à mettre en œuvre vous-même)
Idan Arye

1
Mais en Java, vous pouvez toujours filtrer avant l'itération. Utilisation de flux (1.8 ou version ultérieure) ou d’utilisation d’Apache Collections (1.7 ou version antérieure).
Laiv

@IdanArye - il existe des bibliothèques de modules complémentaires qui ajoutent de telles fonctionnalités à l'API de flux Java (par exemple, JOO-lambda )
Jules

@IdanArye n'a jamais entendu parler d'un takeWhile avant votre commentaire. Le fait de lier explicitement le filtre à un ordre me semble inhabituel et hacky, car dans un filtre "normal", chaque élément doit retourner vrai ou faux, indépendamment de ce qui vient avant ou après. De cette façon, vous pouvez exécuter les choses en parallèle.
user949300

8

Je recommande fortement d’adopter l’approche de la moindre surprise à moins d’obtenir un avantage significatif en agissant autrement.

Les gens ne lisent pas chaque lettre en lisant un mot et ne lisent pas chaque mot en lisant un livre. S'ils sont des adeptes de la lecture, ils regardent les contours des mots et des phrases et laissent leur cerveau remplir le reste. .

Il est donc probable qu'un développeur occasionnel présumera qu'il ne s'agit que d'une norme pour la boucle et ne la regarde même pas:

for(int i=0;i<array.length&&!condition;i++)

Si vous voulez utiliser ce style, peu importe, je vous recommande de changer les parties for(int i=0;i<et d' ;i++)indiquer au cerveau du lecteur qu'il s'agit d'un standard pour la boucle.


Une autre raison d'aller avec if- breakest que vous ne pouvez pas toujours utiliser votre approche avec le for-état. Vous devez utiliser if- breaklorsque la condition de rupture est trop complexe pour être masquée dans une forinstruction ou repose sur des variables uniquement accessibles à l'intérieur de la forboucle.

for(int i=0;i<array.length&&!((someWidth.X==17 && y < someWidth.x/2) || (y == someWidth.x/2 && someWidth.x == 18);i++)

Donc, si vous décidez d'aller avec if- break, vous êtes couvert. Mais si vous décidez d'aller avec for-Conditions, vous devez utiliser un mélange de if- breaket for-Conditions. Pour être cohérent, vous devez déplacer les conditions dans les deux sens entre les for-Conditions et if- à breakchaque fois que les conditions changent.


4

Votre transformation suppose que tout ce qui conditionest évalué se produit truelorsque vous entrez dans la boucle. Nous ne pouvons pas commenter sur la justesse de cela dans ce cas car il n'est pas défini.

Après avoir réussi à "réduire les lignes de code" ici, vous pouvez ensuite regarder

for(int i=0;i<array.length;i++){
    // some other code
    if(condition){
        break;
    }
    // further code
}

et appliquer la même transformation, arriver à

for(int i=0;i<array.length && !condition;i++){
    // some other code
    // further code
}

Ce qui est une transformation invalide, comme vous le faites maintenant sans condition further code.

Un schéma de transformation beaucoup plus sûr consiste à extraire some other codeet à évaluer conditionune fonction distincte

bool evaluate_condition(int i) // This is an horrible name, but I have no context to improve it
{
     // some other code
     return condition;
}

for(int i=0;i<array.length && !evaluate_condition(i);i++){
    // further code
}

4

TL; DR Faites ce qui se lit le mieux dans votre cas particulier

Prenons cet exemple:

for ( int i = 1; i < array.length; i++ ) 
{
    if(something) break;
    // perform operations
}

Dans ce cas, nous ne souhaitons pas que le code de la boucle for s'exécute si la valeur somethingest true, le déplacement du test de somethingdans le champ de condition est donc raisonnable.

for ( int i = 1; i < array.length && !something; i++ )

Là encore, en fonction de si somethingpeut être défini trueavant la boucle, mais pas au sein de celle-ci, cela pourrait offrir plus de clarté:

if(!something)
{
    for ( int i = 1; i < array.length; i++ )
    {...}
}

Maintenant, imaginez ceci:

for ( int i = 1; i < array.length; i++ ) 
{
    // perform operations
    if(something) break;
    // perform more operations
}

Nous envoyons un message très clair là-bas. Si somethingdevient vrai pendant le traitement de la matrice, abandonnez alors toute l'opération à ce stade. Afin de déplacer le contrôle dans le champ de condition, vous devez effectuer les tâches suivantes:

for ( int i = 1; i < array.length && !something; i++ ) 
{
    // perform operations
    if(!something)
    {
        // perform more operations
    }
}

On peut soutenir que le "message" est devenu flou et que nous vérifions la condition de panne à deux endroits - que se passera-t-il si la condition de panne change et que nous en oublions un?

Il existe bien sûr beaucoup plus de formes différentes qu'une boucle ou une condition pourrait être, toutes avec leurs propres nuances.

Écrivez le code pour qu'il se lise bien (ceci est évidemment quelque peu subjectif) et de manière concise. En dehors d'un ensemble draconien de directives de codage, toutes les "règles" ont des exceptions. Ecrivez ce que vous pensez exprimer le mieux la prise de décision et minimisez les risques d'erreurs futures.


2

Bien que cela puisse sembler attrayant, car vous voyez toutes les conditions dans l’en-tête de la boucle et breakpeut prêter à confusion à l’intérieur de grands blocs, une forboucle est un motif dans lequel la plupart des programmeurs s’attendent à une boucle sur une certaine plage.

Surtout depuis que vous le faites, l'ajout d'une condition de rupture supplémentaire peut entraîner une confusion et il est plus difficile de voir si elle est fonctionnellement équivalente.

Une bonne solution consiste souvent à utiliser une boucle whileou do {} whilequi vérifie les deux conditions, en supposant que votre tableau comporte au moins un élément.

int i=0
do {
    // some other code
    i++; 
} while(i < array.length && condition);

En utilisant une boucle qui ne fait que vérifier une condition et ne pas exécuter de code à partir de l'en-tête de la boucle, vous êtes très clair lorsque la condition est vérifiée, quelle est la condition réelle et quel est le code avec les effets secondaires.


Malgré le fait que cette question contienne déjà de bonnes réponses, je souhaitais spécifiquement y revenir, car cela soulève un point important: pour moi, avoir autre chose qu'un simple contrôle lié dans la boucle for ( for(...; i <= n; ...)) rend le code plus difficile à lire car j'ai s'arrêter et réfléchir à ce qui se passe ici et pourrait manquer la condition lors du premier passage parce que je ne m'attends pas à ce qu'il soit là. Pour moi, une forboucle implique que vous boucliez sur une plage. S'il existe une condition de sortie spéciale, elle doit être explicite avec un if, ou vous pouvez utiliser un while.
CompuChip

1

C'est mauvais, car le code est destiné à être lu par des humains - les forboucles non idiomatiques sont souvent difficiles à lire, ce qui signifie que les bogues se cachent plus facilement ici, mais le code original aurait pu être amélioré tout en le maintenant. court et lisible.

Si vous souhaitez rechercher une valeur dans un tableau (l'exemple de code fourni est quelque peu générique, mais il peut aussi s'agir de ce modèle), vous pouvez simplement essayer explicitement d'utiliser une fonction fournie par un langage de programmation spécifiquement à cette fin. Par exemple (pseudo-code, car un langage de programmation n'a pas été spécifié, les solutions varient en fonction d'un langage particulier).

array.find(item -> item.valid)

Cela devrait sensiblement raccourcir la solution tout en la simplifiant, car votre code indique précisément ce dont vous avez besoin.


1

À mon avis, peu de raisons disent que vous ne devriez pas.

  1. Cela réduit la lisibilité.
  2. La sortie peut ne pas être la même. Cependant, cela peut être fait avec une do...whileboucle (pas while) car la condition est vérifiée après une exécution de code.

Mais en plus de cela, considérez ceci,

for(int i=0;i<array.length;i++){

    // Some other code
    if(condition1){
        break;
    }

    // Some more code
    if(condition1){
        break;
    }

    // Some even more code
}

Maintenant, pouvez-vous vraiment y arriver en ajoutant ces conditions à cette forcondition?


Qu'est-ce que " openion "? Voulez-vous dire " opinion "?
Peter Mortensen

Qu'entendez-vous par "peu de raisons en ouverture qui disent que vous ne devriez pas" ? Peux-tu élaborer?
Peter Mortensen le

0

Je pense que supprimer le breakpeut être une bonne idée, mais pas pour sauvegarder des lignes de code.

L'avantage de l'écrire sans cela breakest que la post-condition de la boucle est évidente et explicite. Lorsque vous terminez la boucle et exécutez la ligne suivante du programme, votre condition de boucle i < array.length && !conditiondoit être fausse. Par conséquent, soit iétait supérieur ou égal à votre longueur de tableau, ou conditionest true.

Dans la version avec break, il y a un piège caché. La condition de boucle indique que la boucle se termine lorsque vous avez exécuté un autre code pour chaque index de tableau valide, mais il existe en fait une breakinstruction qui pourrait terminer la boucle avant, et vous ne la verrez pas à moins de consulter le code de la boucle. très soigneusement. Et les analyseurs statiques automatisés, y compris l'optimisation des compilateurs, pourraient également avoir du mal à déterminer quelles sont les conditions postérieures de la boucle.

C'est la raison pour laquelle Edgar Dijkstra a écrit «Goto Considered Harmful». Ce n'était pas une question de style: rompre avec une boucle empêche de raisonner formellement sur l'état du programme. J'ai écrit beaucoup de break;déclarations dans ma vie, et une fois adulte, j'ai même écrit un goto(pour sortir de plusieurs niveaux d'une boucle interne de performances critiques puis pour continuer avec une autre branche de l'arbre de recherche). Cependant, je suis beaucoup plus susceptible d'écrire une returninstruction conditionnelle à partir d'une boucle (qui n'exécute jamais le code après la boucle et ne peut donc pas en vider ses post-conditions) qu'un break.

Postscript

En fait, refactoriser le code pour obtenir le même comportement pourrait nécessiter davantage de lignes de code. Votre refactoring pourrait être valable pour un autre code ou si nous pouvons prouver que conditioncela commencera faux. Mais votre exemple équivaut dans le cas général à:

if ( array.length > 0 ) {
  // Some other code.
}
for ( int i = 1; i < array.length && !condition; i++ ) {
  // Some other code.
}

Si un autre code ne correspond pas à une ligne, vous pouvez le déplacer vers une fonction afin de ne pas vous répéter, puis vous avez presque transformé votre boucle en une fonction récursive.

Je reconnais toutefois que ce n’était pas l’essentiel de votre question et que vous restiez simple.


Le problème n'est pas que la rupture rend difficile la discussion sur le code, le problème est que la rupture à des endroits aléatoires rend le code compliqué. Si cela est réellement nécessaire, il n'est pas difficile de discuter à cause de la rupture, mais parce que le code doit faire des choses compliquées.
gnasher729

Voici pourquoi je ne suis pas d'accord: si vous savez qu'il n'y a pas d' breakénoncé, vous savez alors quelle est la condition de boucle et que la boucle s'arrête lorsque cette condition est fausse. S'il y en a break? Ensuite, la boucle peut se terminer de plusieurs façons et, dans le cas général, déterminer quand l'un d'entre eux pourrait arrêter la boucle résout littéralement le problème de la suppression. En pratique, ce qui me préoccupe le plus, c’est que la boucle ressemble à une chose, mais en réalité, quiconque a écrit le code après la boucle a négligé un cas périphérique enterré dans une logique complexe. Il est difficile de rater des choses dans la boucle.
Davislor

0

Oui, mais votre syntaxe est non conventionnelle et donc mauvaise (tm)

Espérons que votre langue supporte quelque chose comme

while(condition) //condition being a condition which if true you want to continue looping
{
    do thing; //your conditionally executed code goes here
    i++; //you can use a counter if you want to reference array items in order of index
}

2
J'aurais peut-être dû utiliser un mot différent pour «condition». Je ne voulais pas dire littéralement qu'il s'agissait exactement de la même condition dans l'exemple
Ewan

1
Non seulement la boucle for n'est pas non moins conventionnelle à distance, il n'y a absolument aucune raison pour qu'une boucle while de cette forme soit meilleure qu'une boucle for équivalente. En fait, la plupart des gens diraient que c'est pire, comme les auteurs des Lignes directrices de base sur
Sean Burton le

2
Certaines personnes détestent simplement les alternatives créatives. Ou regarder un problème d'une manière différente de celle qui leur est posée. Ou plus généralement, des culs intelligents. Je ressens ta douleur mon frère!
Martin Maat

1
@sean désolé mec, je sais que les gens détestent se faire dire qu'ils ont tort, mais je vous renvoie à es.77
Ewan

2
Cela whilemet l'accent sur le monstre dans le code - l'exception cachée dans le code de routine / ordinaire. La logique métier est à l'avant-plan, la mécanique de bouclage est comprise. Cela évite la réaction "attendez une minute ..." à l' foralternative boucle. Cette réponse corrige le problème. vote absolument.
radarbob

0

Si vous craignez qu'une fordéclaration trop complexe soit difficile à lire, c'est certainement une préoccupation valable. Le gaspillage d'espace vertical est également un problème (bien que moins critique).

(Personnellement, je n'aime pas la règle ifselon laquelle les instructions doivent toujours avoir des accolades, ce qui est en grande partie la cause de l'espace vertical gaspillé ici. Notez que le problème est de gaspiller de l'espace vertical. L' utilisation d' un espace vertical avec des lignes significatives ne devrait pas poser de problème.)

Une option consiste à le scinder en plusieurs lignes. C’est certainement ce que vous devriez faire si vous utilisez des forinstructions très complexes , avec plusieurs variables et conditions. (C’est pour moi une meilleure utilisation de l’espace vertical, donnant à autant de lignes que possible quelque chose de significatif.)

for (
   int i = 0, j = 0;
   i < array.length && !condition;
   i++, j++
) {
   // stuff
}

Une meilleure approche pourrait être d’essayer d’utiliser une forme plus déclarative et moins impérative. Cela varie en fonction de la langue. Vous devrez peut-être écrire vos propres fonctions pour activer ce type de choses. Cela dépend aussi de ce conditionqui est réellement. Vraisemblablement, il doit pouvoir changer d’une manière ou d’une autre, en fonction de ce qui se trouve dans le tableau.

array
.takeWhile(condition)  // condition can be item => bool or (item, index) => bool
.forEach(doSomething); // doSomething can be item => void or (item, index) => void

Fractionner une fordéclaration complexe ou inhabituelle en plusieurs lignes est la meilleure idée de toute cette discussion
user949300

0

Une chose qui a été oubliée: lorsque je sors d'une boucle (je ne fais pas toutes les itérations possibles, peu importe son implémentation), c'est généralement le cas. Non seulement je veux sortir de la boucle, mais je veux aussi savoir pourquoi c'est arrivé. . Par exemple, si je cherche un élément X, non seulement je casse la boucle, mais je veux aussi savoir que X a été trouvé et où il se trouve.

Dans cette situation, avoir une condition en dehors de la boucle est tout à fait naturel. Donc je commence avec

bool found = false;
size_t foundIndex;

et dans la boucle j'écrirai

if (condition) {
    found = true;
    foundIndex = i;
    break;
}

avec la boucle for

for (i = 0; i < something && ! found; ++i)

Maintenant, vérifier l'état de la boucle est assez naturel. L'existence d'une instruction "break" dépend de la nature du traitement que vous souhaitez éviter dans la boucle. Si un traitement est nécessaire et que ce n’est pas le cas, vous pouvez avoir quelque chose comme:

if (! found) { ... }

quelque part dans votre boucle.

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.