Je suis un programmeur autodidacte. J'ai commencé à programmer il y a environ 1,5 an. Maintenant, j'ai commencé à avoir des cours de programmation à l'école. Nous avons des cours de programmation depuis 1/2 an et nous en aurons un autre en ce moment.
Dans les cours, nous apprenons à programmer en C++ (qui est un langage que je savais déjà assez bien utiliser avant de commencer).
Je n'ai pas eu de difficultés pendant ce cours mais il y a un problème récurrent auquel je n'ai pas pu trouver de solution claire.
Le problème est comme ça (en pseudocode):
do something
if something failed:
handle the error
try something (line 1) again
else:
we are done!
Le code invite l'utilisateur à entrer un nombre et le fait jusqu'à ce que l'entrée soit valide. Il utilise cin.fail()
pour vérifier si l'entrée n'est pas valide. Lorsque cin.fail()
est true
je dois appeler cin.clear()
et cin.ignore()
pour pouvoir continuer à obtenir des entrées du flux.
Je suis conscient que ce code ne vérifie pas
EOF
. Les programmes que nous avons écrits ne devraient pas le faire.
Voici comment j'ai écrit le code dans l'une de mes tâches à l'école:
for (;;) {
cout << ": ";
cin >> input;
if (cin.fail()) {
cin.clear();
cin.ignore(512, '\n');
continue;
}
break;
}
Mon professeur a dit que je ne devrais pas utiliser break
et continue
comme ça. Il a suggéré que j'utilise à la place une boucle régulière while
ou do ... while
.
Il me semble que l'utilisation de break
et continue
est la manière la plus simple de représenter ce type de boucle. J'y ai réfléchi pendant un bon moment, mais je n'ai pas vraiment trouvé de solution plus claire.
Je pense qu'il voulait que je fasse quelque chose comme ça:
do {
cout << ": ";
cin >> input;
bool fail = cin.fail();
if (fail) {
cin.clear();
cin.ignore(512, '\n');
}
} while (fail);
Pour moi, cette version semble beaucoup plus complexe car maintenant nous avons également une variable appelée fail
pour garder une trace et la vérification de l'échec d'entrée est effectuée deux fois au lieu d'une seule fois.
J'ai aussi pensé que je pouvais écrire le code comme ça (abuser de l'évaluation des courts-circuits):
do {
cout << ": ";
cin >> input;
if (fail) {
cin.clear();
cin.ignore(512, '\n');
}
} while (cin.fail() && (cin.clear(), cin.ignore(512, '\n', true);
Cette version fonctionne exactement comme les autres. Il n'utilise pas break
ou continue
et le test cin.fail()
n'est effectué qu'une seule fois. Il ne me semble cependant pas juste d'abuser de la "règle d'évaluation des courts-circuits" comme celle-ci. Je ne pense pas que mon professeur le souhaiterait non plus.
Ce problème ne s'applique pas seulement à la vérification de cin.fail()
. J'ai utilisé break
et continue
comme ceci pour de nombreux autres cas qui impliquent de répéter un ensemble de code jusqu'à ce qu'une condition soit remplie où quelque chose doit également être fait si la condition n'est pas remplie (comme appeler cin.clear()
et cin.ignore(...)
dans l'exemple cin.fail()
).
J'ai continué à utiliser break
et continue
tout au long du cours et maintenant mon professeur a cessé de s'en plaindre.
Quelles sont vos opinions à ce sujet?
Pensez-vous que mon professeur a raison?
Connaissez-vous une meilleure façon de représenter ce genre de problème?
J'écrirais l'instruction if légèrement différente, donc elle est prise lorsque l'entrée est réussie.
for (;;) {
cout << ": ";
if (cin >> input)
break;
cin.clear();
cin.ignore(512, '\n');
}
C'est aussi plus court.
Ce qui suggère une manière plus courte qui pourrait être appréciée par votre professeur:
cout << ": ";
while (!(cin >> input)) {
cin.clear();
cin.ignore(512, '\n');
cout << ": ";
}
Ce que vous devez viser, c'est en évitant les boucles brutes .
Déplacez la logique complexe dans une fonction d'aide et soudain, les choses sont beaucoup plus claires:
bool getValidUserInput(string & input)
{
cout << ": ";
cin >> input;
if (cin.fail()) {
cin.clear();
cin.ignore(512, '\n');
return false;
}
return true;
}
int main() {
string input;
while (!getValidUserInput(input)) {
// We wait for a valid user input...
}
}
Ce n'est pas tant que for(;;)
est mauvais. Ce n'est pas aussi clair que des modèles comme:
while (cin.fail()) {
...
}
Ou, comme l'a dit Sjoerd:
while (!(cin >> input)) {
...
}
Considérons le public principal pour ce genre de choses comme étant vos collègues programmeurs, y compris la future version de vous-même qui ne se souvient plus pourquoi vous avez bloqué la pause à la fin comme ça, ou sauté la pause avec la suite. Ce modèle de votre exemple:
for (;;) {
...
if (blah) {
continue;
}
break;
}
... nécessite quelques secondes de réflexion supplémentaire pour simuler par rapport à d'autres modèles. Ce n'est pas une règle stricte et rapide, mais sauter l'instruction break avec la continuation semble désordonné ou intelligent sans être utile, et placer l'instruction break à la fin de la boucle, même si cela fonctionne, est inhabituel. Normalement, les deux break
et continue
sont utilisés pour prématurément évacuer la boucle ou cette itération de la boucle, donc voir l'un ou l'autre à la fin semble un peu bizarre même si ce n'est pas.
A part ça, bonnes choses!
Mon professeur de logique à l'école a toujours dit, et je l'ai martelé dans mon cerveau, qu'il ne devrait y avoir qu'une seule entrée et une seule sortie pour les boucles. Sinon, vous commencez à obtenir du code spaghetti et à ne pas savoir où va le code pendant le débogage.
Dans la vraie vie, je pense que la lisibilité du code est vraiment importante, de sorte que si quelqu'un d'autre a besoin de résoudre ou de déboguer un problème avec votre code, il est facile pour eux de le faire.
De plus, s'il s'agit d'un problème de performances, car vous exécutez ce look des millions de fois, la boucle for peut être plus rapide. Les tests répondraient à cette question.
Je ne connais pas C++ mais j'ai complètement compris les deux premiers de vos exemples de code. Je suppose que continue va à la fin de la boucle for puis la boucle à nouveau. L'exemple while que j'ai complètement compris, mais pour moi, il était plus facile à comprendre que votre exemple for (;;). Le troisième, je devrais faire un travail de réflexion pour le comprendre.
Donc, pour moi, ce qui était plus facile à lire (ne connaissant pas le c ++) et ce que mon professeur de logique a dit que j'utiliserais la boucle while.