Rechenprogramm mit strings



  • Ja aber es muss doch auch so gehen?

    Oder soll ich die Operanden und Rechenzeichen einzeln abfragen lassen und in eine Liste schieben?



  • astir schrieb:

    Ja aber es muss doch auch so gehen?

    Oder soll ich die Operanden und Rechenzeichen einzeln abfragen lassen und in eine Liste schieben?

    Jein. Das könnte schon gehen. Aber das ist alles andere als sauber oder schön. Was machst du mit verschachtelten Klammern? Da dürfte deine Methode ziemlich schnell scheitern. Ich würde es erst gar nicht damit probieren.



  • Vermutlich wirst Du strings in ints und umgekehrt umwandeln müssen,

    Das geht mit den stringstreams ganz gut, z.B. von string in double umwandeln:

    #include <sstream>
    #include <string>
    using namespace std;
    
    int main()
    {
    double d;
    string s = "3345.5";
    
    stringstream sstr;
    sstr << s;
    sstr >> d;
    }
    


  • redrew99 schrieb:

    Vermutlich wirst Du strings in ints und umgekehrt umwandeln müssen,

    Das geht mit den stringstreams ganz gut, z.B. von string in double umwandeln:

    #include <sstream>
    #include <string>
    using namespace std;
    
    int main()
    {
    double d;
    string s = "3345.5";
    
    stringstream sstr;
    sstr << s;
    sstr >> d;
    }
    

    Wen interessiert das? Was hat es mit der Frage zu tun?



  • Wie ich annehme, den TE, da

    astir schrieb:

    ...damit ich dann die beiden Teile des Strings, wo die Operanden stehen, in einen double umwandeln kann...



  • Einen Lexer halte ich auch für übertrieben. Genauso einen AST. Einfach auswerten während man parst, davor eine Grammatik aufstellen. Dürfte nicht besonders schwer sein.



  • Kellerautomat schrieb:

    Einen Lexer halte ich auch für übertrieben. Genauso einen AST. Einfach auswerten während man parst, davor eine Grammatik aufstellen. Dürfte nicht besonders schwer sein.

    Naja, aber irgendwo muss der Parser ja erkennen, ob er grad eine Zahl oder einen Operator an der Hand hat, und das macht man am einfachsten mit einem Lexer. Finde ich jetzt nicht weiter schwer oder übertrieben. Ein AST wär hier erstmal wahrscheinlich schon übertrieben, geb ich dir Recht.



  • http://ideone.com/b6Nyk
    Fand ich jetzt nicht besonders schwer ohne Lexer. 😉



  • Ihr verwirrt mich nur ncoh mehr.

    Es muss ja nach einer der beiden Methoden gehen, denn das wurde uns als Lösungsansatz gegeben....



  • Mechanics schrieb:

    astir schrieb:

    Ja aber es muss doch auch so gehen?

    Oder soll ich die Operanden und Rechenzeichen einzeln abfragen lassen und in eine Liste schieben?

    Jein. Das könnte schon gehen. Aber das ist alles andere als sauber oder schön. Was machst du mit verschachtelten Klammern? Da dürfte deine Methode ziemlich schnell scheitern. Ich würde es erst gar nicht damit probieren.

    Genau aus dem Grund wollte ich den etwas komplexeren Ansatz mit Strings wählen, komm da aber auch nciht weiter...ich bin ehct zu blöd für sowas... 😞



  • Die Lösung, die Kellerautomat gepostet hat, ist doch recht brauchbar, darauf kannst du aufbauen. Was ich nicht ganz optimal finde, ist die Verwendung von char *, vielleicht kannst du das besser lösen 😉



  • Mechanics schrieb:

    Die Lösung, die Kellerautomat gepostet hat, ist doch recht brauchbar, darauf kannst du aufbauen. Was ich nicht ganz optimal finde, ist die Verwendung von char *, vielleicht kannst du das besser lösen 😉

    Ja - alle sind irgendwie ganz verliebt in diese Bereiche, die schon im Memory liegen und vorne zeigt ein Pointer (char*) drauf. Auch der TE hätte die Zeichen gerne in einem string vorliegen, um sich dann mit find und find_first_of an das Zerlegen desselben zu begeben.
    Dabei geht es hier doch ganz klar um einen Input.
    Was viele nicht wissen: für den Input haben die Götter des C++ den std::istream erfunden. Und dessen Lexer-Qualitäten kann man sich in diesem Fall zu Nutze machen, indem man einfach abfragt ob das nächste Zeichen, was daher kommt, ein Operator, also ein Zeichen aus einem begrenzten Vorrat "+-*/" ist. Da kann man auch noch auf Klammer abfragen, aber viel mehr wird es dann nicht mehr und alles andere sollte eine Zahl sein. Und wenn ein Lesefehler auftritt, dann ist halt die Eingabe fehlerhaft. Da braucht man auch keine Exception, sondern nur den std::istream auf fail abfragen.

    Und das ganze sieht dann so aus, wie bei diesem Taschenrechnerprogramm. Das ist dann fast der gleiche Code, den Kellerautomat schon vorgelegt hat, nur mit dem Unterschied, dass man dann 'Zahl' auch zu 'irgendeine Größe' abstrahieren kann, und dann werden auch Dinge wie dies hier möglich.

    Gruß
    Werner



  • Sorry Werner, aber meinen Ansatz finde ich da viel schöner. 😉



  • Kellerautomat schrieb:

    Sorry Werner, aber meinen Ansatz finde ich da viel schöner. 😉

    Der eigene Code ist immer schöner - da bin ich ganz Deiner Meinung 😉
    Oder gibt es noch ein Argument für char* statt std::istream?

    Gruß
    Werner



  • Der Hauptgrund ist wohl, dass ich ctest nicht verstehe. 🤡
    Nach dem zweiten mal hinsehen finde ich es gar nicht mal so schlecht, vielleicht probiere ich das mal aus.



  • Kellerautomat schrieb:

    Sorry Werner, aber meinen Ansatz finde ich da viel schöner. 😉

    Doch, den Code von Werner finde ich schon sehr schick. Aber ich denke, das wird für den TE auch etwas schwer zu verdauen sein...



  • Mechanics schrieb:

    Kellerautomat schrieb:

    Sorry Werner, aber meinen Ansatz finde ich da viel schöner. 😉

    Doch, den Code von Werner finde ich schon sehr schick. Aber ich denke, das wird für den TE auch etwas schwer zu verdauen sein...

    Ja, da hast Du Recht. Das mit Templates kann man natürlich auch weglassen, mir ging es eben eher darum, auf die Verwendung von std::istream statt char* hinzuweisen.

    Kellerautomat schrieb:

    Der Hauptgrund ist wohl, dass ich ctest nicht verstehe.

    Ja - und genau das ist auch einer der Knackpunkte.
    Nur - es fragt kein Mensch hier im Forum, wie man das nächste Zeichen aus einem std::istream abfragt, ohne es 'zu lesen'. Aber jeden Monat kommt jemand mit der Frage wie man einen mathematischen Ausdruck aus einem String heraus holt, und mehr als zwei pro Woche fragen wie man aus einem (meist kurz vorher mit getline gelesenen) String Zahlen oder andere Werte heraus bekommt.
    Und die Antwortenden gehen meist auch immer drauf ein. Sie geben auch i.A. die passenden Antworten in Richtung Parser, und entsprechend Algorithmen, nur der Hinweis auf den std::istream, der für den Input gebaut wurde, das kommt selten. Wobei der std::istream weder alles kann, noch einfach zu handeln ist.

    Bei std::istream ist wohl einiges beim Marketing schief gelaufen.

    Gruß
    Werner



  • Mechanics schrieb:

    .. Aber ich denke, das wird für den TE auch etwas schwer zu verdauen sein...

    Ok - hier noch mal eine einfacherer Fassung:

    #include <iostream>
    
    struct Term // Produkt oder Ausdruck mit Division
    {
        Term( int& target ) : target_( &target ) {}
        int* target_;
    };
    struct Expression // der ganze Ausdruck, hier: Summe oder Differenz
    {
        Expression( int& target ) : target_( &target ) {}
        int* target_;
    };
    
    std::istream& operator>>( std::istream& in, Term t )
    {
        typedef int Wert;
        Wert x1;
        in >> x1;
        for( char op; in >> op; )
        {
            switch( op )
            {
            case '*':
                {
                    Wert x2;
                    if( in >> x2 )
                        x1 *= x2;
                }
                break;
            case '/':
                {
                    Wert x2;
                    if( in >> x2 )
                        x1 /= x2;
                }
                break;
            default:
                in.putback( op );
                *t.target_ = x1;
                return in;
            }
        }
        return in;
    }
    
    std::istream& operator>>( std::istream& in, Expression e )
    {
        typedef int Wert;
        Wert x1;
        in >> Term( x1 );
        for( char op; in >> op; )
        {
            switch( op )
            {
            case '+':
                {
                    Wert x2;
                    if( in >> Term( x2 ) )
                        x1 += x2;
                }
                break;
            case '-':
                {
                    Wert x2;
                    if( in >> Term( x2 ) )
                        x1 -= x2;
                }
                break;
            default:
                in.putback( op ); // kein Operator '+' '-', dann Zeichen zurück in den stream
                *e.target_ = x1;
                return in; // fertig
            }
        }
        return in;
    }
    
    int main()
    {
        using namespace std;
        for( int x; cout << "\nGeben Sie die Rechnung ein (Beenden mit ';'): ", cin >> Expression( x); cin.ignore( 1, '\n' ) )
            cout << "= " << x<< endl;
        return 0;
    }
    

    hallo astir,
    ist das verständlich?

    @Edit: Tippfehler '/n' -> '\n'



  • Hey,

    danke, aber glaub erklären könnte ich das nicht....wir haben auch soetwas noch gar nicht gemacht.

    Ich hab grad ganz andere Probleme. Erst funktioniert alles schön und dann kommt auf einmal beim Debuggen folgendes: "Das aufgerufene Projekt wurde von den Clients getrennt".
    Kann mir jemand sagen was da heißt, bzw. was ich evtl. falsch gemacht habe?

    Ist egrade echt alles deprimierend 😞


Anmelden zum Antworten