Taschenrechner mit integriertem "Prim_Check"
-
Hey C++ler,
ich habe mit Hilfe des Buches:"Einführung in die Programmierung mit C++",von Bjarne Stroustrup einen Taschnrechner entwickelt. Er besitzt einen meiner nach vernünftigen Fehlerabfangmechanismus, eine "Hilfe" Option,die Möglichkeit für den Benutzer eigene Variabalen zu definieren und zu benutzen,eine integrierte "Prim_überprüfungs" Option und er kann natürlich auch jegliche Ausdrücke ausrechnen.
Ich habe unteranderem die "Prim_Check" Option nachträglich eingefügt. Sie funktioniert auch so weit, doch eine Sache wurmt mich:
Wenn man 'c' eingibt um den "Checker" zu öffnen dann die zutestende Zahl eingibt, bekommt der Benutzer zurück ob es sich um eine Primzahl handelt oder nicht...so weit so gut....doch nach der Ausgabe:"....ist eine/keine Primzahl" ,fügt der Rechner immer noch die Fehlermeldung:"Faktor erwartet" an.
Ich bin das mehrmals durchgegangen, aber ich finde den Grund nicht, stehe grade aufm Schlauch.Hier der Code:
#include "../../../std_lib_facilities.h" #include <cmath> #include "conio.h" /* Einfacher Taschenrechner Die Grammatik für die Eingabe lautet: Berechnung: Anweisung Ausgeben Verlassen Berechnung Anweisung Anweisung: Deklaration Ausdruck Deklaration: "let" Name "=" Ausdruck Anweisung: Ausdruck Ausgeben Verlassen Ausgeben: ; Verlassen: q Ausdruck: Term Ausdruck + Term Ausdruck – Term Term: Faktor Term * Faktor Term / Faktor Term % Faktor Faktor: Zahl Name ( Ausdruck ) – Faktor + Faktor Zahl: Gleitkommaliteral Die Eingabe kommt von cin über den Token_stream namens ts. */ //------------------------------------------------------------------------------ const char number = '8'; // t.kind==number bedeutet, dass t ein Zahlen-Token ist const char quit = 'q'; // t.kind==quit bedeutet, dass t ein Verlassen-Token ist const char print = ';'; // t.kind==print bedeutet, dass t ein Ausgeben-Token ist const char name = 'a'; // Name-Token const char let = 'L'; // Deklaration-Token const string declkey = "let";// Schlüsselwort für Deklarationen const string prompt = "> "; const string result = "= "; // zeigt an, dass danach ein Ergebnis folgt const char prim = 'c'; const char Help = 'h'; //------------------------------------------------------------------------------ class Token { public: char kind; // welche Kategorie von Token double value; // für Zahlen: ein Wert string name; // für Namen: der Name selbst 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); // verwirf Zeichen bis und einschließlich des nächsten Vorkommens von c private: bool full; // befindet sich ein Token im Puffer? Token buffer; // hier legen wir ein Token ab, das mit putback() zurückgestellt wurde }; //------------------------------------------------------------------------------ // 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() // liest Zeichen aus cin und baut Token auf { if (full) { // prüfe, ob es bereits ein fertiges Token gibt full=false; return buffer; } char ch; cin >> ch; // beachten Sie, dass >> Whitespace-Zeichen wie // Leerzeichen, Zeilenumbruch, Tabulatorzeichen, etc. überspringt switch (ch) { case quit: case print: case prim: case Help: case '(': case ')': case '+': case '-': case '*': case '/': case '%': case '=': return Token(ch); // jedes Zeichen repräsentiert sich selbst case '.': // ein Gleitkommaliteral 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': // numerisches Literal { 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)) { 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 die gesuchte Token-Kategorie { // zuerst ein Blick in den Puffer: if (full && c==buffer.kind) { full = false; return; } full = false; // now search input: char ch = 0; while (cin>>ch) if (ch==c) return; } //------------------------------------------------------------------------------ Token_stream ts; // stellt get() und putback() zur Verfügung //------------------------------------------------------------------------------ class Variable { public: string name; double value; Variable (string n, double v) :name(n), value(v) { } }; //------------------------------------------------------------------------------ vector<Variable> var_table; //------------------------------------------------------------------------------ double get_value(string s) // liefert den Wert der Variablen mit dem Namen s zurück { 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); } //------------------------------------------------------------------------------ void set_value(string s, double d) // weist der Variablen mit dem Namen s den Wert d zu { 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); } //------------------------------------------------------------------------------ bool is_declared(string var) // ist var bereits in var_table? { 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) // füge (var,val) in var_table ein { if (is_declared(var)) error(var," doppelt deklariert"); var_table.push_back(Variable(var,val)); return val; } //------------------------------------------------------------------------------ double expression(); // Deklaration, damit primary() expression() aufrufen kann //------------------------------------------------------------------------------ // behandelt Zahlen und Klammern double primary() { Token t = ts.get(); switch (t.kind) { case '(': // behandle'(' Ausdruck ')' { double d = expression(); t = ts.get(); if (t.kind != ')') error("')' erwartet"); return d; } case number: return t.value; // liefere den Wert der Zahl zurück case name: return get_value(t.name); // liefere Wert der Variablen zurück case '-': return - primary(); case '+': return primary(); default: error("Faktor erwartet"); } } //------------------------------------------------------------------------------ // behandelt *, / und % 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 '%': { int i1 = narrow_cast<int>(left); int i2 = narrow_cast<int>(term()); if (i2 == 0) error("%: Division 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 } } } //------------------------------------------------------------------------------ double declaration() // behandle: name = ausdruck // deklariere eine Variable namens "name" mit dem Anfangswert "ausdruck" { Token t = ts.get(); if (t.kind != name) error ("Deklaration ohne Name"); string var_name = t.name; Token t2 = ts.get(); if (t2.kind != '=') error("= fehlt in der Deklaration von ", var_name); double d = expression(); define_name(var_name,d); return d; } //------------------------------------------------------------------------------ double statement() { Token t = ts.get(); switch (t.kind) { case let: return declaration(); default: ts.putback(t); return expression(); } } //------------------------------------------------------------------------------ void clean_up_mess() { ts.ignore(print); } //----------------------------------------------------------------------------- bool check(double n){ if (floor(n) == ceil(n)){ //rudet n auf und ab und vergleicht die Ergebnisse return false; } else{ return true; } } void Prim(){ cout<<"Geben Sie die zutestende Zahl ein: "; bool i; double n; double r; int x = 0; double t =1; cin>>n; while(t<=n){ r = n/t; i = check(r); //i= test ob r integer oder double (0=true / 1=false) if(i==0){ ++x; } ++t; } if(x==2){ //wenn r zweimal integer war dann n=Primzahl cout<<n<<" ist eine Primzahl!"; } else{ cout<<n<<" ist keine Primzahl!"; } if(n<=0){ //Fehlerausgabe bei negativer (oder 0) Eingabe cout<<"Fehler: Die Eingabe muss größer als 0 sein! "; } } //------------------------------------------------------------------------------ void help(); void calculate() { while (cin) try { cout << prompt; Token t = ts.get(); while (t.kind == print) t=ts.get(); // zuerst alle Ausgabe-Befehle verwerfen if (t.kind == prim)Prim();//Prim aufrufen if (t.kind == quit) return;// Programm verlassen if (t.kind == Help)help();//help aufrufen ts.putback(t); cout << result << statement() << endl; } catch (exception& e) { cerr << e.what() << endl; // Fehlermeldung ausgeben clean_up_mess(); } } //------------------------------------------------------------------------------ void help() { cout<<"............Help............"<<endl; cout<<endl; cout<<"........Operationen........"<<endl; cout<<endl; cout<<"+"<<endl; cout<<"-"<<endl; cout<<"*"<<endl; cout<<"/"<<endl; cout<<"%"<<endl; cout<<"........Constants........"<<endl; cout<<"pi"<<endl; cout<<"e"<<endl; cout<<"........Options........"<<endl; cout<<"Enter..."<<endl; cout<<"...'c' to open the Prim_Check"<<endl; cout<<"...'let' to define new variables. Example: 'let x = 2'"<<endl; cout<<"...'q' to exit"<<endl; getch(); calculate(); } //------------------------------------------------------------------------------ int main() try { // predefine names: define_name("pi",3.1415926535); define_name("e",2.7182818284); calculate(); keep_window_open(); // berücksichtige den Windows-Konsolenmodus return 0; } catch (exception& e) { cerr << e.what() << endl; keep_window_open("~~"); return 1; } catch (...) { cerr << "Ausnahme\n"; keep_window_open("~~"); return 2; } //-----------------------------------------------------------------------------
Anbei noch eine Frage: Gibt es einen "clear_screen" befehl, der den Konsoleninhalt löscht damit man danach weiter machen kann? ich würde so etwas gerne nach dem Aufruf von "help()" einbauen, da es nervt wenn dann die ganzen Help-Hinweise in der Ausgabe bleiben.
-
Mit dem clean_screen aht sich erledigt, nach ein wenig googeln und ausprobieren, bin ich auch
system("cls");
gestoßen. Klappt wunderbar.
Doch die unerwünschte Fehlermeldung bleibt....
-
1. Kürze deinen Code auf das Wesentliche
2. Versuch, die while-Schleife in Prim() (und damit auch die check()-Rechnerei) durch diese zu ersetzen:
while (t <= n) { if (!(n % t)) ++x; //optional: Frühzeitiges Verlassen der Schleife //if (x > 2) // break; ++t; }