225 votes

Les boucles "while(true)" sont-elles si mauvaises ?

Je programme en Java depuis plusieurs années maintenant, mais je ne suis retourné à l'école que récemment pour obtenir un diplôme officiel. J'ai été assez surpris d'apprendre que, lors de mon dernier devoir, j'ai perdu des points pour avoir utilisé une boucle comme celle qui suit.

do{
     //get some input.
     //if the input meets my conditions, break;
     //Otherwise ask again.
} while(true)

Pour mon test, je ne fais que rechercher une entrée dans la console, mais on m'a dit que ce type de boucle est déconseillé car l'utilisation de break s'apparente à goto nous ne le faisons pas.

Je comprends parfaitement les pièges de goto et son cousin Java break:label et j'ai le bon sens de ne pas les utiliser. Je me rends également compte qu'un programme plus complet fournirait d'autres moyens de s'échapper, par exemple en terminant simplement le programme, mais ce n'était pas une raison citée par mon professeur, donc...

Quel est le problème avec do-while(true) ?

8 votes

Je ne sais pas si l'étiquette "devoirs" s'applique, je voulais plutôt dire que c'était une question de programmation générale, mais je suis d'accord.

24 votes

Demandez à votre professeur, ce genre de choses est assez subjectif.

19 votes

J'ai trouvé cet article utile pour comprendre ce qui est nuisible à propos de goto . La comparaison avec break est probablement bien intentionné, mais en réalité mal compris. Peut-être pouvez-vous sensibiliser votre professeur à ce sujet ;) D'après mon expérience, les professeurs ne connaissent pas grand-chose à l'art de la programmation.

222voto

Jon Skeet Points 692016

Je ne dirais pas que c'est mauvais - mais de la même manière, je chercherais normalement au moins une alternative.

Dans les situations où c'est la première chose que j'écris, j'ai presque toujours au moins essayez pour le remanier en quelque chose de plus clair. Parfois, on ne peut pas faire autrement (ou l'alternative est d'avoir une bool qui ne fait rien de significatif si ce n'est indiquer la fin de la boucle, moins clairement qu'une variable break ) mais cela vaut la peine d'essayer au moins.

Comme exemple de cas où il est plus clair d'utiliser break qu'un drapeau, réfléchissez :

while (true)
{
    doStuffNeededAtStartOfLoop();
    int input = getSomeInput();
    if (testCondition(input))
    {
        break;
    }
    actOnInput(input);
}

Maintenant, forçons-le à utiliser un drapeau :

boolean running = true;
while (running)
{
    doStuffNeededAtStartOfLoop();
    int input = getSomeInput();
    if (testCondition(input))
    {
        running = false;
    }
    else
    {
        actOnInput(input);
    }
}

Je considère que cette dernière est plus compliquée à lire : il y a un élément supplémentaire. else le bloc actOnInput est plus indenté, et si vous essayez de comprendre ce qui se passe quand testCondition renvoie à true vous devez examiner attentivement le reste du bloc afin de vérifier qu'il n'y a pas quelque chose après le site else qui se produirait si running a été réglé sur false ou pas.

Le site break communique l'intention plus clairement et permet au reste du bloc de faire ce qu'il doit faire sans se soucier des conditions antérieures.

Notez que c'est exactement le même genre d'argument que les gens ont à propos des déclarations de retour multiples dans une méthode. Par exemple, si je peux déterminer le résultat d'une méthode dans les premières lignes (par exemple parce qu'une entrée est nulle, ou vide, ou zéro), je trouve plus clair de retourner directement cette réponse que d'avoir une variable pour stocker le résultat, puis tout un bloc d'autre code, et enfin a return déclaration.

3 votes

Je suis d'accord pour dire que ce n'est pas le premier outil que j'utilise, mais il a semblé régler le problème de façon si propre, et j'aime le code propre.

0 votes

Dans ce genre de situation, y a-t-il des raisons d'extraire une partie du code de (ou autour de) la boucle et de le placer dans des méthodes externes, ce qui permet des retours anticipés (au sens propre) ?

3 votes

@X-Zero : Oui, parfois. Si vous pouvez transformer la "pause" en un "retour", c'est souvent une bonne chose... bien que vous puissiez toujours vous retrouver avec une while (true)

103voto

El Marcel Points 1461

AFAIK rien, vraiment. Les enseignants sont juste allergiques à goto parce qu'ils ont entendu quelque part que c'est vraiment mauvais. Sinon, vous écririez simplement :

bool guard = true;
do
{
   getInput();
   if (something)
     guard = false;
} while (guard)

Ce qui est presque la même chose.

C'est peut-être plus propre (car toutes les informations de bouclage sont contenues en haut du bloc) :

for (bool endLoop = false; !endLoop;)
{

}

4 votes

J'aime vos suggestions car la condition de terminaison est beaucoup plus visible. Cela signifie que lorsque vous lisez le code, vous comprenez plus rapidement ce qu'il essaie de faire. Je n'utiliserais jamais une boucle infinie mais j'utilise fréquemment vos deux versions.

4 votes

Le consensus est qu'un drapeau est meilleur qu'une rupture en grande partie parce qu'il exprime une intention ? Je peux voir cela comme étant bénéfique. Toutes les réponses sont solides, mais je vais les marquer comme acceptées.

20 votes

Dans les deux cas, le reste de la boucle est pollué (dans certaines situations au moins) par le fait qu'il faut aller jusqu'à la fin du corps de la boucle. même si tu sais que tu es en train de casser . Je vais mettre un exemple dans ma réponse...

39voto

zzzzBov Points 62084

Douglas Crockford a fait une remarque sur le fait qu'il aurait souhaité JavaScript contient un loop structure :

loop
{
  ...code...
}

Et je ne pense pas Java ne serait pas plus mal d'avoir un loop structure non plus.

Il n'y a rien d'intrinsèquement mauvais avec while(true) boucles, mais il y a est une tendance des enseignants à les décourager. Du point de vue de l'enseignement, il est très facile de faire en sorte que les étudiants créent des boucles sans fin et ne comprennent pas pourquoi la boucle ne s'échappe jamais.

Mais ce qu'ils mentionnent rarement, c'est que tous Les mécanismes de bouclage peuvent être reproduits avec while(true) boucles.

while( a() )
{
  fn();
}

est la même chose que

loop
{
  if ( !a() ) break;
  fn();
}

et

do
{
  fn();
} while( a() );

est la même chose que :

loop
{
  fn();
  if ( !a() ) break;
}

et

for ( a(); b(); c() )
{
  fn();
}

est la même chose que :

a();
loop
{
  if ( !b() ) break;
  fn();
  c();
}

Tant que vous pouvez configurer vos boucles de façon à ce que travaux la construction que vous choisissez d'utiliser n'a pas d'importance. Si elle se produit pour s'intégrer dans un for utiliser une for boucle.

Un dernier point : gardez vos boucles simples. S'il y a beaucoup de fonctionnalités qui doivent se produire à chaque itération, mettez-les dans une fonction. Vous pourrez toujours l'optimiser après l'avoir fait fonctionner.

2 votes

+1 : Il existe des complexités supplémentaires avec for boucles lorsque continue Les déclarations sont impliquées, mais elles ne constituent pas une extension majeure.

0 votes

J'utilise généralement une boucle for lorsqu'il y a une séquence que je veux parcourir, et une boucle while lorsqu'il y a une condition que je veux remplir. Le code est ainsi plus lisible.

4 votes

Est-ce que personne n'écrit for (;;) { d'autres ? (Prononcé "pour toujours"). C'était très populaire autrefois.

18voto

Jessica Brown Points 3042

En 1967, Edgar Dijkstra a écrit un article dans un magazine spécialisé expliquant pourquoi il fallait éliminer le goto des langages de haut niveau pour améliorer la qualité du code. Tout un paradigme de programmation appelé "programmation structurée" en est issu, bien que tout le monde ne soit pas d'accord pour dire que le goto est automatiquement synonyme de mauvais code.

L'essence de la programmation structurée est essentiellement que la structure du code devrait déterminer son flux plutôt que d'avoir des gotos ou des pauses ou de continuer à déterminer le flux, dans la mesure du possible. De la même manière, les points d'entrée et de sortie multiples d'une boucle ou d'une fonction sont également découragés dans ce paradigme.

Bien entendu, ce n'est pas le seul paradigme de programmation, mais il peut souvent être facilement appliqué à d'autres paradigmes comme la programmation orientée objet (ala Java).

Votre professeur a probablement appris, et essaie d'apprendre à votre classe, que la meilleure façon d'éviter le "code spaghetti" est de s'assurer que notre code est structuré et de suivre les règles implicites de la programmation structurée.

Bien qu'il n'y ait rien d'intrinsèquement "mauvais" dans une implémentation qui utilise break, certains considèrent qu'il est beaucoup plus facile de lire du code où la condition de la boucle est explicitement spécifiée dans la condition while(), et élimine certaines possibilités d'être trop rusé. L'utilisation d'une condition while(true) comporte des pièges qui semblent apparaître fréquemment dans le code des programmeurs novices, comme le risque de créer accidentellement une boucle infinie, ou de créer un code difficile à lire ou inutilement confus.

Paradoxalement, le traitement des exceptions est un domaine où l'on s'écarte de la programmation structurée et où l'on s'attend à ce qu'il en soit ainsi à mesure que l'on progresse dans la programmation en Java.

Il est également possible que votre instructeur ait attendu de vous que vous démontriez votre capacité à utiliser une structure de boucle particulière ou une syntaxe enseignée dans ce chapitre ou cette leçon de votre texte, et bien que le code que vous avez écrit soit fonctionnellement équivalent, vous n'avez peut-être pas démontré la compétence particulière que vous étiez censé apprendre dans cette leçon.

2 votes

Voici L'article d'Edsger Dijkstra . C'est une bonne lecture.

14voto

Ken Bloom Points 27197

La convention Java habituelle pour lire les entrées est la suivante :

import java.io.*;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String strLine;

while ((strLine = br.readLine()) != null) {
  // do something with the line
}

Et la convention C++ habituelle pour lire les entrées est :

#include <iostream>
#include <string>
std::string data;
while(std::readline(std::cin, data)) {
  // do something with the line
}

Et en C, c'est

#include <stdio.h>
char* buffer = NULL;
size_t buffer_size;
size_t size_read;
while( (size_read = getline(&buffer, &buffer_size, stdin)) != -1 ){
  // do something with the line
}
free(buffer);

ou si vous êtes convaincu de savoir quelle est la longueur de la plus longue ligne de texte de votre fichier, vous pouvez faire

#include <stdio.h>
char buffer[BUF_SIZE];
while (fgets(buffer, BUF_SIZE, stdin)) {
  //do something with the line
}

Si vous testez pour voir si votre utilisateur a saisi un quit il est facile d'étendre n'importe laquelle de ces 3 structures de boucle. Je vais le faire en Java pour vous :

import java.io.*;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line;

while ((line = br.readLine()) != null  && !line.equals("quit") ) {
  // do something with the line
}

Donc, bien qu'il y ait certainement des cas où break ou goto est justifié, si tout ce que vous faites est de lire à partir d'un fichier ou de la console ligne par ligne, alors vous ne devriez pas avoir besoin d'un while (true) Votre langage de programmation vous a déjà fourni un idiome approprié pour utiliser la commande d'entrée comme condition de la boucle.

0 votes

En fait, si vous utilisez un while (true) au lieu d'une de ces boucles d'entrée conventionnelles, vous pourriez oublier de vérifier la fin du fichier.

3 votes

Vous y parvenez efficacement en plaçant la première partie de la boucle dans la section while conditionnel. Dans la dernière condition Java, elle devient déjà assez lourde, et si vous deviez faire des manipulations importantes pour décider de continuer ou non, elle pourrait devenir assez longue. Vous pourriez le répartir dans une fonction distincte, et c'est peut-être mieux. Mais si vous le voulez dans une seule fonction, et qu'il y a un travail non trivial à faire avant de décider de continuer ou non, while(true) peut être la meilleure solution.

0 votes

@poolie : Alors il est probablement préférable d'utiliser la commande read comme condition de la boucle (qui vérifie EOF), et de vérifier vos autres conditions à l'intérieur de la boucle comme des instructions break.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X