Was ist so schlecht an goto, wenn es für diese offensichtlichen und relevanten Fälle verwendet wird?

40

Ich habe immer gewusst, dass gotodas 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 gotoich 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, gotowenn es für so offensichtliche und imo relevante Fälle verwendet wird?

php_nub_qq
quelle
Kommentare sind nicht für längere Diskussionen gedacht. Diese Unterhaltung wurde in den Chat verschoben .
maple_shaft

Antworten:

120

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

if (!contains($ips, $ip)) throw new Exception('Not allowed');

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:

$list_contains_ip = undef;        # STATE: we don't know yet

foreach ($ips as $i) {
  if ($ip === $i) {
      $list_contains_ip = true;   # STATE: positive
      break;
  }
                                  # STATE: we still don't know yet, huh?                                                          
                                  # Well, then...
  $list_contains_ip = false;      # STATE: negative
}

if (!$list_contains_ip) {
  throw new Exception('Not allowed');
}

wo $list_contains_ipist deine einzige Zustandsvariable oder implizit:

                             # STATE: unknown
foreach ($ips as $i) {       # What are we checking here anyway?
  if ($ip === $i) {
    goto allowed;            # STATE: positive
  }
                             # STATE: unknown
}
                             # guess this means STATE: negative
throw new Exception('Not allowed');

allowed:                     # Guess we jumped over the trap door

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:

if ($ip =~ /:/) goto IP_V6;
if ($ip =~ /\//) goto IP_RANGE;
if ($ip =~ /^10\./) goto IP_IS_PRIVATE;

foreach ($ips as $i) { ... }

IP_IS_PRIVATE:
   foreach ($ip_priv as $i) { ... }

IP_V6:
   foreach ($ipv6 as $i) { ... }

IP_RANGE:
   # i don't even want to know how you'd implement that

ALLOWED:
   # Wait, is this code even correct?
   # There seems to be a bug in here.

Und wer diesen Code debuggen muss, wird Sie und Ihre Kinder verfluchen.

Dijkstra drückt es so aus:

Die ungezügelte Verwendung der Go-to-Anweisung hat zur Folge, dass es fürchterlich schwierig wird, einen aussagekräftigen Satz von Koordinaten zu finden, mit denen der Prozessfortschritt beschrieben werden kann.

Und deshalb gilt GOTO als schädlich.

Wallenborn
quelle
22
Diese $list_contains_ip = false;Aussage scheint falsch
Bergi
1
Eine Tasche voller Kieselsteine ​​ist einfach, ich habe eine praktische Tasche, um sie zu halten. : p
Whatsisname
13
@Bergi: Du hast recht. Das zeigt, wie einfach es ist, diese Dinge durcheinander zu bringen.
Wallenborn
6
@whatsisname Diese Tasche wird als Funktion / Klasse / Paket bezeichnet, um sie aufzunehmen. gotos zu benutzen ist wie sie zu jonglieren und die Tasche wegzuwerfen.
Falco
5
Ich liebe das Zitat von Dijkstra, ich hatte es nicht so gehört, aber es ist ein wirklich guter Punkt. Anstatt einen Fluss zu haben, der ziemlich von oben nach unten verläuft, haben Sie plötzlich etwas, das nach dem Zufallsprinzip über die ganze Seite springen kann. Es ist ziemlich beeindruckend, es so kurz zu fassen.
Bill K
41

Es 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):

if (!in_array($ip, $ips)) throw new Exception('Not allowed');

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. GOTOermö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.

JacquesB
quelle
5
@jameslarge 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.
8bittree
6
"Wenn Sie keine andere Wahl haben, verwenden Sie GOTO" Dies wäre äußerst selten. Ich kann mir ehrlich gesagt keinen Fall vorstellen, in dem Sie keine andere Wahl haben, als völlig lächerliche Einschränkungen, die das Umgestalten von vorhandenem Code oder ähnlichen Unsinn verhindern.
jpmc26
8
Außerdem ist die IMO-Emulation eines gotomit Flaggen und Bedingungen behafteten Ratten-Nestes (wie im zweiten Beispiel des OP) viel weniger lesbar als die Juts- Emulation mit einem goto.
3
@ 8bittree: Strukturierte Programmierung ist für die Quellsprache vorgesehen, nicht für die Zielsprache des Compilers. Die Zielsprache für native Compiler sind CPU-Anweisungen, die im Sinne der Programmiersprache entschieden nicht "strukturiert" sind.
Robert Harvey
4
@ 8bittree Die Antwort auf Ihre Frage zu MIR finden Sie in dem von Ihnen angegebenen Link: "Es ist jedoch in Ordnung, ein solches Konstrukt in MIR zu haben, da wir wissen, dass es nur auf bestimmte Arten verwendet wird, z. B. um eine Schleife oder eine Unterbrechung auszudrücken . " Mit anderen Worten, weil goto in Rust nicht erlaubt ist, wissen sie, dass es strukturiert ist und eine Zwischenversion mit gotos auch. Letztendlich muss der Code runter, um zu Maschinenbefehlen zu gelangen. Was ich von Ihrem Link bekomme, ist, dass sie einfach einen inkrementellen Schritt bei der Konvertierung von Hochsprache zu Niedrigsprache hinzufügen.
JimmyJames
17

Wie andere gesagt haben, liegt das Problem nicht bei sich gotoselbst; Das Problem besteht darin goto, wie die Benutzer Code verwenden und wie er das Verstehen und Verwalten von Code erschwert.

Nehmen Sie das folgende Codefragment an:

       i = 4;
label: printf( "%d\n", i );

Für welchen Wert wird gedruckt i? Wann wird es gedruckt? Bis Sie für jede Instanz goto labelin 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 15 gotos, die in beide Richtungen verzweigten. Das war anfangs ein schlechter Code, aber das Vorhandensein dieser gotomachte es noch viel schlimmer. Mein Kollege brauchte ungefähr 2 Wochen, um den Kontrollfluss zu ergründen. Noch besser ist , diese gotofü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 gotos 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:

  1. Nur vorwärts verzweigen;
  2. Kontrollstrukturen nicht umgehen (dh nicht in den Hauptteil einer ifoder foroder whileAnweisung verzweigen );
  3. Nicht gotoanstelle einer Kontrollstruktur verwenden

Es 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:

T ***myalloc( size_t N, size_t M, size_t P )
{
  size_t i, j, k;

  T ***arr = malloc( sizeof *arr * N );
  for ( i = 0; i < N; i ++ )
  {
    arr[i] = malloc( sizeof *arr[i] * M );
    for ( j = 0; j < M; j++ )
    {
      arr[i][j] = malloc( sizeof *arr[i][j] * P );
      for ( k = 0; k < P; k++ )
        arr[i][j][k] = initial_value();
    }
  }
  return arr;
}

Jetzt haben wir ein Problem - was ist, wenn einer der mallocAnrufe 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 mallocAufrufs überprüfen und die entsprechende Aktion ausführen.

T ***myalloc( size_t N, size_t M, size_t P )
{
  size_t i, j, k;

  T ***arr = malloc( sizeof *arr * N );
  if ( arr )
  {
    for ( i = 0; i < N; i ++ )
    {
      if ( !(arr[i] = malloc( sizeof *arr[i] * M )) )
        goto cleanup_1;

      for ( j = 0; j < M; j++ )
      {
        if ( !(arr[i][j] = malloc( sizeof *arr[i][j] * P )) )
          goto cleanup_2;

        for ( k = 0; k < P; k++ )
          arr[i][j][k] = initial_value();
      }
    }
  }
  goto done;

  cleanup_2:
    // We failed while allocating arr[i][j]; clean up the previously allocated arr[i][j]
    while ( j-- )
      free( arr[i][j] );
    free( arr[i] );
    // fall through

  cleanup_1:
    // We failed while allocating arr[i]; free up all previously allocated arr[i][j]
    while ( i-- )
    {
      for ( j = 0; j < M; j++ )
        free( arr[i][j] );
      free( arr[i] );
    }

    free( arr );
    arr = NULL;

  done:
    return arr;
}

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 a gotonicht sofort ein Zeichen für eine schlechte Vorgehensweise oder ein schlechtes Design ist, ist dies einer der wenigen.

John Bode
quelle
Ich mag diese Regeln. Ich würde es gotoauch 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äre goto's, wenn sie nach diesen Regeln geschrieben wurden.
Wildcard
Wenn Sie aus einer tief verschachtelten Schleife ausbrechen müssen, müssen Sie diese Schleife einfach zu einer anderen Funktion machen und zurückkehren
bunyaCloven
6
Jemand hat es für mich einmal zusammengefasst: "Es ist kein Problem, es ist ein Problem, das von einem kommt". Als Spezialist für Softwareoptimierung bin ich mir einig, dass ein unstrukturierter Kontrollfluss Compiler (und Menschen!) Für eine Schleife werfen kann. Ich habe einmal Stunden damit verbracht, eine einfache Zustandsmaschine (vier von fünf Zuständen) in einer BLAS-Funktion zu entschlüsseln, die verschiedene Formen von GOTO, einschließlich Fortrans (in) berühmten zugewiesenen und arithmetischen Gots, verwendete, wenn ich mich recht entsinne.
Njuffa
@njuffa "Als Spezialist für Softwareoptimierung bin ich mir einig, dass ein unstrukturierter Kontrollfluss Compiler (und Menschen!) in eine Schleife werfen kann." - noch mehr Details? Ich dachte, dass heutzutage praktisch jeder optimierende Compiler SSA als IR verwendet, was bedeutet, dass er einen goto-ähnlichen Code darunter verwendet.
Maciej Piechotka
@MaciejPiechotka Ich bin kein Compiler-Ingenieur. Ja, moderne Compiler scheinen SSA in ihrer Zwischendarstellung zu verwenden. Compiler bevorzugen Single-Entry-Single-Exit-Control-Konstrukte. Sind Sie sich nicht sicher, wie die Verwendung von SSA dazu beiträgt? Durch die Beobachtung des generierten Maschinencodes und der Compiler-Ressourcennutzung sowie Diskussionen mit den Compiler-Ingenieuren wird die Optimierung der Codetransformationen durch einen unstrukturierten Steuerungsfluss behindert, vermutlich aufgrund des "Come-from" -Problems. Die Ressourcennutzung (Zeit, Speicher) kann sich negativ auf die Codeleistung auswirken, wenn der Compiler beschließt, eine Optimierung als zu teuer zu überspringen.
Njuffa
12

return, break, continueUnd throw/ catchsind 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.

Bill K
quelle
3
Ich stimme dem nur wegen des Satzes zu: "Das Wichtigste ... ist, Code lesbar zu machen; es ist fast zweitrangig, ihn dazu zu bringen, etwas zu tun." Ich würde es nur etwas anders sagen; Es geht weniger darum, Code lesbar zu machen, als darum , Code einfach zu machen . Aber wie auch immer, es ist ach so wahr, dass es zweitrangig ist, etwas zu tun .
Wildcard
„ , Und / sind alle wesentlichen gotos“ nicht einverstanden sind , der ehemalige Respekt die Grundsätze der strukturierten Programmierung (obwohl man über Ausnahmen argumentieren könnte), Go-to definitiv nicht. In Anbetracht dieser Frage wird durch diese Bemerkung nicht viel gewonnen. returnbreakcontinuethrowcatch
Eric
@eric Sie können jede der Anweisungen mit GOTOs modellieren. Sie könnten sicherstellen, dass die GOTOs alle mit den Grundsätzen der strukturierten Programmierung übereinstimmen - sie haben einfach eine andere Syntax und müssen, um die Funktionalität genau zu duplizieren, andere Operationen wie "if" einschließen. Der Vorteil der strukturierten Versionen besteht darin, dass sie nur bestimmte Ziele unterstützen - Sie WISSEN, wenn Sie eine Fortsetzung sehen, in der sich ungefähr die Position des Ziels befindet. Der Grund, den ich erwähnte, war, darauf hinzuweisen, dass es die Missbrauchbarkeit ist, die das Problem darstellt, und nicht, dass es nicht korrekt verwendet werden kann.
Bill K
7

GOTOist 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, GOTOwollten sie vollständig verbieten.

Ironischerweise GOTOist 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 GOTObesser lesbar sein." Die Studenten werden das nicht verstehen. Sie werden missbrauchen GOTO, um unlesbaren Code zu erstellen.

Stattdessen sagst du " GOTOböse. GOTOBöse. GOTONicht bestandene Prüfung". Die Studenten werden verstehen , dass !

Stig Hemmer
quelle
4
Dies gilt in der Tat für alle veralteten Dinge. Globals. Einbuchstabige Variablennamen. Selbstmodifizierender Code. Eval. Ich vermute sogar, ungefilterte Benutzereingaben. Wenn wir uns darauf vorbereitet haben, solche Dinge wie die Pest zu vermeiden, sind wir in der richtigen Position, um zu beurteilen, ob eine bestimmte Verwendung von ihnen, die gut ausgeschildert ist, die richtige Lösung für ein bestimmtes Problem sein könnte. Vorher behandeln wir sie am besten als einfach verboten.
Dewi Morgan
4

Mit Ausnahme von gotosind alle Flow-Konstrukte in PHP (und den meisten Sprachen) hierarchisch gegliedert.

Stellen Sie sich einen Code vor, der mit zusammengekniffenen Augen untersucht wird:

a;
foo {
    b;
}
c;

Unabhängig davon , welches Kontrollkonstrukt fooist ( if, whileusw.) gibt es nur bestimmte erlaubt Aufträge für a, bund c.

Sie könnten a- b- c, oder a- coder sogar a- b- b- b- haben c. Aber man konnte nie haben b- coder a- b- a- c.

... es sei denn du hast goto.

$a = 1;
first:
echo 'a';
if ($a === 1) {
    echo 'b';
    $a = 2;
    goto first;
}
echo 'c'; 

goto(insbesondere rückwärts goto) kann so problematisch sein, dass es am besten ist, es einfach in Ruhe zu lassen und hierarchische, blockierte Flow-Konstrukte zu verwenden.

gotos 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.

if(!in_array($ip, $ips, true)) {
    throw new Exception('Not allowed');
}
Paul Draper
quelle
2

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.

Tulains Córdova
quelle
6
gotokann 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.
Matthew Whited
@MatthewWhited Vielleicht, wenn Sie in einer Zehn-Zeilen-Methode ein einzelnes goto haben. Aber die meisten Methoden sind nicht zehn Zeilen lang und wenn Leute GOTO benutzen, benutzen sie es meistens nicht "nur einmal".
Tulains Córdova
2
@MatthewWhited Tatsächlich stammt der Begriff Spaguetti-Code aus verschlungenem , schwer zu verfolgendem Code, der durch unstrukturierte Verzweigungsstrukturen verursacht wird. Oder ist Spaguetti-Code jetzt leichter rot? Vielleicht. Vinyl kehrt zurück, warum nicht GOTO.
Tulains Córdova
3
@Tulains Ich muss glauben, dass es so schlimme Dinge gibt, weil sich die Leute dann immer wieder darüber beschweren, aber die meisten Male, die ich sehe 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, in gotodem for- implementiert wird else.
1
Unstrukturierter Spaghetti-Code stammt aus Sprachen, in denen Funktionen / Methoden nicht existieren. gotoeignet sich auch hervorragend für Dinge wie Ausnahmewiederholungsversuche, Rollbacks und Zustandsautomaten.
Matthew Whited
1

An gotoAussagen 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 gotoeine nicht verschachtelte Schleife, was Sie mit tun können break. In diesem Fall verwenden Sie besser break.

Wenn Sie jedoch ein Nested-Loop-Szenario hätten, gotowäre die Verwendung eleganter / einfacher. Zum Beispiel:

$allowed = false;

foreach ($ips as $i) {
    foreach ($ips2 as $i2) {
        if ($ip === $i && $ip === $i2) {
            $allowed = true;
            goto out;
        }
    }
}

out:

if (!$allowed) {
    throw new Exception('Not allowed');
}

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 .

Höhlenmensch
quelle
6
Ich bin mir bei PHP nicht sicher, aber viele Sprachen unterstützen die Verwendung von breakund continuemit 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 vorzugsweise gotofür verschachtelte Schleifen verwenden.
8bittree
@ 8bittree guter Punkt. Ich wusste nicht, dass die Pause von PHP so ausgefallen ist. Ich weiß nur, dass Cs Pause nicht besonders ausgefallen ist. Perls break (sie nennen es last ) war früher auch ähnlich wie c (dh nicht so schick wie PHP), aber es wurde nach einem gewissen Punkt auch schick. Ich denke, meine Antwort wird immer weniger nützlich, da immer mehr Sprachen ausgefallene Versionen von break einführen :)
caveman
3
Ein Bruch mit einem Tiefenwert würde das Problem ohne die zusätzliche Variable nicht lösen. Eine Variable an sich, die ein zusätzliches Maß an Komplexität schafft.
Matthew Whited
Nur weil Sie geschachtelte Schleifen haben, heißt das natürlich nicht, dass Sie ein Goto benötigen. Es gibt auch andere Möglichkeiten, mit dieser Situation elegant umzugehen.
NPSF3000
@ NPSF3000 Könnten Sie bitte ein Beispiel für eine verschachtelte Schleife angeben, die Sie ohne Verwendung einer goto- Anweisung beenden können , während Sie gleichzeitig eine Sprache verwenden, die keine ausgefallene Version von break enthält , die die Tiefe angibt?
Höhlenmensch
0

Mit goto kann ich schneller Code schreiben!

Wahr. Ist mir egal

Springen existiert in der Montage! Sie nennen es einfach jmp.

Wahr. Ist mir egal

Springen löst Probleme einfacher.

Wahr. Ist mir egal

In den Händen eines disziplinierten Entwicklers kann Code, der goto verwendet, leichter zu lesen sein.

Wahr. Ich war jedoch der disziplinierte Coder. Ich habe gesehen, was mit dem Code im Laufe der Zeit passiert. Gotofä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. Gotomacht es schwierig, über Code nachzudenken. Wir haben wirklich hart gearbeitet schaffen while, do while, for, for each switch, subroutines, functions, und alles nur, weil mit diesen Sachen zu tun ifund gotoauf 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 Tat iffängt an, ein wenig wackelig auszusehen.

kandierte_orange
quelle
1
"Springen löst Probleme einfacher." / "Stimmt. Ist mir egal." - Ich würde es hassen, Ihren Code dort zu sehen, wo Sie absichtlich einfache Lösungen zugunsten dessen vermeiden, was am erweiterbarsten ist. (Von YAGNI gehört?)
user253751
1
@Wildcard if und goto sind sehr einfache Möglichkeiten, Probleme zu lösen, die am besten auf andere Weise gelöst werden. Sie sind niedrig hängende Früchte. Es geht nicht um die jeweilige Sprache. Gotolässt Sie ein Problem abstrahieren, indem Sie es zu einem anderen Codefehler machen. Ifkö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.
candied_orange
2
Obwohl ich Ihnen zustimme, ist dies eine schlecht formulierte Antwort. "Stimmt, ist mir egal" ist kein überzeugendes Argument für irgendetwas. Ich denke, Ihre Hauptbotschaft lautet: Obwohl goto für Leute attraktiv ist, die effiziente, leistungsstarke Tools bevorzugen, führt es zu einer überraschend großen Menge an verschwendeter Zeit, selbst wenn vorsichtig vorgegangen wird.
Artelius
1
@artelius Das Thema "true, don't care" soll nicht überzeugen. Es soll die vielen Punkte veranschaulichen, die ich zugeben werde, um zu gehen und trotzdem dagegen anzukämpfen. Daher ist es sinnlos, mit mir über diese Punkte zu streiten. Da ist das nicht mein Punkt. Verstehe den Punkt?
candied_orange
1
Ich glaube nur, dass eine gute Antwort nicht nur Ihre Meinung ausdrückt, sondern sie auch erklärt und es dem Leser ermöglicht, sein eigenes Urteil zu fällen. Aus heutiger Sicht ist unklar, warum die guten Punkte von goto nicht ausreichen, um die Probleme zu überwiegen. Die meisten von uns haben stundenlang nach Fehlern gesucht, die nicht mit goto zusammenhängen. Es ist nicht das einzige Konstrukt, über das man nur schwer nachdenken kann. Jemand, der diese Frage stellt, ist wahrscheinlich viel weniger erfahren und braucht eine Perspektive.
Artelius
-1

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

Zenilogix
quelle