(nochmal) Taschenrechner Stroustrup Kapitel 7



  • Hallo,

    ich geh vielen sicher auf den Wecker, aber trotzdem brauch ich nochmal Hilfe. Ich bin immer noch am Taschenrechner von Stroustrup, diesmal Kapitel 7.

    Die Grundrechenarten +-*/ und ! sind drin und funktionieren einwandfrei (soweit ich das getest habe). Negative Zahlen klappen ebenfalls. Im letzten Schritt wollte ich die Möglichkeit einbauen, dass der Nutzer eigene Variablen definieren und damit rechnen kann.

    Mein Code entspricht dem aus dem Stroustrup-Buch und trotzdem klappt es nicht. Einzig die Fakultätsberechnung ist hier aus dem Forum. Daher habe ich sie auskommentiert. War aber leider nicht die Fehlerursache.

    Beim Debuggen habe ich bemerkt, dass die Variablen gar nicht erstellt werden - soll heißen, die dort im Code gesetzten Haltepunkte werden gar nicht angetastet. Irgendwie fehlt wohl eine Schnittstelle.

    #include "std_lib_facilities.h"
    
    struct Token{
    	char kind;
    	double value;
    	string name;
    	Token(char ch):kind(ch),value(0){}
    	Token(char ch,double val):kind(ch),value(val){}
    	Token(char ch, string n):kind(ch),name(n){}
    };
    
    class Token_stream {
    public:
       Token_stream();			// erstelle einen Token_stream, der aus cin liest 
       Token get();				// lies ein Token ein (get() ist anderswo definiert)
       void putback(Token t);	// lege ein Token zurück 
       void ignore(char c);
    private:
       bool full;				// befindet sich ein Token im Puffer?
       Token buffer;			// hier legen wir ein Token ab, das mit putback() 
    							// zurückgestellt wurde
    };
    
    //Erstellung eines Name-Wert-Paares zur Speicherung von konstanten Werten,
    //die der Nutzer selbst eingeben kann
    class Variable{
    public:
    	string name;
    	double value;
    
    	Variable(string n, double val):name(n),value(val){}
    };
    
    //Definition eines Vektors für Variablen
    vector<Variable> var_table; //Typ: Variable (siehe Definition class Variable)
    
    //Definition der Konstanten
    const char name = 'a';
    const char let = 'L';
    const string declkey = "let";
    
    const char number = '8'; // t.kind == number bedeutet, dass t ein Zahl-Token ist
    const char quit   = 'q'; // t.kind == quit bedeutet, dass t ein Verlassen-Token ist
    const char print  = ';'; // Ausgeben-Token
    const char prompt = '>'; // Eingabeauforderung
    const char result = '='; // Ergebnisanzeige
    
    // Der Konstruktor setzt full auf false, um anzuzeigen, dass der Puffer leer ist:
    Token_stream::Token_stream()
    :full(false), buffer(0)    // kein Token im Puffer
    {
    }
    
    // Die Memberfunktion putback() stellt ihr Argument zurück in den Puffer von Token_stream:
    void Token_stream::putback(Token t)
    {
        if (full) error("putback(): Zurueckstellen nicht moeglich, Puffer voll");
        buffer = t;       // kopiere t in den Puffer
        full = true;      // Puffer ist jetzt voll
    }
    
    Token Token_stream::get()
    {
        if (full) {       // gibt es bereits ein fertiges Token?
            // Token aus dem Puffer entfernen
            full=false;
            return buffer;
        } 
    
        char ch;
        cin >> ch;				// beachten Sie, dass >> Whitespace-Zeichen wie 
    							// Leerzeichen, Zeilenumbruch, Tabulatorzeichen, etc. überspringt
    
        switch (ch) {
        case print:				// für "Ausgeben"
        case quit:				// für "Verlassen"
    	case let:
    	case name:
    	case '(':	
    	case ')': 
    	case '+':
    	case '-': 
    	case '*':
    	case '/': 
    	case '{': 
    	case '}': 
    	case '!': 
    	case '%':
    	case '=':
            return Token(ch);	// jedes Zeichen repräsentiert sich selbst 
        case '.':				// Gleitkommliteral kann mit einem Punkt . beginnen
        case '0': case '1': case '2': case '3': case '4':
    	case '5': case '6': case '7': case '8': case '9':
            {    
                cin.putback(ch);			 // lege die Ziffer zurück in den Eingabestream
                double val;
                cin >> val;					 // lies eine Gleitkommazahl 
                return Token(number,val);   
            }
        default:
    		if(isalpha(ch))
    		{
    			cin.putback(ch);
    			string s;
    			s += ch;
    
    			while(cin.get(ch) && (isalpha(ch) || isdigit(ch)))
    				s += ch;
    			cin.putback(ch);
    
    			if(s == declkey)
    				return Token(let);		//Schlüsselwort für Deklarationen
    			return Token(name,s);
    		}
            error("Ungueltiges Token");
        }
    }
    
    void Token_stream::ignore(char c)
    			// c repräsentiert eine gesuchte Token-Kategorie
    {
    	//erster Blick in den Puffer
    	if (full && c == buffer.kind)
    	{
    		full = false;
    		return;
    	}
    	full = false;
    
    	//Eingabe durchsuchen
    	char ch = 0;
    	while(cin >> ch)
    		if(ch == c) return;
    }
    
    //------------------------------------------------------------------------------
    
    double fak (double val)	 //berechnet rekursiv die Fakultät einer Zahl
    						 // rekursiv: Die Funktion ruft sich selbst auf
    {
    	if (val > 1) 
    	{
    		return fak(val-1) * val;
        }
    	else
    		return 1;
    }
    
    //------------------------------------------------------------------------------
    
    Token_stream ts;        // stellt get() und putback() zur Verfügung  
    
    //------------------------------------------------------------------------------
    
    double expression();    // Deklaration, damit primary() expression() aufrufen kann
    
    //------------------------------------------------------------------------------
    
    double factorial();		// Deklaration, damit term() factorial() aufrufen kann
    
    //------------------------------------------------------------------------------
    
    bool is_declared(string var) // gibt es bereits var?
    {
    	for(int i=0;i<var_table.size(); i++)
    		if(var_table[i].name == var) return true;
    	return false;
    }
    
    double define_name(string var, double val) // (var, val) hinzufügen
    {
    	if(is_declared(var)) error(var,"doppelt deklariert");
    	var_table.push_back(Variable(var,val));
    	return val;
    }
    
    //------------------------------------------------------------------------------
    
    // behandelt Zahlen und Klammern 
    double primary()
    {
        Token t = ts.get();
    
        switch (t.kind) {
    	case '{': 
    		 {    
                double d = expression();
                t = ts.get();
    			if (t.kind != '}') error("'}' expected");
                return d;
            }
        case '(':    // behandle'(' Ausdruck ')'
            {    
                double d = expression();
                t = ts.get();
                if (t.kind != ')') error("')' expected");
                return d;
            }
    	case number:     
    		return t.value; // liefert den Zahlenwert zurück
    	case '-':
    		return -primary();
    	case '+':
    		return primary();
        default:
            error("Faktor erwartet");
        }
    }
    
    //------------------------------------------------------------------------------
    
    // behandelt *, /
    double term()
    {
        double left = primary();
        Token t = ts.get();        // lies das nächste Token aus dem Token-Stream ein
    
        while(true) {
            switch (t.kind) {
            case '*':
                left *= primary();
                t = ts.get();
    			break;
            case '/':
                {    
                    double d = primary();
                    if (d == 0) error("Division durch null");
                    left /= d; 
                    t = ts.get();
                    break;
                }
    		case '%':
    			{
    				double d = primary();
    				int i1 = int(left);
    				if(i1 != left) error("Linker Operand von Modulo % ist keine ganze Zahl");
    
    				int i2 = int(d);
    				if(i2 != d) error("Rechter Operand von Modulo % ist keine ganze Zahl");
    				if(i2 == 0) error("Divison durch Null");
    				left = i1%i2;
    				t = ts.get();
    				break;
    			}
    		default: 
                ts.putback(t);     // stelle t wieder zurück in den Token-Stream
                return left;
            }
        }
    }
    
    //------------------------------------------------------------------------------
    
    // behandelt + und –
    double expression()
    {
        double left = term();       // liest einen Token ein und wertet ihn aus
        Token t = ts.get();        // lies das nächste Token aus dem Token-Stream ein
    
        while(true) {    
            switch(t.kind) {
            case '+':
                left += term();    // werte Term aus und addiere
                t = ts.get();
                break;
            case '-':
                left -= term();    // werte Term aus und subtrahiere
                t = ts.get();
                break;
            default: 
                ts.putback(t);     // stelle t wieder zurück in den Token-Stream
                return left;       // keine weiteren + oder –; Antwort zurückliefern
            }
        }
    }
    
    //------------------------------------------------------------------------------
    
    //behandelt die Fakultät als Faktor!
    // jetzt alle primary() durch factorial() ersetzen
    /*double factorial()
    {
        double n = primary(); // Hier rekursiv weiter zu primary
    
        Token t = ts.get();
    
        while(t.kind == '!') 
    	{
            n = fak(n);
    		t = ts.get();
        }
    
        ts.putback(t);
    
        return n;
    }*/
    
    //--------------------------------------------------------------------------------
    //Deklaration: Stellt sicher, dass auf let ein Name folgt, dann =, dann Ausdruck
    
    double declaration()
    	//Voraussetzung: let gesehen, nun Behandlung name = ausdruck
    	//deklariere eine Variable namens "name" mit dem Anfangswert "ausdruck"
    {
    	Token t = ts.get();
    	if(t.kind!=name) 
    		error("Die Deklaration erwartet einen Namen.");
    	string var_name = t.name;
    
    	Token t2 = ts.get();
    	if(t2.kind != '=') 
    		error("Das = fehlt in der Deklaration von", var_name);
    
    	double d = expression();
    	define_name(var_name,d);
    	return d;
    
    }
    
    //-----------------------------------------------------------------------
    //Anweisung behandelt Ausdrücke und Deklarationen
    
    double statement()
    {
    	Token t = ts.get();
    	switch(t.kind)
    	{
    	case let:
    		return declaration();
    	default:
    		ts.putback(t);
    	return expression();
    	}
    }
    
    //---------------------------------------------------------------------------------
    //Aufräumen nach Tippfehler und Neustart
    void clean_up_mess()
    {
    	ts.ignore(print);
    }
    
    //---------------------------------------------------------------------------------
    //Durchsucht den Vektor var_table nach Namen und gibt die zugehörigen Werte zurück
    double get_value(string s)
    {
    	for(int i =0 ; i < var_table.size() ; i++)
    	{
    		if(var_table[i].name == s)
    			return var_table[i].value;
    		error("get: Nicht definierte Variable",s);
    	}
    }
    
    //------------------------------------------------------------------------------
    //Weist Variablen im Vektor var_table einen (neuen) Wert zu
    void set_value(string s, double d)
    {
    	for(int i=0; i < var_table.size(); i++)
    	{
    		if(var_table[i].name == s)
    		{
    			var_table[i].value = d;
    			return;
    		}
    
    		error("set: Nicht definierte Variable",s);
    	}
    
    }
    
    //-----------------------------------------------------------------------------
    //Schleife zur Auswertung der Ausdrücke
    
    void calculate()
    {
    	while (cin) 
    	try{
    		cout << prompt;
            Token t = ts.get();
    		while(t.kind == print) t = ts.get();
    		if (t.kind == quit) return;
    
    		ts.putback(t);
            cout << result << statement() << endl;
        }
    	catch (exception& e) 
    	{
        cerr << "Fehler: " << e.what() << '\n'; 
    			//keep_window_open();
    	clean_up_mess();
    	}
    }
    
    //-----------------------------------------------------------------------------
    
    int main()
    try
    {    
    	calculate();
    
    	keep_window_open();
    	return 0;
    }
    catch (...) {
        cerr << "Hoppla: unbekannte Ausnahme!\n"; 
    			// keep_window_open();
    	cout << "Um das Fenster zu schliessen ~ eingeben.\n";
    	char ch;
    	while(cin >> ch)
    		if(ch == '~') return 2;
        return 2;
    }
    

    Ich sitze echt schon 2 Tage dran und finde es einfach nicht. Ist auch etwas viel und damit unübersichtlich geworden. Falls jemand helfen kann, wär ich wirklich dankbar. Ein Hinweis genügt mir hoffentlich schon.



  • Das ist ein tatsächlich ein wenig viel Code...
    Wenn ich mir allerdings den Teil anschaue, in dem Variablen eingeführt werden, fällt mir auf, dass ch zweimal in den String gepackt wird.

    Lieschen Müller schrieb:

    Token Token_stream::get()
    {
        //...
        default:
    		if(isalpha(ch))
    		{
    			cin.putback(ch);
    			string s;
    			s += ch;
    
    			while(cin.get(ch) && (isalpha(ch) || isdigit(ch)))
    				s += ch;
    			cin.putback(ch);
    
    			if(s == declkey)
    				return Token(let);		//Schlüsselwort für Deklarationen
    			return Token(name,s);
    		}
        //...
    }
    


  • Stimmt!! Danke, das habe ich total übersehen. Hätte halt nicht einfach abschreiben sollen.

    Es fehlte auch noch eine Zeile in primary()

    double primary()
    {
        Token t = ts.get();
        switch (t.kind) {
            //...
        case name:
            return get_value(t.name); // liefere Wert der Variablen zurück
           //...
        default:
            error("Faktor erwartet");
        }
    }
    

    Jetzt klappt's. Vielen Dank, Furble Wurble.


Anmelden zum Antworten