Frage zu "return"



  • Die erinnerung schrieb:

    Noch Fragen? Und glaub mir, so ist besser.

    Ja: Warum kein Template?



  • Es geht hier um Funktionen.

    Natürlich könnte man noch ein Template reinhauen, aber das wäre, glaube ich, zu viel.

    Nagut, hier mit Template:

    //Eine Funktion
    template <class T> T newt(T x_0, T(*fct)(T))
    {
        //...
    }
    
    //Zwei Funktionen:
    template <class T> T newt(T x_0, T(*fct)(T), T(*der)(T))
    {
        //...
    }
    

    Würde auch gehen...



  • Die erinnerung schrieb:

    Es geht hier um Funktionen.

    Natürlich könnte man noch ein Template reinhauen, aber das wäre, glaube ich, zu viel.

    Nagut, hier mit Template:

    Würde auch gehen...

    rofl, nein.

    template <typename Functor>
    double newt(double x_0, Functor fct)
    {
        double x_n = 0, y_test = fct(x_0), erg = 0;
    
        if(y_test < 1.e-30 && y_test > -1.e-30)
        {
            return x_0;
        }
        x_n = x_0 - fct(x_0)/der(x_0);
    
        return newt(x_n, fct);
    }
    

    Nicht nur übersichtlicher, sondern erzeugt auch noch sehr wahrscheinlich deutlich schnelleren Code. 😉



  • Das habe ich noch nicht gelernt.



  • Danke für die Antworten, ich schau mir das mal in Ruhe an. 🙂
    Habe btw. herausgefunden, weshalb die Funktion nicht alle Nullstellen findet: Die numerische Berechnung der Ableitung zickt bei bestimmten Werten für "dx" ein wenig rum. Wenn man das variiert, funktioniert es.



  • Nur, um Cooky zu ärgern kommt jetzt die ultimative Variante der Funktion:

    template <typename T, typename Functor>
    T newt(T x_0, Functor fct, Functor der)
    {
        double x_n = 0, y_test = fct(x_0), erg = 0;
    
        if(y_test < 1.e-30 && y_test > -1.e-30)
        {
            return x_0;
        }
        x_n = x_0 - fct(x_0)/der(x_0);
    
        return newt(x_n);
    }
    


  • Die erinnerung schrieb:

    Nur, um Cooky zu ärgern kommt jetzt die ultimative Variante der Funktion:

    Die ist aber falsch. fct und der müssen ja nicht vom gleichen Typ sein.



  • sollten aber, weil der die Ableitung von fct ist...

    Also doch richtig!



  • Hat das auch einen Sinn oder ist das nur geistige Onanie? Ändere lieber

    y_test < 1.e-30 && y_test > -1.e-30
    

    in

    abs(y_test) < 1e-30
    

    und frage dich dann, ob diese Schranke wirklich sinnvoll ist bzw. ob man sie nicht in Abhängigkeit von T jeweils anders wählen sollte.



  • Die erinnerung schrieb:

    Also doch richtig!

    Nene, das hat erstmal nichts mit dem Typ zu tun. Funktortypen setzen sich u.U. aus mehr zusammen als nur aus einer Funktionssignatur, darauf kannste dich also nicht verlassen. Zudem hast du in Zeile 12 noch was vergessen. 😉



  • Dann halt so :p

    template <typename T, typename Functor, typename Functor2>
    T newt(T x_0, Functor fct, Functor2 der)
    {
        T x_n = 0, y_test = fct(x_0);
    
        if(abs(y_test) < 1.e-30)
        {
            return x_0;
        }
        x_n = x_0 - fct(x_0)/der(x_0);
    
        return newt(x_n);
    }
    

    Es war nicht Zeile 12, sondern Zeile 4. Also ganz blöd bin ich auch nicht!

    Wobei ich immernoch die Version nehmen würde. Da ist nämlich sichergestellt, dass der Datentyp stimmt!

    template <typename T>
    T newt(T x_0, T (*fct)(T), T (*der)(T))
    {
        T x_n = 0, y_test = fct(x_0);
    
        if(abs(y_test) < 1.e-30)
        {
            return x_0;
        }
        x_n = x_0 - fct(x_0)/der(x_0);
    
        return newt(x_n);
    }
    


  • Die erinnerung schrieb:

    Es war nicht Zeile 12, sondern Zeile 4. Also ganz blöd bin ich auch nicht!

    Guck dir Zeile 12 noch mal genau an.

    Die erinnerung schrieb:

    Wobei ich immernoch die Version nehmen würde. Da ist nämlich sichergestellt, dass der Datentyp stimmt!

    Ja? Mit T = std::string? Nene, solange C++ keine Concepts anbietet kannste das vergessen. (Manchmal kann man vielleicht schönere Fehlermeldungen mit static_assert bekommen, aber das ist auch mühselig.) Nimm Funktoren. 😉



  • Ich meinte, dass so sichergestellt ist, dass der Datentyp der Funktionen in der Funktion stimmt. Bei deiner Variant ist es das nämlich nicht.



  • Die erinnerung schrieb:

    Ich meinte, dass so sichergestellt ist, dass der Datentyp der Funktionen in der Funktion stimmt. Bei deiner Variant ist es das nämlich nicht.

    😕 Lesen -> Denken -> Verstehen. Natürlich meintest du das. Ändert aber nichts daran, dass du ohne SFINAE Tricks oder static_assert bei einem Template nie sicher sein kannst, dass man einen ordentlichen Typ vorgesetzt bekommt. Egal wo. Das ist ziemlich blöd, aber ohne Concepts nicht vermeidbar. Und ich halte es für ziemlich großen Quatsch, die wesentlich ⚠ schlechtere Lösung zu bevorzugen, nur um sich selbst mit einem falschen Eindruck von Typsicherheit zu beglücken.



  • Ok. Soweit klar, aber std::string wird der Compiler nicht akzeptieren, weil der Operator - vorkommt und vorallem die Funktion abs(). Die lässt nur numerische Typen zu und das ist doch genau das, was er braucht?

    Außerdem haben wir noch die Operatoren / und <. < unterstützen zwar viele Typen, aber / so gut wie ausschließlich numerische.

    Wobei ich sagen würde, in beide Fällen mekert der Compiler, wenn man Mist eingibt.



  • Die erinnerung schrieb:

    Wobei ich sagen würde, in beide Fällen mekert der Compiler, wenn man Mist eingibt.

    Ja, natürlich. Da muss man schon sehr viel Glück haben, dass man da für T etwas wählt, das funktioniert, aber irgendwie Quatsch ist. Das kann quasi nur Absicht sein. Aber das ist ja auch gar nicht das Problem. Denn was man eigentlich erreichen möchte, ist eine Fehlerbehandlung wie bei normalen Funktionen:

    double div(double a, double b)
    {
      return a / b;
    }
    
    int main()
    {
      div("Hallo", "Welt"); // Fehler beim Aufrufer
    }
    

    Und was man nicht möchte, ist

    template <typename T>
    T div(T a, T b)
    {
      return a / b; // Fehler taucht erst hier auf, weil kein passender operator / 
    }
    
    int main()
    {
      div("Hallo", "Welt");
    }
    

    Und letzteres kann halt extrem beknackte Fehlermeldungen geben, wenn man sich dann erstmal durch 5 Template-Stacks kämpfen muss. Obwohl der eigentlich Fehler nur sein sollte: Hey, dein Typ erfüllt die Anforderungen nicht! Deswegen sind Concepts ja so wichtig für die Sprache, obwohl sie primär nur ein "usability Feature" sind.



  • Jo, das ist ein Problem, aber es lässt sich halt nicht umgehen.

    Wenn man sowas in einer Libary einbaut, sollte man in der Dokumentation daruf hinweisen.


Anmelden zum Antworten