Ist goto noch evil...



  • !rr!rr_. schrieb:

    Schleifen sind sehr wohl durch Intervall und Iterator ersetzbar. Alle in der Praxis auftretenden Schleifen werden höchstens 1e1000-mal durchlaufen, also reichen endliche Intervalle vollkommen aus.

    Bleibt noch die Endlosschleife - die tritt in der Praxis nicht auf, weil kein Programm unendlich lange läuft.

    Die Ackermann-Funktion ist nicht LOOP-berechenbar.

    Edit: Ein bisschen ausführlicher:
    IIRC in den späten Zwanzigern hat man geglaubt, dass man keine Endlosschleife brauchen würde, um Turing-vollständig zu werden. Das trifft jedoch nicht zu. Ackermann hat eine Funktion konstruiert, die durch Turingmaschinen berechenbar ist, aber so schnell wächst, dass sie nicht mit Schleifen berechnet werden kann, bei denen vorher die Anzahl der Durchläufe feststeht.



  • hab' ich gesagt, daß die Anzahl Durchläufe vorher feststehen muß? 😕

    Schleifen, die bei Eintreten einer Bedingung abbrechen, lassen sich auch durch Intervall und Iterator ersetzen:

    [ 1 to: 1e1000 do: [ :i | 
    	1/(4-i). Transcript show: i ] ] 
    	on: Error do: [ 
    		Transcript show: 'oops ...' ].
    "123oops ..."
    


  • !rr!rr_. schrieb:

    hab' ich gesagt, daß die Anzahl Durchläufe vorher feststehen muß? 😕

    Lies doch bitte die Postings von Michael E. und bei Dir unbekannten Begriffen schau sie wenigstens oberflächlich im Netz nach.



  • wozu? hast du mein Posting nicht verstanden?



  • !rr!rr_. schrieb:

    wozu? hast du mein Posting nicht verstanden?

    Gegenfrage: Hast du überhaupt die grundlegenden Konzepte der Berechenbarkeit verstanden? Du kannst mit endlichen Schleifen nicht alles ausrechnen, egal wie groß die Anzahl der Durchläufe auch gesetzt wird.



  • @!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.


Anmelden zum Antworten