Ist goto noch evil...



  • @!rr!rr_.

    Wo ist da jetzt der grundlegende Unterschied zu einer Schleife? Die Semantik ist doch genau die selbe: Ich habe eine Zählervariable i, die von 1 bis 1e1000 läuft und die im Schleifenkörper verwendet werden kann.
    Versteh' mich nicht falsch, ich bin auch ein Freund von Sprachen, bei denen man die üblichen Kontrollstrukturen nur mit Hilfe von Funktionen schreiben kann (siehe z.B. Haskell), aber zu sagen "in der OOP braucht man genaugenommen weder goto noch Schleifen, sobald man einen Datentyp "Intervall" hat und einen passenden Iterator." ist doch etwas seltsam, da dein Code von der Semantik her eine ganz normale Schleife ist.

    Und irgendwo in der Implementierung wird sicherlich auch eine Schleife auftauchen und sei es nur in Form von Rekursion. Oder wie sieht die Implementierung von 1 to: 1e1000 do: aus? Ich kenne Smalltalk nicht, aber ich gehe mal davon aus das do: eine Funktion ist, die ja auch irgendwie implementiert sein muss.



  • Trolle vs. Klugscheißer



  • CStoll schrieb:

    Gegenfrage: Hast du überhaupt die grundlegenden Konzepte der Berechenbarkeit verstanden?

    nö. Hätte ich sollen?

    wir reden hier von Sprachelementen von Hochsprachen und nicht von Loop-, While-, Blubb- etc -Berechenbarkeitsklassen.



  • !rr!rr_. schrieb:

    CStoll schrieb:

    Gegenfrage: Hast du überhaupt die grundlegenden Konzepte der Berechenbarkeit verstanden?

    nö. Hätte ich sollen?

    wir reden hier von Sprachelementen von Hochsprachen und nicht von Loop-, While-, Blubb- etc -Berechenbarkeitsklassen.

    Ja, hättest du 😉
    Wenn du zu viel aus einer Sprache rauswirfst, verlässt du den Bereich der Turing-Berechenbarkeit. Und das bedeutet, daß es Probleme gibt, die du mit dieser reduzierten Sprache nicht mehr lösen kannst.

    goto kannst du in ca. 99,99% aller Fälle durch sauberere Sprachkonstrukte ersetzen, ohne die Berechenbarkeitsklasse der Sprache zu reduzieren, Schleifen und Rekursionen kannst du nicht ohne Einschränkungen weglassen.



  • abc.w schrieb:

    berniebutt schrieb:

    ...
    hell:
    ...
    goto nirwana;
    ...
    nirwana:
    ... goto hell;
    ...
    

    Na, das ist ja trivial... es gibt z.B. so was:

    /* Hier macht jemand viele Sachen und bei Fehler springt er ans Ende der Funktion */
    if (err)
        goto label_a;
    ...
    if (err)
        goto label_b;
    ...
    if (err)
        goto label_c;
    ...
    if (err)
        goto label_d;
    ...
    if (err)
        goto label_e;
    ...
    /* keine Fehler, einfach return 0 */
        return 0;
    /* und das ist eine Art "Destruktor" am Ende der Funktion: */
    label_e:
        ...
    label_d:
        ...
    label_b:   /* Warum label_b vor label_c... */
        ...
    label_c:
        ...
    label_a:
        ...
        return err;
    

    warum kommt label_b vor label_c... 😕 🙂

    Vermutlich sind die Sprungmarken so angelegt, das sichergestellt ist, das immer das aufgeräumt wird, was auch aufgeräumt werden muss. Die Reihenfolge garantiert dies. Das ist zwar clever, aber nicht sofort ersichtlich. 🙂



  • ghjghj schrieb:

    Vermutlich sind die Sprungmarken so angelegt, das sichergestellt ist, das immer das aufgeräumt wird, was auch aufgeräumt werden muss. Die Reihenfolge garantiert dies. Das ist zwar clever, aber nicht sofort ersichtlich. 🙂

    Das ist ein Standard-Trick in C.





  • ghjghj schrieb:

    Vermutlich sind die Sprungmarken so angelegt, das sichergestellt ist, das immer das aufgeräumt wird, was auch aufgeräumt werden muss. Die Reihenfolge garantiert dies. Das ist zwar clever, aber nicht sofort ersichtlich. 🙂

    Das ist vielleicht clever und nicht sofort ersichtlich, macht aber die ganze Funktion so, wie soll ich es sagen, so monolitisch, Teile der Funktion so verzahnt ineinander, so abhängig voneinander, abhängig von der Vorgeschichte usw., dass man Angst haben muss, wenn man diese Funktion erweitern muss, macht man irgendwas kaputt. Das möchte man ja vermeiden, man möchte Code schreiben, der einfach zu warten, zu erweitern u.ä. ist.



  • Ja, das Beispiel ist ja auch totaler Mist.



  • hustbaer schrieb:

    Ja, das Beispiel ist ja auch totaler Mist.

    Dieser totale Mist stammt aus der Realität... ist schon länger her, ich habe die Funktion dann so gefixt:

    if (err)
    {
        cleanup(ptr);
        return err;
    }
    ...
    if (err)
    {
        cleanup(ptr);
        return err;
    }
    ...
    

    ptr ist ein Zeiger auf die Daten, die freigegeben werden müssen. Was soll man denn in diesem Fall noch machen 😕 Man bekommt diese Aufgabe, im fremden Code einen Fehler zu finden, was bedeutet, man muss die eine oder andere Funktion ändern. Dann stellt man fest, es gibt viel zu viele Pfade in der Funktion, die man kaputtmachen kann. Keine Zeit zum Durchblicken, nicht genug Papier, um sich eine Skizze zu machen und wenn man sich eine Skizze gemacht hat, dann sieht sie aus wie ein Spinngewebe, Durchblick = 0.
    Einfach goto rausschmeissen und das war's, sich das Leben einfach machen. Nachteil ist natürlich, man hat eine neue Datenstruktur, eine zusätzliche neue Funktion (cleanup()), viele returns.



  • CStoll schrieb:

    Wenn du zu viel aus einer Sprache rauswirfst, verlässt du den Bereich der Turing-Berechenbarkeit.

    Richtig ... das gibt einen Marienkäfer ins Aufgabenheft.

    Aber was hat das mit mir zu tun? Ich rede von Schleifen, die durch Intervall + Iterator ersetzbar sind. Auf Hochsprachenebene. Und nicht loop-Berechenbarkeit.

    Beschränkung auf Schleifen mit feststehenden Obergrenzen hat jmd anders in die Diskussion gebracht.



  • !rr!rr_. schrieb:

    Beschränkung auf Schleifen mit feststehenden Obergrenzen hat jmd anders in die Diskussion gebracht.

    Ja. Es war ein gewisser !rr!rr_., der zum Beispiel 1e1000 als Obergrenze nahm.



  • abc.w schrieb:

    Dieser totale Mist stammt aus der Realität... ist schon länger her, ich habe die Funktion dann so gefixt

    Ist das C-Code? Ansonsten wäre RAII wahrscheinlich die beste Idee gewesen.



  • volkard schrieb:

    Ja. Es war ein gewisser !rr!rr_., der zum Beispiel 1e1000 als Obergrenze nahm.

    das scheint ja arg kompliziert zu verstehen zu sein ...

    Pseudo-Code:

    while condition do .bla.bla. od
    

    kann in der Praxis ersetzt werden durch

    1 ... 1e1000 do
       if not condition then exit
       else .bla.bla.
    od
    

    Schleifen werden nun mal nicht öfter als 1e1000-mal ausgeführt.



  • abc.w schrieb:

    hustbaer schrieb:

    Ja, das Beispiel ist ja auch totaler Mist.

    Dieser totale Mist stammt aus der Realität... ist schon länger her, ich habe die Funktion dann so gefixt:

    if (err)
    {
        cleanup(ptr);
        return err;
    }
    ...
    if (err)
    {
        cleanup(ptr);
        return err;
    }
    ...
    

    ptr ist ein Zeiger auf die Daten, die freigegeben werden müssen. Was soll man denn in diesem Fall noch machen 😕 Man bekommt diese Aufgabe, im fremden Code einen Fehler zu finden, was bedeutet, man muss die eine oder andere Funktion ändern. Dann stellt man fest, es gibt viel zu viele Pfade in der Funktion, die man kaputtmachen kann. Keine Zeit zum Durchblicken, nicht genug Papier, um sich eine Skizze zu machen und wenn man sich eine Skizze gemacht hat, dann sieht sie aus wie ein Spinngewebe, Durchblick = 0.
    Einfach goto rausschmeissen und das war's, sich das Leben einfach machen. Nachteil ist natürlich, man hat eine neue Datenstruktur, eine zusätzliche neue Funktion (cleanup()), viele returns.

    Würde ich mit Deiner Funktion arbeiten müssen, würde ich sie zuerstmal zurückfixen.



  • Nexus schrieb:

    abc.w schrieb:

    Dieser totale Mist stammt aus der Realität... ist schon länger her, ich habe die Funktion dann so gefixt

    Ist das C-Code? Ansonsten wäre RAII wahrscheinlich die beste Idee gewesen.

    Das ist C-Code (C++ habe ich schon seit längerem nichts gemacht und muss jetzt wahrscheinlich alles neu lernen...) und das Beispiel stammte aus einem Linux-Kernel Treiber, weiss jetzt nicht, ob Quellcode davon öffentlich verfügbar ist. Der Linux-Kernel ist eigentlich voller gotos, wahrscheinlich, weil die Studenten es damals so angefangen haben (und sind es jetzt wahrscheinlich leid, geben es aber nicht zu) und man muss sagen, das Ganze funktioniert ja irgendwie.
    Muss man aber mit dem Code was machen, hat man plötzlich Angst, den Code "kaputt zu reparieren". Man sieht eine einfache Sprungmarke, die z.B. von zwei Stellen angesprungen wird, zwei Vorgeschichten sozusagen, bei zwei und mehr Sprungmarken spielt vielleicht (bestimmt...) die Reihenfolge eine Rolle, in welcher sie angesprungen werden, mal von da, mal von dort, man muss "Kombinatorik" betreiben, alle Pfade skizzieren auf Papier usw.

    volkard schrieb:

    Würde ich mit Deiner Funktion arbeiten müssen, würde ich sie zuerstmal zurückfixen.

    Bitte schön, ich habe keine Probleme damit. Wenn jemand da geübt ist, kann das Ganze überblicken, bitte schön, ich kann das nicht, deswegen mache ich es mir einfach bzw. bilde mir ein, dass es einfach ist, was ich da mache 🙂



  • !rr!rr_. schrieb:

    Pseudo-Code:

    while condition do .bla.bla. od
    

    kann in der Praxis ersetzt werden durch

    1 ... 1e1000 do
       if not condition then exit
       else .bla.bla.
    od
    

    Schleifen werden nun mal nicht öfter als 1e1000-mal ausgeführt.

    Da wäre ich mir nicht so sicher. Selbst im Umgang mit LOOP-Berechenbarkeit ist mir keine Obergrenze der Schleifendurchläufe bekannt.



  • das ist eben der Unterschied zwischen Theorie und Praxis.

    Das Gebiet heißt ja nicht umsonst "BerechenbarkeitsTheorie".



  • Man kann auch den Intervalltypen derart anpassen, dass als oberste Grenze unendlich zulässig ist. Dann hat man Turingvollständigkeit auch wieder theoretisch.
    Die Aussage "man kann Schleifen und goto durch Intervall und Iterator ersetzen" kann man aber trotzdem nicht so stehen lassen, denn das schließt noch nicht ein, dass man die Iteration auch abbrechen können muss. Und wenn wir schon eine Iteration haben, kann man das auch gleich mit Schleife übersetzen.



  • !rr!rr__ schrieb:

    das ist eben der Unterschied zwischen Theorie und Praxis.

    Auch in der Praxis ist es sinnlos, eine willkürliche Obergrenze festzulegen. Und es gibt genug Programme, da wird eben keine Grenze vorgesehen, weil es einfach Humbug wäre - die laufen solange, wie notwendig ist, um den Anwender glücklich zu machen.
    (von den Problemen, Zahlen in der Größenordnung um 1e1000 in der nötigen Genauigkeit zu verarbeiten, will ich gar nicht erst anfangen).


Anmelden zum Antworten