Frage zu "return"



  • Zurückgegeben werden nicht Variablen, sondern Werte.. und zurückgegeben wird immer an den Aufrufer, das muss nicht zwangsläufig die main Funktion sein.. 💡
    (Einfach die Lösung sagen wäre ja langweilig. :p)



  • Heureka!

    double newt(double x_0) // Ausführen des Newtonverfahrens
    {
        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);
    }
    

    Scheint zu funktionieren!

    Vielen Dank für die Hinweise!

    Tante Edit: Grade noch nen Absolutbetrag in der Bedingung spendiert, das sieht hübscher aus. 🙂



  • Und da bin ich wieder. Diesmal mit einem, wahrscheinlich, noch trivialerem Problem. Ich scheine den Wald vor lauter Bäumen nicht zu sehen.

    Folgendes Problem: Um von Funktionen mehrere Nullstellen bestimmen zu können, ohne das Programm mehrfach ausführen zu müssen, habe ich eine while()-Schleife in die main()-Funktion eingebaut, die quasi kleine Teilintervalle auf Nullstellen überprüft. Dazu der folgende Quellcode:

    int main(int argc, char** argv)
    {
        double from = 0, to = 0, erg = 0, erg_1 = 1.e200, step = 0;
        ofstream ausgabe;
    
        /* Benutzereingaben */
        cout << "Parametereingabe:" << endl;
    
        cout << "Startwert: ";
        cin >> from;
        cout << "Endwert: ";
        cin >> to;
        cout << "Schrittweite: ";
        cin >> step;
    
        /* Ausführen des Algorithmus in kleinen Teilintervallen */
        while(from <= to)
        {     
            erg = newt(from); // Finden der Nullstellen einer Funktion
    
            if(erg != erg_1) // Nur neue Ergebnisse ausgeben
            {
                    cout << "Nullstelle bei x = " << erg << endl; // Ausgabe der Nullstelle auf das Terminal
    
                    /* Dateioperationen */
                    ausgabe.open("nullstellen.dat",ios::app); // Ausgabedatei öffnen
                    if(ausgabe.is_open()) // Nur schreiben, wenn die Datei geöffnet ist
                            ausgabe << erg << endl; // Nullstelle in Datei schreiben
                    else
                        cout << "Datei kann nicht aufgerufen werden!" << endl;
                    ausgabe.close(); // Ausgabedatei schließen
            }
            erg_1 = erg;
            from += step;
        }
    
        return 0;
    }
    

    Dummerweise scheint die Schleife abzubrechen, sobald eine Nullstelle gefunden wurde, woran kann das liegen? Meiner Ansicht nach ist die Exit-Bedingung doch eigentlich eindeutig.

    Viele Grüße!



  • Fehler liegt woanders, es wird nur eine Nullstelle gefunden. Der Code ist so korrekt. (Nicht unbedingt schön. :p)



  • Hmm, dann suche ich mal die restlichen Funktionen ab. Wie kann man denn, nur aus Interesse, schöner coden?



  • Kleiner Tipp:

    ich würde deine Funktion so umschreiben:

    double newt(double x_0, double(*fct)(double)) // Ausführen des Newtonverfahrens
    {
        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);
    }
    

    Der zweite Parameter ist, die Funktion, die untersucht werden soll.

    (Kleine Frage: was macht die Funktion der() ? Ist das die Ableitung? falls ja müsste die Funktion so aussehen:

    double newt(double x_0, double(*fct)(double), double(*der)(double))
    //...
    

    )

    Noch Fragen? Und glaub mir, so ist besser.

    Und aufruf sieht so aus:

    newt(x, &funktion);     // Mit zwei Parametern und "der()" ist eine feste Funktion
    newt(x, &funktion, &der);    //Mit drei Parametern und "der()" ist die Ableitung.
    

    Viel Spaß damit!



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


Anmelden zum Antworten