Considérez le code suivant:
public void doSomething(int input)
{
while(true)
{
TransformInSomeWay(input);
if(ProcessingComplete(input))
break;
DoSomethingElseTo(input);
}
}
Supposons que ce processus implique un nombre d'étapes fini mais dépendant de l'entrée; la boucle est conçue pour se terminer d'elle-même à la suite de l'algorithme et n'est pas conçue pour s'exécuter indéfiniment (jusqu'à ce qu'elle soit annulée par un événement extérieur). Parce que le test pour voir si la boucle doit se terminer se trouve au milieu d'un ensemble logique d'étapes, la boucle while elle-même ne vérifie actuellement rien de significatif; la vérification est effectuée à la place «appropriée» dans l'algorithme conceptuel.
On m'a dit que c'était du mauvais code, car il était plus sujet aux bogues car la condition de fin n'était pas vérifiée par la structure de la boucle. Il est plus difficile de comprendre comment vous sortiriez de la boucle, et pourrait inviter des bogues car la condition de rupture pourrait être contournée ou omise accidentellement en raison de changements futurs.
Maintenant, le code pourrait être structuré comme suit:
public void doSomething(int input)
{
TransformInSomeWay(input);
while(!ProcessingComplete(input))
{
DoSomethingElseTo(input);
TransformInSomeWay(input);
}
}
Cependant, cela duplique un appel à une méthode dans le code, violant DRY; s'ils TransformInSomeWay
étaient plus tard remplacés par une autre méthode, les deux appels devraient être trouvés et modifiés (et le fait qu'il y en ait deux soit moins évident dans un morceau de code plus complexe).
Vous pouvez également l'écrire comme:
public void doSomething(int input)
{
var complete = false;
while(!complete)
{
TransformInSomeWay(input);
complete = ProcessingComplete(input);
if(!complete)
{
DoSomethingElseTo(input);
}
}
}
... mais vous avez maintenant une variable dont le seul but est de déplacer la vérification de condition vers la structure de la boucle, et doit également être vérifiée plusieurs fois pour fournir le même comportement que la logique d'origine.
Pour ma part, je dis qu'étant donné l'algorithme que ce code implémente dans le monde réel, le code original est le plus lisible. Si vous le traversiez vous-même, c'est ainsi que vous le penseriez, et il serait donc intuitif pour les personnes familiarisées avec l'algorithme.
Alors, qui est "mieux"? vaut-il mieux confier la responsabilité de la vérification de l'état à la boucle while en structurant la logique autour de la boucle? Ou est-il préférable de structurer la logique d'une manière "naturelle" comme indiqué par les exigences ou une description conceptuelle de l'algorithme, même si cela peut signifier contourner les capacités intégrées de la boucle?
do ... until
construction est également utile pour ces types de boucles car elles n'ont pas besoin d'être "amorcées".Réponses:
Restez avec votre première solution. Les boucles peuvent devenir très impliquées et une ou plusieurs coupures nettes peuvent être beaucoup plus faciles pour vous, quiconque regarde votre code, l'optimiseur et les performances du programme que d'élaborer des instructions if et des variables ajoutées. Mon approche habituelle est de mettre en place une boucle avec quelque chose comme "while (true)" et d'obtenir le meilleur codage possible. Souvent, je peux et je remplace alors les ruptures par un contrôle de boucle standard; la plupart des boucles sont assez simples, après tout. La condition de fin doit être vérifiée par la structure de la boucle pour les raisons que vous mentionnez, mais pas au prix de l'ajout de nouvelles variables entières, de l'ajout d'instructions if et de la répétition du code, qui sont tous encore plus sujets aux bogues.
la source
Ce n'est un problème significatif que si le corps de la boucle n'est pas trivial. Si le corps est si petit, alors l'analyser comme ça est trivial, et pas un problème du tout.
la source
J'ai du mal à trouver les autres solutions mieux que celle avec break. Bon, je préfère la voie Ada qui permet de faire
mais la solution de rupture est le rendu le plus propre.
Mon POV est que lorsqu'il manque une structure de contrôle, la solution la plus propre est de l'imiter avec d'autres structures de contrôle, sans utiliser de flux de données. (Et de même, lorsque j'ai un problème de flux de données, je préfère des solutions de flux de données pures à celle impliquant des structures de contrôle *).
Ne vous méprenez pas, la discussion n'aurait pas lieu si la difficulté n'était pas la même: la condition est la même et présente au même endroit.
Lequel de
et
rendre mieux la structure prévue? Je vote pour le premier, vous n'avez pas à vérifier que les conditions correspondent. (Mais voyez mon indentation).
la source
tandis que (vrai) et break est un idiome commun. C'est la manière canonique d'implémenter des boucles de mi-sortie dans des langages de type C. Ajouter une variable pour stocker la condition de sortie et un if () autour de la seconde moitié de la boucle est une alternative raisonnable. Certains magasins peuvent uniformiser officiellement l'un ou l'autre, mais aucun n'est supérieur; ils sont équivalents.
Un bon programmeur travaillant dans ces langages devrait être capable de savoir ce qui se passe en un coup d'œil s'il voit l'un ou l'autre. Cela pourrait faire une bonne petite question d'entrevue; remettez à quelqu'un deux boucles, une en utilisant chaque idiome, et demandez-leur quelle est la différence (bonne réponse: "rien", avec peut-être un ajout de "mais j'utilise habituellement celui-là.")
la source
Vos alternatives ne sont pas aussi mauvaises que vous ne le pensez. Un bon code devrait:
Indiquez clairement avec de bons noms de variables / commentaires dans quelles conditions la boucle doit être terminée. (La condition de rupture ne doit pas être cachée)
Indiquez clairement l'ordre des opérations. C'est là que placer la 3e opération optionnelle (
DoSomethingElseTo(input)
) à l'intérieur du if est une bonne idée.Cela dit, il est facile de laisser les instincts perfectionnistes et de polissage du laiton prendre beaucoup de temps. Je voudrais juste modifier le si comme mentionné ci-dessus; commentez le code et continuez.
la source
Les gens disent que c'est une mauvaise pratique à utiliser
while (true)
, et la plupart du temps, ils ont raison. Si votrewhile
instruction contient beaucoup de code compliqué et qu'il n'est pas clair quand vous sortez de if, alors vous vous retrouvez avec du code difficile à maintenir et un simple bug pourrait provoquer une boucle infinie.Dans votre exemple, vous avez raison d'utiliser
while(true)
car votre code est clair et facile à comprendre. Il est inutile de créer du code inefficace et plus difficile à lire, simplement parce que certaines personnes considèrent que c'est toujours une mauvaise pratique à utiliserwhile(true)
.J'étais confronté à un problème similaire:
L'utilisation
do ... while(true)
évite d'isset($array][$key]
être appelée deux fois inutilement, le code est plus court, plus propre et plus facile à lire. Il n'y a absolument aucun risque qu'un bug de boucle infinie soit introduitelse break;
à la fin.Il est bon de suivre les bonnes pratiques et d'éviter les mauvaises pratiques, mais il y a toujours des exceptions et il est important de garder l'esprit ouvert et de réaliser ces exceptions.
la source
Si un flux de contrôle particulier s'exprime naturellement sous la forme d'une instruction break, il n'est pratiquement jamais préférable d'utiliser d'autres mécanismes de contrôle de flux pour émuler une instruction break.
(notez que cela inclut le déroulement partiel de la boucle et le glissement du corps de la boucle vers le bas de sorte que la boucle commence coïncide avec le test conditionnel)
Si vous pouvez réorganiser votre boucle afin que le flux de contrôle s'exprime naturellement en ayant une vérification conditionnelle au début de la boucle, tant mieux. Cela vaut peut-être même la peine de réfléchir à la possibilité. Mais non, n'essayez pas de placer une cheville carrée dans un trou rond.
la source
Vous pouvez également aller avec ceci:
Ou moins confus:
la source
Lorsque la complexité de la logique devient difficile à utiliser, l'utilisation d'une boucle sans limite avec des interruptions en milieu de boucle pour le contrôle de flux est en fait la plus logique. J'ai un projet avec du code qui ressemble à ceci. (Remarquez l'exception à la fin de la boucle.)
Pour faire cette logique sans rupture de boucle illimitée, je me retrouverais avec quelque chose comme ceci:
Ainsi, l'exception est maintenant levée dans une méthode d'assistance. De plus, il a beaucoup d'
if ... else
imbrication. Je ne suis pas satisfait de cette approche. Par conséquent, je pense que le premier modèle est le meilleur.la source