Ist goto noch evil...



  • inflames2k schrieb:

    goto ist und bleibt Evil. - Alle fälle in denen ein goto sinnvoll zu verwenden wäre, kann man mit Schleifen abdecken.

    Goto mit Schleifen-Konstrukten zu ersetzen ist aber noch mehr evil als goto selbst.

    Konstrukte ala

    while (run)
        {
            for (...)
            {
                // ...
                if (...)
                {
                    run = false;
                    break;
                }
                // ...
            }
        }
    

    gehören mit zum Schlimmsten was man so machen kann.

    Destruktoren (RAII statt goto cleanup ) und Hilfsfunktionen ( return statt goto exit_all_loops ) sind dagegen IMO vollkommen OK bzw. sogar guter Stil.



  • hustbaer schrieb:

    Konstrukte ala

    while (run)
        {
            for (...)
            {
                // ...
                if (...)
                {
                    run = false;
                    break;
                }
                // ...
            }
        }
    

    Witzigerweise gibt es in Fortran mit die eleganteste Lösung für das Problem, benannte Schleifen aus denen man mit Anweisung und Namen herausspringen kann.

    Das sieht da so aus

    outer_loop: do i=0,N
      inner_loop: do j=0,N
        if ... then
          exit inner_loop
        end if
        if ... then
          exit outer_loop
        end if
      end do
    end do
    

    Insofern ist der Bedarf an goto in Fortran eher gering. Mir ist kürzlich in C nur beim Initialisieren und der Fehlerbehandlung der Bedarf an gotos aufgetreten.



  • Am Ende wird alles immer zu einem goto.

    jne label, jmp label,...
    

    Seufz.



  • Fortran mit die eleganteste Lösung für das Problem

    Es gibt andere Sprachen mit anderen Loesungen. Elegant finde ich es trotzdem nicht.



  • in der OOP braucht man genaugenommen weder goto noch Schleifen, sobald man einen Datentyp "Intervall" hat und einen passenden Iterator.



  • ~john schrieb:

    Witzigerweise gibt es in Fortran mit die eleganteste Lösung für das Problem, benannte Schleifen aus denen man mit Anweisung und Namen herausspringen kann.

    Das sieht da so aus

    outer_loop: do i=0,N
      inner_loop: do j=0,N
        if ... then
          exit inner_loop
        end if
        if ... then
          exit outer_loop
        end if
      end do
    end do
    

    Insofern ist der Bedarf an goto in Fortran eher gering. Mir ist kürzlich in C nur beim Initialisieren und der Fehlerbehandlung der Bedarf an gotos aufgetreten.

    Das gibt es in Ada auch. In C oder C++ kann man sich das mit dem "Named-Loop-Idiom" nachbauen. Damit bekommt man etwas, dass dem sehr nahe kommt. Allerdings auch wieder mit goto (wie auch sonst).



  • !rr!rr_. schrieb:

    in der OOP braucht man genaugenommen weder goto noch Schleifen, sobald man einen Datentyp "Intervall" hat und einen passenden Iterator.

    Solange es keine Endlos-Intervalle gibt in denen man endlos Code ausführen kann, ist das etwas kurz gedacht.



  • !rr!rr_. schrieb:

    in der OOP braucht man genaugenommen weder goto noch Schleifen, sobald man einen Datentyp "Intervall" hat und einen passenden Iterator.

    In der funktionalen Programmierung braucht man weder goto noch Schleifen sobald man die Programmiersprache benutzt 🙂

    ~john schrieb:

    Witzigerweise gibt es in Fortran mit die eleganteste Lösung für das Problem, benannte Schleifen aus denen man mit Anweisung und Namen herausspringen kann.

    Viel eleganter als ein goto mit sinnvoll benannten Sprungmarken finde ich das nicht unbedingt.

    Ich war allerdings schon ein paar Mal davor, ein goto einzusetzen, schlussendlich löste ich es dann doch anders. Aber ich würde sicher nicht kategorisch auf goto verzichten, wenn es den Code vereinfacht.



  • Ich würde es auch nicht aus Prinzip vermeiden, aber wenn man mal ehrlich ist passiert es aber extrem selten, dass ein goto wirklich mehr Sinn macht als etwas anderes. Spontan fällt mir jetzt keine Situation ein, die man nicht auch anders besser lösen könnte.



  • Hier nochmal ganz bewusst der imo schöne Code mit goto:

    for(std::size_t n = 0; n < 100; ++n)
        for(std::size_t m = 0; m < 100; ++m)
            for(std::size_t l = 0; l < 100; ++l)
                if(cube[n][m][l] == "Bananenweizen")
                    goto rausHierRufzeichen;
                else
                    // mach irgendwas
    rausHierRufzeichen:
    

    SCHÖN!



  • Bananenweizen sollte eh eine exception werfen.



  • Bashar schrieb:

    Trollen ist immer noch evil.

    edit: Aber wie man sieht, beißen sie wie verrückt, also was soll man machen?

    Goto fischen?



  • Eisflamme schrieb:

    Hier nochmal ganz bewusst der imo schöne Code mit goto:

    for(std::size_t n = 0; n < 100; ++n)
        for(std::size_t m = 0; m < 100; ++m)
            for(std::size_t l = 0; l < 100; ++l)
                if(cube[n][m][l] == "Bananenweizen")
                    goto rausHierRufzeichen;
                else
                    // mach irgendwas
    rausHierRufzeichen:
    

    SCHÖN!

    n = 100;
    ist das weniger schön ?



  • !rr!rr_. schrieb:

    in der OOP braucht man genaugenommen weder goto noch Schleifen, sobald man einen Datentyp "Intervall" hat und einen passenden Iterator.

    Da wär ich aber gespannt, wie du den größten gemeinsamen Teiler von zwei Zahlen effizient ausrechnest. Bitte aber ohne Rekursion, weil das nur ein verstecktes goto ist. Der ggT könnte mit Iteratoren recht schwierig werden. 🙂

    (Technischer Hintergrund der Frage, für Interessierte: http://www.math.uiuc.edu/People/vandendries/gc.pdf )



  • Christoph schrieb:

    Da wär ich aber gespannt, wie du den größten gemeinsamen Teiler von zwei Zahlen effizient ausrechnest. Bitte aber ohne Rekursion, weil das nur ein verstecktes goto ist. Der ggT könnte mit Iteratoren recht schwierig werden. 🙂

    Sind ja bei Euklid nur max 64 Durchläufe für 32-Bitter. Die kann man auch Untereinanderschreiben.

    Wie !rr!rr_. durch Iteratoren Schleifen vermeiden will, weiß ich aber auch nicht. Ich benutze Iteratoren doch oft als Schleifenlaufvariable.



  • volkard schrieb:

    Christoph schrieb:

    Da wär ich aber gespannt, wie du den größten gemeinsamen Teiler von zwei Zahlen effizient ausrechnest. Bitte aber ohne Rekursion, weil das nur ein verstecktes goto ist. Der ggT könnte mit Iteratoren recht schwierig werden. 🙂

    Sind ja bei Euklid nur max 64 Durchläufe für 32-Bitter. Die kann man auch Untereinanderschreiben.

    Guter Punkt. Also ist man mit einem Iterator über ein vorher berechnetes Intervall maximal 64 Mal schlechter als mit Euklid. Das ist gar nicht so schlecht, wie ich zuerst dachte.

    Bei unbeschränktem Input wird es wohl erst richtig fies, wenn man nicht mehr vorher ausrechnen kann wieviele Iterationen Euklid maximal braucht.

    edit: Obwohl es vielleicht auch da wieder anders aussieht, wenn man die log-Funktion zur Verfügung hat. Aber das wär Fließkomma, das will man nicht haben im ggT-Algorithmus, denk ich.



  • edit: Obwohl es vielleicht auch da wieder anders aussieht, wenn man die log-Funktion zur Verfügung hat. Aber das wär Fließkomma, das will man nicht haben im ggT-Algorithmus, denk ich.

    Nehmen wir doch die Länge des Inputs als log.



  • volkard schrieb:

    edit: Obwohl es vielleicht auch da wieder anders aussieht, wenn man die log-Funktion zur Verfügung hat. Aber das wär Fließkomma, das will man nicht haben im ggT-Algorithmus, denk ich.

    Nehmen wir doch die Länge des Inputs als log.

    Die hat man nicht unbedingt verfügbar, weil das ein verstecktes Implementierungsdetail der Sprache sein könnte. Aber OK, ich seh langsam ein, dass mein Beispiel für zu viele Lücken hat, um auf reale Sprachen anwendbar zu sein.

    Es ging mir darum, dass primitiv-rekursive Funktionen nicht in der Lage sind, die Anzahl der benötigten Iterationen des Euklid-Algorithmus im Vorfeld effizient auszurechnen, und zwangsläufig Linear-Zeit brauchen, um den ggT zu ermitteln. Der euklidische Algorithmus braucht nur logarithmische Zeit.

    Die Bemerkung von !rr!rr_. klang für mich danach, als wolle er alles in primitiv-rekursiven Funktionen ausdrücken, d.h. insbesondere alle for-Schleifen iterieren über ein Intervall, das vor der for-Schleife bekannt ist und sich während den Iterationen nicht ändert.



  • ToGoOrNotToGo schrieb:

    ...oder darf man es wieder verwenden?

    Ernst gemeinte Frage.

    Jo, jetzt ist es wieder in Ordnung, die modernen Computer haben genug Rechenpower und dank 64 Bit ist GOTO nun auch kein Problem mehr.



  • Tim06TR schrieb:

    Eisflamme schrieb:

    Hier nochmal ganz bewusst der imo schöne Code mit goto:

    for(std::size_t n = 0; n < 100; ++n)
        for(std::size_t m = 0; m < 100; ++m)
            for(std::size_t l = 0; l < 100; ++l)
                if(cube[n][m][l] == "Bananenweizen")
                    goto rausHierRufzeichen;
                else
                    // mach irgendwas
    rausHierRufzeichen:
    

    SCHÖN!

    n = 100;
    ist das weniger schön ?

    Es ist ca. 100x weniger schön, und macht ausserdem noch was anderes (die inneren Schleifen werden nicht sofort abgebrochen).

    Warum ist es 100x weniger schön? Weil es 100x weniger klar ist. Man muss erstmal 100 Gehirnzellen bemühen um zu verstehen was es tut. Beim goto Code reichen 10.

    Und wenn man es gleich mit return statt goto schreiben würde, dann käme man mit 2-3 Gehirnzellen aus. Also noch besser. Weil klarer.


Anmelden zum Antworten