165 votes

Comment sortir d'une boucle depuis l'intérieur d'un interrupteur ?

J'écris un code qui ressemble à ça :

while(true) {
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        break; // **HERE, I want to break out of the loop itself**
    }
}

Existe-t-il un moyen direct de le faire ?

Je sais que je peux utiliser un drapeau et sortir de la boucle en plaçant une rupture conditionnelle juste après le commutateur. Je veux juste savoir si le C++ a déjà une construction pour cela.

23 votes

Pourquoi avez-vous besoin d'une rupture conditionnelle après l'interrupteur ? Changez simplement votre while de while(true) à while(flag)...

8 votes

@Dave_Jarvis Je suppose que c'est une version simplifiée qu'il a mise ici pour illustrer ce qu'il essayait de faire.

0 votes

Voir : Code complet (2e éd.). Voir aussi 'Structured Programming with goto statements' par D E Knuth ( pplab.snu.ac.kr/cours/adv_pl05/papers/p261-knuth.pdf ).

232voto

Mehrdad Afshari Points 204872

Vous pouvez utiliser goto .

while ( ... ) {
   switch( ... ) {
     case ...:
         goto exit_loop;

   }
}
exit_loop: ;

17 votes

Mais ne vous emballez pas avec ce mot-clé.

69 votes

C'est drôle, je me fais descendre parce que les gens n'aiment pas goto . Le PO a clairement mentionné sans utiliser de drapeaux . Pouvez-vous suggérer une meilleure solution, compte tenu des limites de l'OP :)

0 votes

Je suis presque sûr que c'est la seule façon de le faire, mais à moins que vous ne vouliez un code spaghetti, j'éviterais le goto.

81voto

sakra Points 13373

Une autre solution consiste à utiliser le mot-clé continue en combinaison avec break c'est-à-dire :

for (;;) {
    switch(msg->state) {
    case MSGTYPE:
        // code
        continue; // continue with loop
    case DONE:
        break;
    }
    break;
}

Utilisez le continue pour terminer chaque étiquette de cas où vous voulez que la boucle continue et utilisez l'instruction break pour terminer les étiquettes de cas qui devraient mettre fin à la boucle.

Bien entendu, cette solution ne fonctionne que s'il n'y a pas de code supplémentaire à exécuter après l'instruction switch.

20 votes

Si cette méthode est effectivement très élégante, elle présente l'inconvénient que la plupart des développeurs doivent d'abord la regarder pendant une minute pour comprendre comment/si cela fonctionne. :(

10 votes

La dernière phrase est ce qui fait que ce n'est pas si génial. :( Sinon, une mise en œuvre très propre.

0 votes

Quand on y pense, aucun code informatique n'est bon. Nous sommes seulement formés pour le comprendre. Par exemple, le code xml ressemblait à un déchet jusqu'à ce que je l'apprenne. Maintenant, il ressemble moins à des déchets. for(int x=0 ; x<10 ; ++x, moveCursor(x,y)) a en fait causé une erreur logique dans mon programme. J'ai oublié que la condition de mise à jour se produisait à la fin.

55voto

Dave Jarvis Points 12598

Prémisse

Le code suivant doit être considéré comme une mauvaise forme, quelle que soit la langue ou la fonctionnalité souhaitée :

while( true ) {
}

Arguments à l'appui

Le site while( true ) la boucle est une mauvaise forme parce qu'elle :

  • Brise le contrat implicite d'une boucle while.
    • La déclaration de la boucle while doit indiquer explicitement le sólo état de sortie.
  • Cela implique qu'il tourne en boucle pour toujours.
    • Le code à l'intérieur de la boucle doit être lu pour comprendre la clause de terminaison.
    • Les boucles qui se répètent indéfiniment empêchent l'utilisateur de mettre fin au programme depuis l'intérieur de celui-ci.
  • Est inefficace.
    • Il existe plusieurs conditions de fin de boucle, y compris la vérification de "vrai".
  • est sujet à des bogues.
    • Impossible de déterminer facilement où placer le code qui sera toujours exécuté pour chaque itération.
  • Conduit à un code inutilement complexe.
  • Analyse automatique du code source.
    • Pour trouver des bogues, analyser la complexité des programmes, effectuer des contrôles de sécurité ou déduire automatiquement tout autre comportement du code source sans exécution du code, la spécification de la ou des conditions de rupture initiales permet aux algorithmes de déterminer des invariants utiles, améliorant ainsi les mesures d'analyse automatique du code source.
  • Boucles infinies.
    • Si tout le monde utilise toujours while(true) pour les boucles qui ne sont pas infinies, nous perdons la capacité de communiquer de manière concise lorsque les boucles n'ont pas de condition de terminaison. (On peut soutenir que cela s'est déjà produit, donc le point est discutable).

Alternative à "Aller à"

Le code suivant est une meilleure forme :

while( isValidState() ) {
  execute();
}

bool isValidState() {
  return msg->state != DONE;
}

Avantages

Pas de drapeau. Non goto . Aucune exception. Facile à changer. Facile à lire. Facile à réparer. En plus du code :

  1. Isole la connaissance de la charge de travail de la boucle de la boucle elle-même.
  2. Permet à la personne qui maintient le code d'étendre facilement la fonctionnalité.
  3. Permet d'attribuer plusieurs conditions de terminaison en un seul endroit.
  4. Sépare la clause de terminaison du code à exécuter.
  5. est plus sûr pour les centrales nucléaires ;-)

Le deuxième point est important. Sans connaître le fonctionnement du code, si quelqu'un me demandait de faire en sorte que la boucle principale laisse à d'autres threads (ou processus) du temps CPU, deux solutions me viennent à l'esprit :

Option 1

Insérer rapidement la pause :

while( isValidState() ) {
  execute();
  sleep();
}

Option n° 2

Annulez l'exécution :

void execute() {
  super->execute();
  sleep();
}

Ce code est plus simple (et donc plus facile à lire) que celui d'une boucle avec une fonction switch . Le site isValidState devrait seulement déterminer si la boucle doit continuer. La partie la plus importante de la méthode doit être abstraite et intégrée dans la fonction execute qui permet aux sous-classes de remplacer le comportement par défaut (une tâche difficile si l'on utilise une méthode intégrée switch y goto ).

Exemple Python

Comparez la réponse suivante (à une question sur Python) qui a été publiée sur StackOverflow :

  1. Bouclez pour toujours.
  2. Demandez à l'utilisateur de saisir son choix.
  3. Si l'entrée de l'utilisateur est 'restart', continuer la boucle pour toujours.
  4. Sinon, arrêtez la boucle pour toujours.
  5. Fin.

Code

while True: 
    choice = raw_input('What do you want? ')

    if choice == 'restart':
        continue
    else:
        break

print 'Break!' 

Versus :

  1. Initialiser le choix de l'utilisateur.
  2. Boucle alors que le choix de l'utilisateur est le mot "redémarrage".
  3. Demandez à l'utilisateur de saisir son choix.
  4. Fin.

Code

choice = 'restart';

while choice == 'restart': 
    choice = raw_input('What do you want? ')

print 'Break!'

Aquí, while True aboutit à un code trompeur et excessivement complexe.

0 votes

Voir : Code Complete (2nd Edn). Voir aussi 'Structured Programming with goto statements' par D E Knuth ( pplab.snu.ac.kr/cours/adv_pl05/papers/p261-knuth.pdf ).

1 votes

Même si le titre de cette réponse n'est peut-être pas le meilleur, je dirais que le contenu est très bon et bien expliqué. Vous ne verrez jamais while(true) dans mon code et cet article explique toutes les raisons pour lesquelles. Un vote positif de ma part.

64 votes

L'idée que while(true) devrait être nuisible me semble bizarre. Il y a des boucles qui vérifient avant de s'exécuter, il y a celles qui vérifient après, et il y a celles qui vérifient au milieu. Puisque ces dernières n'ont pas de construction syntaxique en C et C++, vous devrez utiliser while(true) o for(;;) . Si vous considérez que cela est faux, c'est que vous n'avez pas encore suffisamment réfléchi aux différents types de boucles.

26voto

Alterlife Points 2914

Une façon élégante de le faire serait de le mettre dans une fonction :

int yourfunc() {

    while(true) {

        switch(msg->state) {
        case MSGTYPE: // ... 
            break;
        // ... more stuff ...
        case DONE:
            return; 
        }

    }
}

En option (mais c'est une "mauvaise pratique") : comme cela a déjà été suggéré, vous pouvez utiliser un goto, ou lancer une exception dans le switch.

4 votes

L'exception doit être utilisée pour, eh bien, lancer une exception, et non pour un comportement bien connu d'une fonction.

0 votes

Je suis d'accord avec Afterlife : Mettez-le dans une fonction.

19 votes

Lancer une exception est vraiment mauvais dans ce cas. Cela signifie "Je veux utiliser un goto, mais j'ai lu quelque part que je ne devrais pas les utiliser, alors je vais utiliser un subterfuge et faire semblant d'être intelligent".

16voto

Amber Points 159296

À ma connaissance, il n'existe pas de "double rupture" ou de construction similaire en C++. La construction la plus proche serait un goto - qui, bien que son nom ait une mauvaise connotation, existe dans la langue pour une raison : tant qu'il est utilisé avec prudence et parcimonie, c'est une option viable.

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