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;
    }
    

Log in to reply