(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, dasschzweimal 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.