L'utilisation de goto en vaut-elle la peine?

29

gotoest presque universellement découragé. Est-ce que l'utilisation de cette déclaration en vaut la peine?

Casebash
la source
16
xkcd.com/292
TheLQ
1
gotoest ce que le processeur fait en fin de compte, mais cela ne fonctionne pas bien avec ce que les humains ont besoin de comprendre et d'abstraire car il n'y a pas de marque sur l'extrémité cible. Comparez avec Intercal COMEFROM.
2
@ ThorbjørnRavnAndersen, que veulent dire les humains lorsqu'ils parlent de transitions d'état dans une machine à états? Ne voyez-vous pas une similitude? «Aller dans un état donné », c'est ce que cela signifie et tout le reste ne serait rien d'autre qu'une complication inutile d'une chose aussi triviale. Et qu'entendez-vous par «pas de marque»? L'étiquette est une telle marque.
SK-logic
@ SK-logic, dépend de la distance à laquelle vous autoriserez les goto. Les machines d'état n'ont pas besoin de goto pour être implémentées.
@ ThorbjørnRavnAndersen, bien sûr, goton'est pas requis. C'est juste la manière la plus rationnelle de les implémenter dans les langages impératifs, car c'est la chose la plus proche de la sémantique même de la transition d'état. Et sa destination peut être assez éloignée de la source, elle n'entravera pas la lisibilité. Mon exemple préféré d'un tel code est la mise en œuvre par DE Knuth du jeu Adventure.
SK-logic

Réponses:

50

Cela a été discuté plusieurs fois sur Stack Overflow, et Chris Gillum a résumé les utilisations possibles degoto :

Quitter proprement une fonction

Souvent, dans une fonction, vous pouvez allouer des ressources et devez quitter à plusieurs endroits. Les programmeurs peuvent simplifier leur code en plaçant le code de nettoyage des ressources à la fin de la fonction, tous les "points de sortie" de la fonction se trouveront sur l'étiquette de nettoyage. De cette façon, vous n'avez pas à écrire de code de nettoyage à chaque "point de sortie" de la fonction.

Quitter les boucles imbriquées

Si vous êtes dans une boucle imbriquée et devez rompre toutes les boucles, un goto peut rendre cela beaucoup plus propre et plus simple que les instructions break et les if-checks.

Améliorations des performances de bas niveau

Ceci n'est valable que dans le code critique, mais les instructions goto s'exécutent très rapidement et peuvent vous donner un coup de pouce lorsque vous vous déplacez dans une fonction. Il s'agit cependant d'une arme à double tranchant, car un compilateur ne peut généralement pas optimiser le code qui contient des gotos.

Je dirais, comme beaucoup d'autres le diraient, que dans tous ces cas, l'utilisation de gotoest utilisée comme un moyen de sortir d'un coin dans lequel on s'est codé et est généralement un symptôme de code qui pourrait être refactorisé.

Communauté
la source
28
Le premier problème est résolu très soigneusement par des finallyblocs dans les langages modernes, et le second est résolu par des breaks étiquetés . Cependant, si vous êtes coincé avec C, gotoc'est à peu près la seule façon de résoudre ces problèmes avec élégance.
Chinmay Kanchi
2
Très bons points. J'aime la façon dont Java permet de sortir de plusieurs niveaux de boucles
Casebash
4
@Chinmay - enfin, les blocs ne s'appliquent qu'aux 1) langages modernes qui les ont et b) vous pouvez tolérer la surcharge (la gestion des exceptions a une surcharge). Autrement dit, l'utilisation finallyn'est valide que dans ces conditions. Il existe des situations très rares, mais valables, où un goto est la voie à suivre.
luis.espinal
21
D'accord, les gens ... quelle est la différence entre break 3ou break myLabelet goto myLabel. Oh, c'est juste le mot-clé. Si vous utilisez break, continueou un mot-clé similaire, vous utilisez en fait un goto(Si vous utilisez, for, while, foreach, do/loopvous utilisez un goto conditionnel.)
Matthew Whited
4
À propos des performances de bas niveau - le comportement du processeur moderne peut être difficile à prévoir (pipeline, exécution dans le désordre), donc dans 99% des cas, cela n'en vaut pas la peine ou même plus lent. @MatthewWhited: Portée. Si vous entrez la portée à un point arbitraire, il n'est pas clair (pour l'homme) quels constructeurs doivent être appelés (le problème existe également en C).
Maciej Piechotka
42

Les constructions de flux de contrôle de niveau supérieur ont tendance à correspondre aux concepts du domaine problématique. Un if / else est une décision basée sur une condition. Une boucle indique d'effectuer une action à plusieurs reprises. Même une déclaration de rupture dit "nous faisions cela à plusieurs reprises, mais maintenant nous devons arrêter".

Une instruction goto, d'autre part, tend à correspondre à un concept dans le programme en cours d'exécution, pas dans le domaine problématique. Il dit de continuer l'exécution à un point spécifié du programme . Quelqu'un qui lit le code doit déduire ce que cela signifie par rapport au domaine problématique.

Bien sûr, toutes les constructions de niveau supérieur peuvent être définies en termes de gotos et de branches conditionnelles simples. Cela ne signifie pas qu'ils ne sont que des gotos déguisés. Considérez-les comme des gotos restreints - et ce sont les restrictions qui les rendent utiles. Une instruction break est implémentée comme un saut à la fin de la boucle englobante, mais il vaut mieux la considérer comme opérant sur la boucle dans son ensemble.

Toutes choses étant égales par ailleurs, le code dont la structure reflète celle du domaine problématique a tendance à être plus facile à lire et à maintenir.

Il n'y a aucun cas où une instruction goto est absolument requise (il y a un théorème à cet effet), mais il y a des cas où cela peut être la moins mauvaise solution. Ces cas varient d'une langue à l'autre, selon les constructions de niveau supérieur prises en charge par la langue.

En C, par exemple, je pense qu'il y a trois scénarios de base où un goto est approprié.

  1. Sortir d'une boucle imbriquée. Cela ne serait pas nécessaire si la langue avait une instruction break étiquetée.
  2. Renvoi d'un bout de code (généralement un corps de fonction) en cas d'erreur ou autre événement inattendu. Cela ne serait pas nécessaire si le langage comportait des exceptions.
  3. Implémentation d'une machine à états finis explicite. Dans ce cas (et, je pense, seulement dans ce cas), un goto correspond directement à un concept dans le domaine problématique, passant d'un état à un autre état spécifié, où l'état actuel est représenté par le bloc de code en cours d'exécution. .

D'un autre côté, une machine à états finis explicite peut également être implémentée avec une instruction switch dans une boucle. Cela présente l'avantage que chaque état commence au même endroit dans le code, ce qui peut être utile pour le débogage, par exemple.

L'utilisation principale d'un goto dans un langage raisonnablement moderne (celui qui prend en charge if / else et les boucles) est de simuler une construction de flux de contrôle qui manque dans le langage.

Keith Thompson
la source
5
C'est en fait la meilleure réponse à ce jour - plutôt surprenant, pour une question qui a un an et demi. :-)
Konrad Rudolph
1
+1 à "la meilleure réponse à ce jour". --- "L'utilisation principale d'un goto dans un langage raisonnablement moderne (qui prend en charge if / else et les boucles) est de simuler une construction de flux de contrôle qui manque dans le langage."
Dave Dopson
Dans la machine d'état switch-statement, chaque écriture sur la valeur de contrôle est vraiment un goto. Les SSSM sont souvent de bonnes structures à utiliser, car ils séparent l'état SM de la construction d'exécution, mais ils n'éliminent pas vraiment les "gotos".
supercat
2
"Toutes choses étant égales par ailleurs, le code dont la structure reflète celle du domaine problématique a tendance à être plus facile à lire et à maintenir." Je le sais complètement depuis longtemps, mais il me manquait les mots pour le communiquer si précisément d'une manière que les autres programmeurs peuvent et vont réellement comprendre. Merci pour ça.
Wildcard
Le «théorème du programme structuré» m'amuse, car il tend à démontrer clairement que l'utilisation d'instructions de programmation structurée pour émuler un goto est beaucoup moins lisible que le simple fait d'utiliser un goto!
11

Cela dépend sûrement du langage de programmation. La principale raison gotoa été controversée en raison de ses effets néfastes qui surviennent lorsque le compilateur vous permet de l'utiliser trop généreusement. Des problèmes peuvent survenir, par exemple, s'il vous permet d'utiliser gotode telle manière que vous pouvez maintenant accéder à une variable non initialisée, ou pire, pour sauter dans une autre méthode et jouer avec la pile d'appels. Il devrait être de la responsabilité du compilateur de refuser le flux de contrôle absurde.

Java a tenté de «résoudre» ce problème en l'interdisant gotoentièrement. Cependant, Java vous permet d'utiliser à l' returnintérieur d'un finallybloc et donc d'avaler une exception par inadvertance. Le même problème est toujours là: le compilateur ne fait pas son boulot. La suppression gotode la langue ne l'a pas corrigé.

En C #, gotoest aussi sûr que break, continue, try/catch/finallyet return. Il ne vous permet pas d'utiliser des variables non initialisées, il ne vous permet pas de sortir d'un bloc finally, etc. Le compilateur se plaindra. C'est parce qu'il résout le vrai problème, qui est comme je l'ai dit le flux de contrôle absurde. goton'annule pas comme par magie l'analyse d'affectation définitive et d'autres vérifications raisonnables du compilateur.

Timwi
la source
Votre point est que C # goton'est pas mal? Si c'est le cas, c'est le cas pour tous les langages modernes couramment utilisés avec une gotoconstruction, pas seulement C # ...
lvella
9

Oui. Lorsque vos boucles sont imbriquées à plusieurs niveaux, gotoc'est le seul moyen de sortir élégamment d'une boucle intérieure. L'autre option consiste à définir un indicateur et à sortir de chaque boucle si cet indicateur remplit une condition. C'est vraiment moche et assez sujet aux erreurs. Dans ces cas, gotoc'est tout simplement mieux.

Bien sûr, la breakdéclaration étiquetée de Java fait la même chose, mais sans vous permettre de sauter à un point arbitraire dans le code, ce qui résout proprement le problème sans autoriser les choses qui font le gotomal.

Chinmay Kanchi
la source
Quelqu'un veut-il expliquer le downvote? C'était une réponse parfaitement valable à la question ...
Chinmay Kanchi
4
goto n'est jamais une réponse élégante. Lorsque vos boucles sont imbriquées à plusieurs niveaux, votre problème est que vous n'avez pas réussi à architecturer votre code pour éviter les boucles multinestérifiées. Les appels de procédure NE SONT PAS l'ennemi. (Lire les articles "Lambda: The Ultimate ...".) Utiliser un goto pour sortir des boucles imbriquées à plusieurs niveaux est de mettre un pansement sur une fracture multiple ouverte: cela peut être simple mais ce n'est pas le bon répondre.
John R. Strohm
6
Et bien sûr, il n'y a jamais de cas étrange où des boucles profondément imbriquées sont nécessaires? Être un bon programmeur ne consiste pas seulement à suivre les règles, mais aussi à savoir quand les enfreindre.
Chinmay Kanchi
2
@ JohnR.Strohm Ne dites jamais jamais. Si vous utilisez des termes comme « jamais » dans la programmation, vous avez clairement ne sont pas traités cas d'angle, l' optimisation des ressources, contraintes, etc. profondément boucles imbriquées, goto est très bien en effet le plus propre, façon élégante de sortir des boucles. Peu importe combien vous avez refactorisé votre code.
Sujay Phadke
@ JohnR.Strohm: Deuxième fonctionnalité la plus manquée lors du passage de VB.NET à C #: la Doboucle. Pourquoi? Parce que je peux changer la boucle qui a besoin d'une rupture profonde en une Doboucle et taper Exit Doet il n'y a pas GoTo. Les boucles imbriquées se produisent tout le temps. Briser les piles se produit environ% du temps.
Joshua
7

La plupart du découragement vient d'une sorte de "religion" qui a été créée autour de Dieu Djikstra qui était convaincante au début des années 60 à propos de son pouvoir aveugle de:

  • sauter n'importe où dans n'importe quel bloc de code
    • fonction non exécutée depuis le début
    • boucles non exécutées depuis le début
    • initialisation de variable ignorée
  • sauter loin de n'importe quel bloc de code sans aucun nettoyage possible.

Cela n'a plus rien à voir avec l' gotoénoncé des langages modernes, dont l'existence est simplement due à soutenir la création de structures de code autres que celles fournies par le langage.

En particulier, le premier point principal ci-dessus est plus autorisé et le second est nettoyé (si vous gotosortez d'un bloc, la pile est déroulée correctement et tous les destructeurs appropriés sont appelés)

Vous pouvez vous référer à cette réponse pour avoir une idée de la façon dont même le code n'utilisant pas goto peut être illisible. Le problème ne vient pas de lui-même, mais de sa mauvaise utilisation.

Je peux écrire un programme entier sans utiliser if, juste for. Bien sûr, ce ne sera pas bien lisible, un look maladroit et inutilement compliqué.

Mais le problème n'est pas for. C'est moi.

Des choses comme break, continue, throw, bool needed=true; while(needed) {...}, etc. sont faisant remarquer plus de Masquerade goto pour échapper à l' écart des cimeterres des zélotes Djikstrarian, que -50 ans après l'invention de laguages- modernes veulent toujours leurs prisonniers. Ils ont oublié de quoi Djikstra parlait, ils ne se souviennent que du titre de sa note (GOTO considéré comme nuisible, et ce n'était même pas son nouveau titre: il a été modifié par l'éditeur) et blâmer et bash, bash et blâmer chaque contructure ayant ces 4 lettre placée dans l'ordre.

Nous sommes en 2011: il est temps de comprendre que le gotofait de tenir compte de la GOTOdéclaration de Djikstra était convaincant.

Emilio Garavaglia
la source
1
"Des choses comme pause, continuer, lancer, bool nécessaire = vrai; tandis que (nécessaire) {...}, etc. notent plus que masquerade goto" Je ne suis pas d'accord avec cela. Je préférerais que "des choses comme les pauses, etc. soient limitées ", ou quelque chose comme ça. Le principal problème avec goto est qu'il est généralement trop puissant. Dans la mesure où le goto actuel est moins puissant que celui de Dijkstra, votre réponse me convient.
Muhammad Alkarouri
2
@MuhammadAlkarouri: Je suis totalement d'accord. Vous venez de trouver une meilleure formulation pour exprimer exactement mon concept. Mon point est que ces restrictions peuvent parfois ne pas être applicables et le type de restriction dont vous avez besoin n'est pas dans la langue. Ensuite, une chose plus «puissante», moins spécialisée, vous permet de contourner ce problème. Par exemple, Java break nn'est pas disponible en C ++, donc goto escape, même s'il escapen'est pas nécessaire qu'il soit juste sorti de la boucle n-ple.
Emilio Garavaglia
4

Le goto impair ici ou là, tant qu'il est local à une fonction, nuit rarement de manière significative à la lisibilité. Il en profite souvent en attirant l'attention sur le fait qu'il se passe quelque chose d'inhabituel dans ce code qui nécessite l'utilisation d'une structure de contrôle quelque peu inhabituelle.

Si les gotos (locaux) nuisent considérablement à la lisibilité, cela signifie généralement que la fonction contenant le goto est devenue trop complexe.

Le dernier goto que j'ai mis dans un morceau de code C était de construire une paire de boucles de verrouillage. Il ne rentre pas dans la définition normale de l'utilisation «acceptable», mais la fonction est finalement devenue beaucoup plus petite et plus claire. Pour éviter le goto, il aurait fallu une violation particulièrement désordonnée de DRY.

blucz
la source
1
La réponse à cette question est très clairement énoncée dans l'ancien slogan "Lay's Potato Chips": "Je parie que vous ne pouvez pas en manger un seul". Dans votre exemple, vous avez deux boucles «imbriquées» (quoi que cela signifie, et je parie que ce n'est pas joli), et le goto vous a donné un bon marché. Qu'en est-il du gars de la maintenance, qui doit modifier ce code après avoir été heurté par un bus? Le goto va-t-il rendre sa vie plus facile ou plus difficile? Ces deux boucles doivent-elles être repensées?
John R. Strohm
3

Je pense que toute cette question a consisté à aboyer le mauvais arbre.

GOTO en tant que tel ne me semble pas problématique, mais il s'agit plutôt très souvent d'un symptôme d'un véritable péché: le code spaghetti.

Si le GOTO provoque un croisement majeur des lignes de contrôle de flux, alors c'est mauvais, point final. S'il ne traverse aucune ligne de contrôle de débit, il est inoffensif. Dans la zone grise entre les deux, nous avons des choses comme les renflouements de boucle, il y a encore des langages qui n'ont pas ajouté de constructions qui couvrent tous les cas gris légitimes.

Le seul cas où je me suis retrouvé à l'utiliser effectivement depuis de nombreuses années est le cas de la boucle où le point de décision est au milieu de la boucle. Vous vous retrouvez avec un code en double, un drapeau ou un GOTO. Je trouve que la solution GOTO est la meilleure des trois. Il n'y a pas de croisement de lignes de contrôle de flux ici, c'est inoffensif.

Loren Pechtel
la source
Le cas auquel vous faites allusion est parfois surnommé «boucle et demie» ou «N plus une demi-boucle» et c'est un cas d'utilisation célèbre pour les gotos qui sautent dans une boucle (si la langue le permet; depuis l'autre cas, sauter hors de la boucle au milieu, est généralement trivial sans goto).
Konrad Rudolph
(Hélas, la plupart des langues interdisent de sauter dans une boucle.)
Konrad Rudolph
@KonradRudolph: Si vous implémentez la "boucle" avec GOTO, il n'y a pas d'autre structure de boucle dans laquelle sauter.
Loren Pechtel
1
Je pense gotoque crier LE FLUX DE CONTRÔLE ICI NE CONVIENT PAS AUX MODÈLES DE PROGRAMMATION STRUCTURÉS NORMAUX . Parce que les flux de contrôle qui correspondent à des modèles de programmation structurés sont plus faciles à raisonner que ceux qui ne le font pas, on devrait essayer d'utiliser ces modèles lorsque cela est possible; si le code correspond à de tels modèles, il ne faut pas crier que non. D'un autre côté, dans les cas où le code ne correspond pas vraiment à de tels modèles, il peut être préférable de le crier que de prétendre que le code convient quand il ne le fait pas vraiment.
supercat
1

Oui, le goto peut être utilisé pour bénéficier de l'expérience du développeur: http://adamjonrichardson.com/2012/02/06/long-live-the-goto-statement/

Cependant, comme pour tout outil puissant (pointeurs, héritage multiple, etc.), il faut être discipliné en l'utilisant. L'exemple fourni dans le lien utilise PHP, ce qui limite l'utilisation de la construction goto à la même fonction / méthode et désactive la possibilité de sauter dans un nouveau bloc de contrôle (par exemple, boucle, instruction switch, etc.)

AdamJonR
la source
1

Dépend de la langue. Il est encore largement utilisé dans la programmation Cobol, par exemple. J'ai également travaillé sur un appareil Barionet 50, dont le langage de programmation du micrologiciel est un ancien dialecte BASIC qui, bien sûr, vous oblige à utiliser Goto.

user16764
la source
0

Je dirais non. Si vous trouvez la nécessité d'utiliser GOTO, je parie qu'il est nécessaire de repenser le code.

Walter
la source
3
Peut-être, mais vous n'avez fourni aucun raisonnement
Casebash
Dijkstra a fourni le raisonnement, en détail, il y a des décennies.
John R. Strohm
2
Juste Dogma. Pas de résons. Remarque: Djikstra n'est PAS PLUS UNE RÉSON VALIDE: il faisait référence à la déclaration BASIC ou FORTRAN GOTO du début des années 60. Ils n'ont rien à voir avec les gots-s vraiment anémiques (par rapport à la puissance de ceux-ci) d'aujourd'hui.
Emilio Garavaglia
0

gotopeut être utile lors du portage du code assembleur hérité vers C. Dans un premier temps, une conversion instruction par instruction en C, en gotoremplacement de l' branchinstruction assembleur , peut permettre un portage très rapide.

Graham Borland
la source
-1

Je dirais non. Ils doivent toujours être remplacés par un outil plus spécifique au problème que le goto est utilisé pour résoudre (sauf s'il n'est pas disponible dans votre langue). Par exemple, les instructions break et les exceptions résolvent les problèmes précédemment résolus de fuite de boucle et de gestion des erreurs.

Fishtoaster
la source
Je dirais que lorsque les langues ont ces deux caractéristiques, elles gotosont généralement absentes de ces langues.
Chinmay Kanchi
Mais est-ce parce qu'ils n'en ont pas besoin ou parce que les gens pensent qu'ils n'en ont pas besoin?
Michael K
Vos opinions sont fanatiques. Il y a de nombreux cas où gotola chose la plus proche de la sémantique du domaine très problématique, tout le reste serait un sale hack.
SK-logic
@ SK-logic: Je ne pense pas que le mot "sectaire" signifie ce que vous pensez qu'il signifie.
Keith Thompson
1
@Keith, "intolérément dévoué à ses propres opinions et préjugés" - c'est ce que j'observe constamment ici, avec ce lot aux yeux bandés. "Devrait toujours être remplacé" sont au moins les mots des fanatiques. Rien à voir avec une opinion juste et saine, mais juste une pure bigoterie. Ce "toujours" ne laisse aucune place à une autre opinion, voyez-vous?
SK-logic