Grammatik Funtionen Taschenrechner
-
Hi Leute,
Der erste Beitrag und direkt ein Problem :)........Ich lerne seit einigen Monaten C++ mit einem Buch.
Nun programmieren wir gerade einen Taschenrechner.
Er soll die Eingaben des Benutzers in Token zerlegen und somit "vorrausschauen" können ob noch ein * oder ein / folgt.
Denn er beherrscht ja keine Punkt-vor-Strich Rechnung.
Er würde z.b. bei 5+2*2 , 20 herraus bekommen, obwohl das richtige Ergebnis ja 9 lautet.Die Grammatik lautet folgendermassen:
Ausdruck:
Term
Ausdruck "+" Term
Ausdruck "-" TermTerm:
Faktor
Term "*" Faktor
Term "/" Faktor
Term "%" FaktorFaktor:
Zahl
"(" Ausdruck ")"Zahl:
GleitkommaliteralDer Code sieht folgender maßen aus:
(Die Funktionen heißen anders: expression()=Ausdruk, Term()=Term,
primary()=Faktor)#include "std_lib_facilities.h" //------------------------------------------------------------------------------ class Token { public: char kind; // welche Kategorie von Token double value; // für Zahlen: ein Wert Token(char ch) // erstelle ein Token aus einem char :kind(ch), value(0) { } Token(char ch, double val) // erstelle ein Token aus einem char und einem double :kind(ch), value(val) { } }; //------------------------------------------------------------------------------ Token get_token() // lies ein Token aus cin { char ch; cin >> ch; // beachten Sie, dass >> Whitespace-Zeichen wie // Leerzeichen, Zeilenumbruch, Tabulatorzeichen, etc. überspringt switch (ch) { //noch kein case ';': // für "Ausgeben" //noch kein case 'q': // für "Verlassen" case '(': case ')': case '+': case '-': case '*': case '/': return Token(ch); // jedes Zeichen repräsentiert sich selbst case '.': 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('8',val); // '8' repräsentiert "eine Zahl" } default: error("Ungueltiges Token"); } } //------------------------------------------------------------------------------ double expression(); // liest einen Ausdruck ein und wertet ihn aus //------------------------------------------------------------------------------ double term(); // liest einen Token ein und wertet ihn aus //------------------------------------------------------------------------------ double primary() // liest einen Faktor ein und wertet ihn aus { Token t = get_token(); switch (t.kind) { case '(': // behandle'(' Ausdruck ')' { double d = expression(); t = get_token(); if (t.kind != ')') error("')' erwartet"); return d; } case '8': // wir verwenden '8' zur Repräsentation einer Zahl return t.value; // liefere den Wert der Zahl zurück default: error("Faktor erwartet"); } } //------------------------------------------------------------------------------ int main() try { while (cin) cout << expression() << '\n'; keep_window_open(); } catch (exception& e) { cerr << e.what() << endl; keep_window_open(); return 1; } catch (...) { cerr << "Ausnahme\n"; keep_window_open(); return 2; } //------------------------------------------------------------------------------ double expression() { double left = term(); // liest einen Token ein und wertet ihn aus Token t = get_token(); // hole das nächste Tolen while(true) { switch(t.kind) { case '+': left += term(); // werte Term aus und addiere t = get_token(); break; case '-': left -= term(); // werte Term aus und subtrahiere t = get_token(); break; default: return left; // keine weiteren + oder –; Antwort zurückliefern } } } //------------------------------------------------------------------------------ double term() { double left = primary(); Token t = get_token(); // hole das nächste Tolen while(true) { switch (t.kind) { case '*': left *= primary(); t = get_token(); break; case '/': { double d = primary(); if (d == 0) error("Division durch null"); left /= d; t = get_token(); break; } default: return left; } } } //------------------------------------------------------------------------------Das eigentliche Problem:
Immer der erste Befehl in den Funktionen expression(), term() und primary()
lässt mich rätseln...........Er lautet:
double left = term(); // liest einen Token ein und wertet ihn aus
Wie ist das zu verstehen?
Wird hier die doublevariable left mit term() initialisiert?
Welcher wert wird von left an term() übergeben und woher kommt er?
Übergibt left zum Beispiel den Wert '2', was macht dann term() damit bzw. was sendet term() zurück?Ich hoffe ihr könnt mir etwas weiter helfen.
Mfg Golfi1812
-
Diese Anweisung holt sich jeweils den ersten Summanden bzw. Faktor der Kette, indem es die jeweilige Funktion für die niederrangigeren Ausdrücke aufruft (expression() ruft term() auf, dieses wiederum primary(), das wiederum per get_token() eine Zahl oder Klammer aus der Eingabe rausholt).
-
Vielen Dank,
Doch eine Frage bleibt:
Woher nimmt double left seinen Wert?
Es muss ja einen Wert an term() liefern ,damit tern() ihn auswerten kann.
-
Der holt es von
primary()- und primary() bekommt den Wert entweder vonget_token()(wenn die nächste Eingabe eine Zahl ist - get_token() holt den Wert aus der Eingabe) oder rekursiv vonexpression()(die die nächste Eingabe eine Klammer ist).
-
Okay verstehe,
Aber die double variable left ist nicht Global, also gibt es insgesamt 2 davon in diesem Programm in expression() und in term()?
-
Richtig erkannt - genauso hast du drei verschiedene Variablen namens t in deinen Funktionen.
-
Okay Vielen Dank für deine schnelle Hilfe!
Echt super vor 1 Stunde angemeldet und innerhalb kurzer Zeit ein eher komplexes Problem gelöst Super! So bin ich das nicht gewöhnt!Ich werde nochmal den Code ausdrucken und mit handschriftlichen Kommentaren versehen um alles genau nachvollziehen zu können.
Vielen Dank

-
Dazu gibt es einen schönen Artikel im Magazin, wo ein solcher Taschenrechner entwickelt wird, der immer weitere Funktionen erhält: http://www.c-plusplus.net/forum/268247
Sollte also deine Frage absolut beantworten und dir vielleicht sogar noch mehr Wissen zum Thema beibringen.
-
Ich habe nochmal eine Frage:
Das ganze beginnt in der Funktion primary():
double primary() // liest einen Faktor ein und wertet ihn aus { Token t = get_token(); switch (t.kind) { case '(': // behandle'(' Ausdruck ')' { double d = expression(); t = get_token(); if (t.kind != ')') error("')' erwartet"); return d; } case '8': // wir verwenden '8' zur Repräsentation einer Zahl return t.value; // liefere den Wert der Zahl zurück default: error("Faktor erwartet"); } }Hier wird ja in Zeile 4 eine switchanwesiung begonnen.
Wenn das Token ein '(' ist, muss ja der Grammatik nach nach einem Ausdruck gesucht werden [Grammatikregel = '(' Ausdruck ')'].So wird dann in Zeile 7 eine doublevariable namens d mit expression() initialisiert. So weit so gut.
Hier wird expression() aufgerufen, expression() ruft dann wiederrum term() auf ,das wiederum primary() und primary beginnt den Kresilauf von neuem.
Das Programm würde also in einer Endlosschleife immer wieder die Funktionen nacheinander aufrufen. Das ist doch Unsinn! Habe ich etwa einen Denkfehler?
Mfg Golfi1812

-
Golfi1812 schrieb:
Das Programm würde also in einer Endlosschleife immer wieder die Funktionen nacheinander aufrufen. Das ist doch Unsinn! Habe ich etwa einen Denkfehler?
Dein Denkfehler liegt in der Funktionsweise der get_token() Funktion - diese liest und verschluckt jedes Mal einen Teil der Eingabe. Das heißt, wenn du das nächste Mal (drei Rekursionsstufen tiefer) wieder in der primary() ankommst, erhältst du andere Eingabedaten und kommst so möglicherweise in den
case '8':Zweig.Worüber ich mir gerade Gedanken mache: Die term() und expression() brechen mit ihrer Arbeit ab, wenn das nächste eingelesene Token nicht passt. Dann sieht die jeweils aufrufende Funktion aber nicht, welches Zeichen hinter dem verarbeiteten Teil der Eingabe stand.
PS: Ich schließe mich mal der Aussage von Tipgeber an - lies den Magazin-Artikel.