Pourquoi Go a-t-il une instruction "goto"

110

J'ai été surpris de constater que Go a une déclaration «goto» . On m'a toujours appris que les déclarations «goto» sont une chose du passé et du mal car elles obstruent le flux réel d'un programme, et que les fonctions ou méthodes sont toujours un meilleur moyen de contrôler le flux.

J'ai dû louper quelque chose. Pourquoi Google l'a-t-il inclus?

nuire
la source
5
Il y a des moments où vous avez vraiment besoin d'une déclaration goto. Les Goto ne sont mauvais que lorsqu'ils sont utilisés sans discernement. Par exemple, s'il est très difficile, voire impossible, d'écrire un analyseur de machine à états finis sans instructions goto.
xbonez
5
Ce n'est pas spécifique à Go, mais pour une bonne discussion sur les raisons pour lesquelles les langues conservent la déclaration et pour voir les arguments contre son utilisation, consultez cet article . Il y a quelques bonnes références liées à la question. Edit: en voici un autre .
Cᴏʀʏ
3
Pour éviter que l'OP ne passe à travers les discussions SO fournies, voici la discussion sur LKML qui résume assez bien pourquoi gotoest utile dans certains cas. Lisez après avoir étudié la réponse de @ Kissaki.
kostix
1
Connexes: programmers.stackexchange.com/q/566/33478 (et voir ma réponse ).
Keith Thompson
Il est utile de mettre en œuvre un modèle de continuation, dans lequel vous enregistrez la pile, puis revenez à l'endroit où vous étiez lorsque vous souhaitez reprendre.
Justin Dennahower

Réponses:

78

Lorsque nous vérifions le code source de la bibliothèque standard Go, nous pouvons voir où les gotos sont réellement bien appliqués.

Par exemple, dans le math/gamma.gofichier, l' gotoinstruction est utilisée :

  for x < 0 {
    if x > -1e-09 {
      goto small
    }
    z = z / x
    x = x + 1
  }
  for x < 2 {
    if x < 1e-09 {
      goto small
    }
    z = z / x
    x = x + 1
  }

  if x == 2 {
    return z
  }

  x = x - 2
  p = (((((x*_gamP[0]+_gamP[1])*x+_gamP[2])*x+_gamP[3])*x+_gamP[4])*x+_gamP[5])*x + _gamP[6]
  q = ((((((x*_gamQ[0]+_gamQ[1])*x+_gamQ[2])*x+_gamQ[3])*x+_gamQ[4])*x+_gamQ[5])*x+_gamQ[6])*x + _gamQ[7]
  return z * p / q

small:
  if x == 0 {
    return Inf(1)
  }
  return z / ((1 + Euler*x) * x)
}

Le gotodans ce cas nous évite d'introduire une autre variable (booléenne) utilisée uniquement pour le flux de contrôle, vérifiée à la fin. Dans ce cas , l' gotoinstruction rend le code en fait meilleur à lire et à suivre plus facilement (tout à fait contrairement à l'argument contre gotovous mentionné).

Notez également que le goto déclaration a un cas d'utilisation très spécifique. La spécification du langage sur goto indique qu'il ne peut pas sauter par-dessus les variables entrant dans la portée (en cours de déclaration), et il ne peut pas sauter dans d'autres blocs (de code).

Kissaki
la source
69
Dans votre exemple, pourquoi ne pas simplement introduire une fonction small(x,z)à appeler à la place? De cette façon, nous n'avons pas à penser aux variables accessibles dans l' small:étiquette. Je soupçonne que la raison en est que go manque encore de certains types de support inlining dans le compilateur.
Thomas Ahle
5
@Jessta: C'est ce pour quoi nous avons de la visibilité et de la portée, non?
Thomas Ahle
6
@ThomasAhle Go ne permet pas gotode pointer vers une étiquette après l'introduction de nouvelles variables. L'exécution de l'instruction "goto" ne doit pas faire entrer dans la portée des variables qui n'étaient pas déjà dans la portée au point de goto.
km6zla
4
@ ogc-nick Désolé, je n'ai pas été clair, je voulais dire que les fonctions peuvent être déclarées dans la portée où elles sont nécessaires, donc elles ne sont pas visibles pour le code qui n'en a pas besoin. Je ne parlais pas de goto et de portée.
Thomas Ahle
4
@MosheRevah Le code référencé n'est pas optimisé pour la lisibilité. Il est optimisé pour les performances brutes, en utilisant un goto qui s'étend sur 22 lignes dans une seule fonction. (Et la proposition de Thomas Ahle est encore plus lisible à mes yeux.)
joel.neely
30

Goto est une bonne idée lorsqu'aucune des fonctionnalités de contrôle intégrées ne fait tout ce que vous voulez et lorsque vous pouvez exprimer ce que vous voulez avec un goto. (C'est dommage dans ces cas dans certaines langues lorsque vous n'avez pas de goto. Vous finissez par abuser de certaines fonctionnalités de contrôle, en utilisant des indicateurs booléens ou en utilisant d'autres solutions pires que goto.)

Si une autre fonction de contrôle (utilisée de manière raisonnablement évidente) peut faire ce que vous voulez, vous devriez l'utiliser de préférence à goto. Sinon, soyez audacieux et utilisez goto!

Enfin, il convient de noter que Go's goto a quelques restrictions conçues pour éviter certains bugs obscurs. Voir ces restrictions dans la spécification.

Sonia
la source
7

Les déclarations Goto ont reçu beaucoup de discrédit depuis l'ère du code Spaghetti dans les années 60 et 70. À l'époque, la méthodologie de développement de logiciels était très médiocre ou nulle. Cependant Goto n'est pas nativement mauvais mais peut bien sûr être mal utilisé et abusé par des programmeurs paresseux ou non qualifiés. De nombreux problèmes avec Gotos abusés peuvent être résolus avec des processus de développement tels que les révisions de code d'équipe.

gotosont des sauts de la même manière technique que continue, breaket return. On pourrait soutenir que ces déclarations sont mauvaises de la même manière, mais elles ne le sont pas.

La raison pour laquelle l'équipe Go a inclus Gotos est probablement due au fait qu'il s'agit d'une primitive de contrôle de flux commune. En outre, ils ont conclu, espérons-le, que la portée de Go exclut de rendre un langage sans danger pour les idiots impossible à abuser.

Gustav
la source
continue,, breaket returnsont très différents dans une clé particulière: ils spécifient seulement "quitter la portée englobante". Non seulement ils encouragent, mais exigent explicitement que le développeur considère la structure de son code et s'appuie sur des primitives de programmation structurées (pour les boucles, les fonctions et les instructions de commutation). La seule et unique grâce des gotoinstructions est qu'elles vous permettent d'écrire un assembly dans un HLL lorsque l'optimiseur du compilateur n'est pas à la hauteur de la tâche, mais cela se fait au détriment de la lisibilité et de la maintenabilité.
Tir parthe le
La raison pour laquelle cela est si surprenant de trouver dans vos déplacements est que, dans tous les cas où les développeurs de golang avaient le choix entre paradigme de programmation structuré et « contrôle de flux exceptionnel » / manipulations de pointeur d'instruction aiment setjmp, longjmp, gotoet try / except / finallyils ont choisi de pécher par excès de prudence. goto, fwict, est le seul acquiescement au flux de contrôle pré- "programmation structurée".
Tir parthe le