Warum hat Go eine "goto" -Anweisung?

110

Ich war überrascht, dass Go eine "goto" -Aussage hat. Mir wurde immer beigebracht, dass 'goto'-Aussagen der Vergangenheit angehören und böse sind, da sie den tatsächlichen Ablauf eines Programms verschließen und dass Funktionen oder Methoden immer eine bessere Möglichkeit sind, den Ablauf zu steuern.

Mir muss etwas fehlen. Warum hat Google es aufgenommen?

Schaden
quelle
5
Es gibt Zeiten, in denen Sie wirklich eine goto-Erklärung benötigen. Goto's sind nur dann böse, wenn sie wahllos verwendet werden. Zum Beispiel, wenn es sehr schwierig, wenn nicht unmöglich ist, einen Parser für endliche Zustandsmaschinen ohne goto-Anweisungen zu schreiben.
Xbonez
5
Es ist nicht spezifisch für Go, aber für eine gute Diskussion darüber, warum Sprachen die Aussage beibehalten, und um Argumente gegen ihre Verwendung zu sehen, lesen Sie diesen Beitrag . In der Frage sind einige gute Referenzen verlinkt. Edit: hier ist noch einer .
16.
3
Um zu verhindern, dass das OP die bereitgestellten SO-Diskussionen durchläuft, finden Sie hier die Diskussion zu LKML, in der zusammengefasst wird, warum gotodies in bestimmten Fällen nützlich ist. Lesen Sie nach dem Studium von @ Kissakis Antwort.
Kostix
1
Verwandte: programmers.stackexchange.com/q/566/33478 (und siehe meine Antwort ).
Keith Thompson
Es ist nützlich, ein Fortsetzungsmuster zu implementieren, bei dem Sie den Stapel speichern und dann wieder dorthin zurückkehren, wo Sie waren, als Sie fortfahren möchten.
Justin Dennahower

Antworten:

78

Wenn wir den Quellcode der Go-Standardbibliothek überprüfen, können wir sehen, wo gotos tatsächlich gut angewendet wird.

In der math/gamma.goDatei wird beispielsweise die folgende gotoAnweisung verwendet :

  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)
}

Die gotoin diesem Fall erspart uns die Einführung ein anderes (boolean) Variable nur für Steuerstrom verwendet wird , am Ende geprüft. In diesem Fallgoto macht die Anweisung den Code tatsächlich besser lesbar und leichter zu befolgen (ganz im Gegensatz zu dem von gotoIhnen erwähnten Argument gegen Sie).

Beachten Sie auch, dass die gotoAnweisung einen sehr spezifischen Anwendungsfall hat. Die Sprachspezifikation auf goto besagt, dass möglicherweise nicht über Variablen gesprungen wird, die in den Gültigkeitsbereich fallen (deklariert werden), und dass sie möglicherweise nicht in andere (Code-) Blöcke springen.

Kissaki
quelle
69
Warum führen Sie in Ihrem Beispiel nicht einfach eine Funktion ein small(x,z), die stattdessen aufgerufen werden soll? Auf diese Weise müssen wir nicht darüber nachdenken, auf welche Variablen im small:Label zugegriffen werden kann. Ich vermute, der Grund dafür ist, dass go immer noch bestimmte Arten von Inlining-Unterstützung im Compiler fehlt.
Thomas Ahle
5
@Jessta: Dafür haben wir Sichtbarkeit und Spielraum, oder?
Thomas Ahle
4
@ ogc-nick Entschuldigung, ich war mir nicht sicher, ich meinte, die Funktionen können in dem Bereich deklariert werden, in dem sie benötigt werden, sodass sie für Code, der sie nicht benötigt, nicht sichtbar sind. Ich habe nicht über Goto und Scope gesprochen.
Thomas Ahle
4
@MosheRevah Der Code, auf den verwiesen wird, ist nicht für die Lesbarkeit optimiert. Es ist für die Rohleistung optimiert und verwendet ein Goto, das 22 Zeilen innerhalb einer einzelnen Funktion umfasst. (Und Thomas Ahles Vorschlag ist für mein Auge noch besser lesbar.)
joel.neely
30

Springen ist eine gute Idee, wenn keine der integrierten Steuerungsfunktionen genau das tut, was Sie wollen, und wenn Sie mit einem goto ausdrücken können, was Sie wollen. (In diesen Fällen ist es in einigen Sprachen eine Schande, wenn Sie kein goto haben. Am Ende missbrauchen Sie eine Kontrollfunktion, verwenden boolesche Flags oder verwenden andere Lösungen, die schlechter sind als goto.)

Wenn eine andere Steuerungsfunktion (die auf ziemlich offensichtliche Weise verwendet wird) das tun kann, was Sie wollen, sollten Sie sie vorziehen, um zu gehen. Wenn nicht, sei mutig und benutze goto!

Schließlich ist es erwähnenswert, dass Go's goto einige Einschränkungen hat, um einige obskure Fehler zu vermeiden. Siehe diese Einschränkungen in der Spezifikation.

Sonia
quelle
7

Die Aussagen von Goto wurden seit der Ära des Spaghetti-Codes in den 60er und 70er Jahren stark diskreditiert . Damals gab es eine sehr schlechte bis gar keine Softwareentwicklungsmethode. Goto sind jedoch nicht von Natur aus böse, sondern können natürlich von faulen oder ungelernten Programmierern missbraucht und missbraucht werden. Viele Probleme mit missbrauchten Gotos können mit Entwicklungsprozessen wie Teamcode-Überprüfungen gelöst werden.

gotosind Sprünge in der gleichen technischen Weise wie continue, breakund return. Man könnte argumentieren, dass dies Aussagen sind, die auf die gleiche Weise böse sind, aber sie sind es nicht.

Warum das Go-Team Gotos aufgenommen hat, liegt wahrscheinlich daran, dass es sich um ein allgemeines Grundelement für die Flusskontrolle handelt. Außerdem kamen sie hoffentlich zu dem Schluss, dass der Geltungsbereich von Go ausschließt, dass eine idiotensichere Sprache nicht missbraucht werden kann.

Gustav
quelle
continue,, breakund returnunterscheiden sich in einem Schlüssel sehr stark: Sie geben nur "den einschließenden Bereich verlassen" an. Sie ermutigen den Entwickler nicht nur, sondern verlangen ausdrücklich, dass er die Struktur seines Codes berücksichtigt und sich auf strukturierte Programmierprimitive (für Schleifen, Funktionen und switch-Anweisungen) verlässt. Die einzige Rettung von gotoAnweisungen besteht darin, dass Sie Assembler in eine HLL schreiben können, wenn der Optimierer des Compilers der Aufgabe nicht gewachsen ist. Dies geht jedoch zu Lasten der Lesbarkeit und Wartbarkeit.
Parthian Shot
Der Grund dafür ist so überraschend in gehen zu finden , ist , dass in jedem anderen Fall , in dem die golang Entwickler zwischen strukturiertem Programmierparadigma hatten die Wahl und „außergewöhnliche Flusssteuerung“ / Befehlszeiger Manipulationen wie setjmp, longjmp, goto, und try / except / finallysie zu err auf der Seite gewählt Vorsicht. gotofwict ist die einzige Zustimmung zum Kontrollfluss vor der "strukturierten Programmierung".
Parthian Shot