Ich habe immer gewusst, dass goto
das etwas Schlimmes ist, eingeschlossen in einen Keller, der niemals für immer zu sehen ist, aber ich bin heute auf ein Codebeispiel gestoßen, dessen Verwendung absolut sinnvoll ist goto
.
Ich habe eine IP, bei der ich überprüfen muss, ob sie in einer Liste von IPs enthalten ist, und dann mit dem Code fortfahren muss. Andernfalls wird eine Ausnahme ausgelöst.
<?php
$ip = '192.168.1.5';
$ips = [
'192.168.1.3',
'192.168.1.4',
'192.168.1.5',
];
foreach ($ips as $i) {
if ($ip === $i) {
goto allowed;
}
}
throw new Exception('Not allowed');
allowed:
...
Wenn ich es nicht benutze, muss goto
ich eine Variable wie verwenden
$allowed = false;
foreach ($ips as $i) {
if ($ip === $i) {
$allowed = true;
break;
}
}
if (!$allowed) {
throw new Exception('Not allowed');
}
Meine Frage ist, was ist so schlimm daran, goto
wenn es für so offensichtliche und imo relevante Fälle verwendet wird?
Antworten:
GOTO selbst ist kein unmittelbares Problem, es sind die impliziten Zustandsmaschinen, die die Leute dazu neigen, damit zu implementieren. In Ihrem Fall möchten Sie einen Code, der überprüft, ob die IP-Adresse in der Liste der zulässigen Adressen enthalten ist
Ihr Code möchte also eine Bedingung überprüfen. Der Algorithmus zur Implementierung dieser Prüfung sollte hier keine Rolle spielen, da die Prüfung im mentalen Bereich Ihres Hauptprogramms atomar ist. Das ist wie es sein sollte.
Aber wenn Sie den Code, der die Prüfung durchführt, in Ihr Hauptprogramm einfügen, verlieren Sie diesen. Sie führen einen veränderlichen Zustand ein, entweder explizit:
wo
$list_contains_ip
ist deine einzige Zustandsvariable oder implizit:Wie Sie sehen, enthält das GOTO-Konstrukt eine nicht deklarierte Statusvariable. Das ist per se kein Problem, aber diese Zustandsvariablen sind wie Kieselsteine: Wenn man einen trägt, ist es nicht schwer, eine Tasche voll davon zu tragen, wird man schwitzen. Ihr Code bleibt nicht gleich: Im nächsten Monat werden Sie aufgefordert, zwischen privaten und öffentlichen Adressen zu unterscheiden. Im darauffolgenden Monat muss Ihr Code IP-Bereiche unterstützen. Nächstes Jahr werden Sie gefragt, ob Sie IPv6-Adressen unterstützen möchten. In kürzester Zeit sieht Ihr Code so aus:
Und wer diesen Code debuggen muss, wird Sie und Ihre Kinder verfluchen.
Dijkstra drückt es so aus:
Und deshalb gilt GOTO als schädlich.
quelle
$list_contains_ip = false;
Aussage scheint falschEs gibt einige legitime Anwendungsfälle für
GOTO
. Zum Beispiel zur Fehlerbehandlung und -bereinigung in C oder zur Implementierung einiger Arten von Zustandsautomaten. Dies ist jedoch nicht einer dieser Fälle. Das zweite Beispiel ist meiner Meinung nach lesbarer, aber noch lesbarer wäre es, die Schleife in eine separate Funktion zu extrahieren und dann zurückzukehren, wenn Sie eine Übereinstimmung finden. Noch besser wäre (im Pseudocode kenne ich die genaue Syntax nicht):Also, was ist daran so schlimm
GOTO
? Strukturierte Programmierung verwendet Funktionen und Kontrollstrukturen, um den Code so zu organisieren, dass die syntaktische Struktur die logische Struktur widerspiegelt. Wenn etwas nur bedingt ausgeführt wird, wird es in einem bedingten Anweisungsblock angezeigt. Wenn etwas in einer Schleife ausgeführt wird, erscheint es in einem Schleifenblock.GOTO
ermöglicht es Ihnen, die syntaktische Struktur zu umgehen, indem Sie willkürlich herumspringen, wodurch es viel schwieriger wird, dem Code zu folgen.Natürlich, wenn Sie keine andere Wahl haben
GOTO
, aber wenn der gleiche Effekt mit Funktionen und Kontrollstrukturen erzielt werden kann, ist dies vorzuziehen.quelle
it's easier to write good optimizing compilers for "structured" languages.
Sorgfalt ausarbeiten? Als Gegenbeispiel führten die Rust-Leute MIR ein , eine Zwischenrepräsentation im Compiler, die speziell Schleifen, Fortsetzungen, Unterbrechungen und dergleichen durch gotos ersetzt, weil es einfacher zu überprüfen und zu optimieren ist.goto
mit Flaggen und Bedingungen behafteten Ratten-Nestes (wie im zweiten Beispiel des OP) viel weniger lesbar als die Juts- Emulation mit einemgoto
.Wie andere gesagt haben, liegt das Problem nicht bei sich
goto
selbst; Das Problem besteht daringoto
, wie die Benutzer Code verwenden und wie er das Verstehen und Verwalten von Code erschwert.Nehmen Sie das folgende Codefragment an:
Für welchen Wert wird gedruckt
i
? Wann wird es gedruckt? Bis Sie für jede Instanzgoto label
in Ihrer Funktion verantwortlich sind, können Sie nicht wissen. Das einfache Vorhandensein dieses Etiketts zerstört Ihre Fähigkeit, Code durch einfache Überprüfung zu debuggen. Für kleine Funktionen mit ein oder zwei Zweigen kein großes Problem. Für nicht kleine Funktionen ...In den frühen 90er Jahren erhielten wir einen Stapel C-Code, der eine 3D-Grafikanzeige ansteuerte und anzeigte, dass er schneller laufen soll. Es waren nur etwa 5000 Codezeilen, aber alles war drin
main
, und der Autor verwendete ungefähr 15goto
s, die in beide Richtungen verzweigten. Das war anfangs ein schlechter Code, aber das Vorhandensein diesergoto
machte es noch viel schlimmer. Mein Kollege brauchte ungefähr 2 Wochen, um den Kontrollfluss zu ergründen. Noch besser ist , diesegoto
führte s im Code so dicht mit sich selbst gekoppelt , dass wir nicht konnten keine Veränderungen etwas , ohne brechen machen.Wir haben versucht, mit der Optimierung der Stufe 1 zu kompilieren, und der Compiler hat den gesamten verfügbaren Arbeitsspeicher, dann den gesamten verfügbaren Swap-Speicher aufgebraucht und dann das System in Panik versetzt (was wahrscheinlich nichts mit den
goto
s selbst zu tun hat , aber ich mag es, diese Anekdote da draußen zu werfen).Am Ende gaben wir dem Kunden zwei Möglichkeiten - lassen Sie uns das Ganze von Grund auf neu schreiben oder schnellere Hardware kaufen.
Sie kauften schnellere Hardware.
Bodes Regeln für die Verwendung von
goto
:if
oderfor
oderwhile
Anweisung verzweigen );goto
anstelle einer Kontrollstruktur verwendenEs gibt Fälle , in denen eine
goto
ist die richtige Antwort, aber sie sind selten (Ausbruch aus einer tief verschachtelten Schleife ist über den Ort nur ich es verwenden würde).BEARBEITEN
Im Folgenden finden Sie einen der wenigen gültigen Anwendungsfälle für
goto
. Angenommen, wir haben die folgende Funktion:Jetzt haben wir ein Problem - was ist, wenn einer der
malloc
Anrufe auf halbem Weg fehlschlägt? So unwahrscheinlich ein Ereignis auch sein mag, wir möchten weder ein teilweise zugewiesenes Array zurückgeben noch die Funktion mit einem Fehler beenden. Wir möchten nach uns selbst aufräumen und teilweise zugewiesenen Speicher freigeben. In einer Sprache, die bei einer fehlerhaften Zuweisung eine Ausnahme auslöst, ist dies recht einfach: Sie schreiben einfach einen Ausnahmehandler, um die bereits zugewiesenen Elemente freizugeben.In C haben Sie keine strukturierte Ausnahmebehandlung. Sie müssen den Rückgabewert jedes
malloc
Aufrufs überprüfen und die entsprechende Aktion ausführen.Können wir das tun, ohne zu verwenden
goto
? Natürlich können wir - es erfordert nur ein wenig zusätzliche Buchhaltung (und in der Praxis ist das der Weg, den ich einschlagen würde). Wenn Sie jedoch nach Orten suchen, an denen die Verwendung von agoto
nicht sofort ein Zeichen für eine schlechte Vorgehensweise oder ein schlechtes Design ist, ist dies einer der wenigen.quelle
goto
auch nach diesen Regeln nicht verwenden - ich würde es überhaupt nicht verwenden, es sei denn, es ist die einzige Form der Verzweigung, die in der Sprache verfügbar ist -, aber ich kann sehen, dass es kein zu großer Albtraum zum Debuggen wäregoto
's, wenn sie nach diesen Regeln geschrieben wurden.GOTO
, einschließlich Fortrans (in) berühmten zugewiesenen und arithmetischen Gots, verwendete, wenn ich mich recht entsinne.return
,break
,continue
Undthrow
/catch
sind alle wesentlichen gotos - sie alle die Steuerung an ein anderes Stück Code und alle mit gotos umgesetzt werden könnten - in der Tat ich in einem Schulprojekt so einmal tat, ein PASCAL Lehrer sagte , wie viel besser Pascal war als Grund wegen der Struktur ... also musste ich gegenteilig sein ...Das Wichtigste am Software-Engineering (ich werde diesen Begriff über Codierung verwenden, um auf eine Situation hinzuweisen, in der Sie von jemandem bezahlt werden, um zusammen mit anderen Ingenieuren eine Codebasis zu erstellen, die fortlaufend verbessert und gewartet werden muss) ist, Code lesbar zu machen. - Etwas zu tun ist fast zweitrangig. Ihr Code wird nur einmal geschrieben, aber in den meisten Fällen verbringen die Leute Tage und Wochen damit, ihn zu überarbeiten / neu zu lernen, zu verbessern und zu reparieren - und jedes Mal, wenn sie (oder Sie) von vorne anfangen müssen und versuchen, sich zu erinnern / herauszufinden dein Code.
Die meisten Funktionen, die Sprachen im Laufe der Jahre hinzugefügt wurden, dienen dazu, die Wartung der Software zu vereinfachen und das Schreiben zu vereinfachen (obwohl einige Sprachen in diese Richtung gehen - sie verursachen häufig langfristige Probleme ...).
Im Vergleich zu ähnlichen Flow-Control-Anweisungen können GOTOs von ihrer besten Seite fast genauso einfach befolgt werden (eine einzige Goto, die in einem von Ihnen vorgeschlagenen Fall verwendet wird) und bei Missbrauch ein Albtraum sein - und sie können sehr leicht missbraucht werden ...
Nachdem wir uns ein paar Jahre lang mit Spaghetti-Alpträumen beschäftigt hatten, sagten wir nur "Nein". Als Community werden wir das nicht akzeptieren - zu viele Leute vermasseln es, wenn ihnen ein wenig Spielraum eingeräumt wird - das ist wirklich das einzige Problem bei ihnen. Du könntest sie benutzen ... aber selbst wenn es der perfekte Fall ist, wird der nächste Mann annehmen, dass du ein schrecklicher Programmierer bist, weil du die Geschichte der Community nicht verstehst.
Viele andere Strukturen wurden entwickelt, um Ihren Code verständlicher zu machen: Funktionen, Objekte, Gültigkeitsbereiche, Verkapselung, Kommentare (!) Sowie die wichtigsten Muster / Prozesse wie "DRY" (Verhinderung von Duplikaten) und "YAGNI". (Reduzieren von Überverallgemeinerung / Komplikation von Code) - alles wirklich nur für den NÄCHSTEN, um Ihren Code zu lesen.
quelle
return
break
continue
throw
catch
GOTO
ist ein Werkzeug. Es kann zum Guten oder zum Bösen eingesetzt werden.In den schlechten alten Zeiten war es mit FORTRAN und BASIC fast das einzige Werkzeug.
Wenn Sie sich den Code von damals ansehen
GOTO
, müssen Sie herausfinden, warum er dort ist. Es kann Teil einer Standardsprache sein, die Sie schnell verstehen können ... oder Teil einer albtraumhaften Kontrollstruktur, die es niemals hätte geben dürfen. Sie wissen es nicht, bis Sie nachgesehen haben, und es ist leicht, sich zu irren.Die Menschen wollten etwas Besseres und es wurden fortschrittlichere Kontrollstrukturen erfunden. Dies betraf die meisten Anwendungsfälle, und Menschen, die von Bösen verbrannt wurden,
GOTO
wollten sie vollständig verbieten.Ironischerweise
GOTO
ist es nicht so schlimm, wenn es selten ist. Wenn Sie eines sehen, wissen Sie , dass etwas Besonderes los ist, und es ist einfach, das entsprechende Etikett zu finden, da es das einzige Etikett in der Nähe ist.Schneller Vorlauf bis heute. Sie unterrichten als Dozent Programmierung. Sie könnten sagen "In den meisten Fällen sollten Sie die erweiterten neuen Konstrukte verwenden, aber in einigen Fällen kann eine einfache
GOTO
besser lesbar sein." Die Studenten werden das nicht verstehen. Sie werden missbrauchenGOTO
, um unlesbaren Code zu erstellen.Stattdessen sagst du "
GOTO
böse.GOTO
Böse.GOTO
Nicht bestandene Prüfung". Die Studenten werden verstehen , dass !quelle
Mit Ausnahme von
goto
sind alle Flow-Konstrukte in PHP (und den meisten Sprachen) hierarchisch gegliedert.Stellen Sie sich einen Code vor, der mit zusammengekniffenen Augen untersucht wird:
Unabhängig davon , welches Kontrollkonstrukt
foo
ist (if
,while
usw.) gibt es nur bestimmte erlaubt Aufträge füra
,b
undc
.Sie könnten
a
-b
-c
, odera
-c
oder sogara
-b
-b
-b
- habenc
. Aber man konnte nie habenb
-c
odera
-b
-a
-c
.... es sei denn du hast
goto
.goto
(insbesondere rückwärtsgoto
) kann so problematisch sein, dass es am besten ist, es einfach in Ruhe zu lassen und hierarchische, blockierte Flow-Konstrukte zu verwenden.goto
s haben einen Platz, aber meistens als Mikrooptimierungen in Low-Level-Sprachen. IMO, es gibt keinen guten Platz dafür in PHP.Zu Ihrer Information, der Beispielcode kann noch besser geschrieben werden als jeder Ihrer Vorschläge.
quelle
Bei niedrigen Sprachniveaus ist GOTO unvermeidlich. Auf hohem Niveau sollte dies jedoch vermieden werden (falls die Sprache dies unterstützt), da dies die Lesbarkeit von Programmen erschwert .
Alles läuft darauf hinaus, den Code schwerer lesbar zu machen. Hochsprachen sind erforderlich, um das Lesen von Code zu vereinfachen, als niedrigsprachige Sprachen wie Assembler oder C.
GOTO verursacht weder eine globale Erwärmung noch Armut in der Dritten Welt. Es erschwert nur das Lesen von Code.
Die meisten modernen Sprachen haben Kontrollstrukturen, die GOTO überflüssig machen. Einige wie Java haben es nicht einmal.
Tatsächlich stammt der Begriff Spaguetti-Code von verschlungenen , schwer zu verfolgenden Code-Ursachen durch unstrukturierte Verzweigungsstrukturen.
quelle
goto
kann Code lesefreudiger machen. Wenn Sie zusätzliche Variablen und verschachtelte Zweige gegenüber einem einzelnen Sprung erstellt haben, ist der Code viel schwieriger zu lesen und zu verstehen.goto
, sind keine Beispiele für verschlungenen Spaghetti-Code, sondern eher einfache Dinge, die oft Muster des Kontrollflusses in Sprachen emulieren, die nicht funktionieren Die Syntax dafür fehlt: Die beiden häufigsten Beispiele sind das Ausbrechen mehrerer Schleifen und das Beispiel im OP, ingoto
demfor
- implementiert wirdelse
.goto
eignet sich auch hervorragend für Dinge wie Ausnahmewiederholungsversuche, Rollbacks und Zustandsautomaten.An
goto
Aussagen selbst ist nichts auszusetzen . Das Falsche ist, dass einige der Leute die Aussage unangemessen verwenden.Zusätzlich zu dem, was JacquesB gesagt hat (Fehlerbehandlung in C), beenden Sie
goto
eine nicht verschachtelte Schleife, was Sie mit tun könnenbreak
. In diesem Fall verwenden Sie besserbreak
.Wenn Sie jedoch ein Nested-Loop-Szenario hätten,
goto
wäre die Verwendung eleganter / einfacher. Zum Beispiel:Das obige Beispiel ist für Ihr spezielles Problem nicht sinnvoll, da Sie keine verschachtelte Schleife benötigen. Aber ich hoffe, dass Sie nur den Teil der verschachtelten Schleife sehen.
Bonous point: Wenn Ihre IP-Liste klein ist, ist Ihre Methode in Ordnung. Wenn die Liste jedoch wächst, wissen Sie, dass Ihr Ansatz eine asymptotisch schlechteste Laufzeitkomplexität von O (n) aufweist . Wenn Ihre Liste wächst, möchten Sie möglicherweise eine andere Methode verwenden, mit der O (log n) (z. B. eine Baumstruktur) oder O (1) (eine Hash-Tabelle ohne Kollisionen) erzielt wird .
quelle
break
undcontinue
mit einer Nummer (um so viele Schleifenebenen zu unterbrechen / fortzusetzen) oder einer Bezeichnung (um die Schleife mit dieser Bezeichnung zu unterbrechen / fortzusetzen), wobei dies im Allgemeinen der Fall sein sollte vorzugsweisegoto
für verschachtelte Schleifen verwenden.Wahr. Ist mir egal
Wahr. Ist mir egal
Wahr. Ist mir egal
Wahr. Ich war jedoch der disziplinierte Coder. Ich habe gesehen, was mit dem Code im Laufe der Zeit passiert.
Goto
fängt gut an. Dann setzt der Drang zur Wiederverwendung von Code ein. Ziemlich bald befinde ich mich an einem Haltepunkt, ohne verdammte Ahnung zu haben, was auch nach Betrachtung des Programmstatus vor sich geht.Goto
macht es schwierig, über Code nachzudenken. Wir haben wirklich hart gearbeitet schaffenwhile
,do while
,for
,for each
switch
,subroutines
,functions
, und alles nur, weil mit diesen Sachen zu tunif
undgoto
auf dem Gehirn ist schwer.Also nein. Wir wollen nicht anschauen
goto
. Sicher, es ist lebendig und gut in der Binärdatei, aber wir müssen das nicht in der Quelle sehen. In der Tatif
fängt an, ein wenig wackelig auszusehen.quelle
Goto
lässt Sie ein Problem abstrahieren, indem Sie es zu einem anderen Codefehler machen.If
können Sie diese Abstraktion wählen. Es gibt bessere Möglichkeiten zum Abstrahieren und bessere Möglichkeiten zum Auswählen der Abstraktion. Polymorphismus für einen.Assemblersprachen haben normalerweise nur bedingte / unbedingte Sprünge (entspricht GOTO. Ältere Implementierungen von FORTRAN und BASIC hatten keine Steuerblockanweisungen, die über eine gezählte Iteration (die DO-Schleife) hinausgingen. Alle anderen Steuerflüsse bleiben IFs und GOTOs überlassen Diese Sprachen wurden durch eine numerisch gekennzeichnete Anweisung abgeschlossen, sodass Code, der für diese Sprachen geschrieben wurde, schwer zu befolgen war und häufig fehleranfällig war.
Um den Punkt zu unterstreichen, gibt es die facettenreich erfundene " COME FROM " -Anweisung.
Es ist praktisch nicht erforderlich, GOTO in Sprachen wie C, C ++, C #, PASCAL, Java usw. zu verwenden. Es können alternative Konstruktionen verwendet werden, die mit ziemlicher Sicherheit ebenso effizient und weitaus wartungsfreundlicher sind. Es ist wahr, dass ein GOTO in einer Quelldatei kein Problem sein wird. Das Problem ist, dass nicht viele erforderlich sind, um die Verfolgung einer Codeeinheit zu erschweren und die Wartung fehleranfällig zu machen. Deshalb ist die akzeptierte Weisheit, GOTO zu vermeiden, wann immer dies möglich ist.
Dieser Wikipedia-Artikel zur goto-Anweisung könnte hilfreich sein
quelle