Kleiner "Taschenrechner" (als Quellcode)



  • Hallo!
    Es ist vollbracht. Mein erstes groesseres C++-Programm ist fertig. Es handelt sich hierbei um ein Programm, dass mathematische Terme wie

    -32,4+(34,32/2+(34*3,5+2)/5)
    

    berechnet, dabei natuerlich die Klammern und Punkt-Vor-Strich beachtet. Minuszahlen sind auch moeglich (zumindest am Anfang des Terms sowie zu Beginn jeder Klammer - an einem anderen Platz wuerden sich Minuszahlen ja sowieso aufloesen, deswegen hab ich mir das mal so "erleichert") sowie auch Kommazahlen in zweierlei Formen: "3,5" oder "3.5".
    Whitespaces werden ignoriert.

    Es geht mir vor allem um folgendes (das ist auch der Hauptgrund fuer die Threaderoeffnung): Ich finde ich habe alles viel zu kompliziert gemacht, ich denke man haette es auch einfacher hinkriegen koennen, aber dafuer fehlt mir wohl die Erfahrung in C++... Ist der Weg den ich gegangen bin (Vererbung) der richtige oder waere es besser gewesen das anders zu loesen?
    An welchen Stellen hab ich Mist gemacht (im Sinne der objektorientierten Programmierung - das Programm selbst funktioniert ja). Das sind so die Fragen, die ich an die fortgeschritteneren C++-Programmierern habe. Vielleicht hat ja einer Lust diesen Riesen Code durchzugucken.
    Kritik erwuenscht.

    Rechner.h

    #ifndef RECHNER_H
    #define RECHNER_H
    
    #include <string>
    #include <sstream>
    #include <vector>
    #include <cstddef>
    #include <cassert>
    
    // Rechner ohne Klammerunterstuetzung
    // (beruecksichtigt Punkt-Vor-Strich)
    class SimpleRechner
    {
    public:
    	SimpleRechner() : Rechnung("") {}
    	SimpleRechner(const std::string& rech) : Rechnung(rech) {}
    	virtual ~SimpleRechner() {}
    	void Put(const std::string& rech) { Rechnung += rech; }
    	void Reset() { Rechnung = ""; }
    	virtual bool Check(std::string*) const;	//Syntaxueberpruefung
    	virtual long double GetResult() const;
    private:
    	std::string Rechnung;
    protected:
    	const std::string& GetRechnung() const { return Rechnung; }
    	std::string RemoveWhiteSpaces(const std::string&) const;
    	bool findPunkt(const std::vector<char>&) const;
    	bool findStrich(const std::vector<char>&) const;
    
    	template<class T>
    	void RemoveVectorElement(std::vector<T>&, size_t) const;
    
    	void Aufteiler(std::vector<std::string>&, std::vector<char>&) const;
    };
    
    // Membertemplate
    template<class T>
    void SimpleRechner::RemoveVectorElement(std::vector<T>& vec, size_t pos) const
    {
    	std::vector<T> newvec;
    	for (size_t i=0; i<vec.size(); i++)
    		if (i != pos)
    			newvec.push_back(vec[i]);
    	vec = newvec;
    }
    
    #endif
    

    Rechner.cpp

    #include "Rechner.h"
    
    bool SimpleRechner::Check(std::string* fehlermeldung) const
    {
    	std::string Rech = RemoveWhiteSpaces(Rechnung);
    	bool Operator = false;
    	bool Zahl = false;
    	bool Komma = false;
    	size_t start = 0;
    
    	if (Rech.size() > 0 && Rech[0] == '-') start = 1;	// fuer negative Zahlen
    
    	for (size_t i=start; i<Rech.size(); i++)
    	{
    		if (Rech[i] >= '0' && Rech[i] <= '9')
    		{
    			Zahl = true;
    			Operator = false;
    		}
    		else if (Rech[i] == ',' || Rech[i] == '.')
    		{
    			if (!Zahl)
    			{
    				if (fehlermeldung)
    					*fehlermeldung = "Komma ohne L-Wert. (Meinten Sie 0,[...]?)";
    				return false;
    			}
    			else if (Komma)
    			{
    				if (fehlermeldung)
    					*fehlermeldung = "Nur ein Komma pro Zahl gestattet!";
    				return false;
    			}
    			else
    			{
    				Komma = true;
    				Operator = false;
    			}
    		}
    		else if (Rech[i] == '+' || Rech[i] == '-' || Rech[i] == '*' || Rech[i] == '/')
    		{
    			if (!Zahl)
    			{
    				if (fehlermeldung)
    					*fehlermeldung = "Operator ohne L-Wert!";
    				return false;
    			}
    			else if (Operator)
    			{
    				if (fehlermeldung)
    					*fehlermeldung = "Doppelter Operator";
    				return false;
    			}
    			else if (Rech[i-1] == '.' || Rech[i-1] == ',')
    			{
    				if (fehlermeldung)
    					*fehlermeldung = "Komma ohne R-Wert";
    				return false;
    			}
    			else
    			{
    				Operator = true;
    				Zahl = false;
    				Komma = false;
    			}
    		}
    		else
    		{
    			if (fehlermeldung)
    				*fehlermeldung = "Nicht erlaubtes Zeichen enthalten";
    			return false;
    		}
    	}
    
    	if (!Zahl)
    	{
    		if (fehlermeldung)
    			*fehlermeldung = "Es fehlt die letzte Zahl";
    		return false;
    	}
    	else if (Komma && Rech[Rech.size()-1] == '.' || Rech[Rech.size()-1] == ',')
    	{
    		if (fehlermeldung)
    			*fehlermeldung = "Komma ohne R-Wert.";
    		return false;
    	}
    	return true;
    }
    
    std::string SimpleRechner::RemoveWhiteSpaces(const std::string& rech) const
    {
    	std::string newstr;
    	for (size_t i=0; i<rech.size(); i++)
    		if (rech[i] != ' ')
    			newstr.push_back(rech[i]);
    	return newstr;
    }
    
    void SimpleRechner::Aufteiler(std::vector<std::string>& zahl, std::vector<char>& op) const
    {
    	bool Z = false;
    	size_t start = 0;
    	if (Rechnung.size() > 0 && Rechnung[0] == '-') {
    		zahl.push_back("-");
    		size_t i = 0;
    		for (i=1; i<Rechnung.size(); i++) {
    			if ((Rechnung[i] >= '0' && Rechnung[i] <= '9') || Rechnung[i] == '.' || Rechnung[i] == ',' ||
    				Rechnung[i] == ' ') {
    				if (Rechnung[i] == ',') zahl[zahl.size()-1].push_back('.');
    				else zahl[zahl.size()-1].push_back(Rechnung[i]);
    			}
    			else
    				break;
    		}
    		start = i;
    	}
    
    	for (size_t i=start; i<Rechnung.size(); i++)
    	{
    		if (Rechnung[i] == '+' || Rechnung[i] == '-' || Rechnung[i] == '*' || Rechnung[i] == '/')
    		{
    			op.push_back(Rechnung[i]);
    			Z = false;
    		}
    		else if ((Rechnung[i] >= '0' && Rechnung[i] <= '9') || Rechnung[i] == '.' || Rechnung[i] == ',')
    		{
    			char temp;
    			if (Rechnung[i] == ',') temp = '.'; else temp = Rechnung[i];
    			if (Z)
    				zahl[zahl.size()-1].push_back(temp);
    			else
    			{
    				zahl.push_back("");
    				zahl[zahl.size()-1].push_back(temp);
    				Z = true;
    			}
    		}
    	}
    }
    
    bool SimpleRechner::findPunkt(const std::vector<char>& vec) const
    {
    	for (size_t i=0; i<vec.size(); i++)
    		if (vec[i] == '*' || vec[i] == '/')
    			return true;
    	return false;
    }
    
    bool SimpleRechner::findStrich(const std::vector<char>& vec) const
    {
    	for (size_t i=0; i<vec.size(); i++)
    		if (vec[i] == '+' || vec[i] == '-')
    			return true;
    	return false;
    }
    
    long double SimpleRechner::GetResult() const
    {
    	assert(Check(NULL));
    	std::vector<std::string> Zahlen;
    	std::vector<char> Oper;
    	Aufteiler(Zahlen, Oper);
    
    	std::istringstream input1, input2;	// Umwandeln der Zahl-Strings zum rechnen
    	long double in1, in2;					// fuer die istreams
    
    	long double Erg = 0;						// Ergebnis einzelner Aufgaben
    
    	// Punkt-Aufloesen
    	while (findPunkt(Oper))		// Schleife solange ausfuehren, bis keine Punkt-Aufgaben vorhanden sind
    	for (size_t i=0; i<Oper.size(); i++)
    	{
    		if (Oper[i] == '*' || Oper[i] == '/')
    		{
    			input1.clear(); input2.clear();
    			input1.str(Zahlen[i]);
    			input1 >> in1;
    			input2.str(Zahlen[i+1]);
    			input2 >> in2;
    
    			if (Oper[i] == '*')
    				Erg = in1 * in2;
    			else
    				Erg = in1 / in2;
    
    			std::ostringstream output;
    			output << Erg;
    			Zahlen[i] = output.str();	// Ergebnis rein und alles andere wegloeschen
    			RemoveVectorElement(Zahlen, i+1);
    			RemoveVectorElement(Oper, i);
    			break;	// nochmal von vorn
    		}
    	}
    
    	// Rest aufloesen
    	// dasselbe wie oben nur mit den Strich-Operatoren
    	while (findStrich(Oper))
    	for (size_t i=0; i<Oper.size(); i++)
    	{
    		if (Oper[i] == '+' || Oper[i] == '-')
    		{
    			input1.clear(); input2.clear();
    			input1.str(Zahlen[i]);
    			input1 >> in1;
    			input2.str(Zahlen[i+1]);
    			input2 >> in2;
    
    			if (Oper[i] == '+')
    				Erg = in1 + in2;
    			else
    				Erg = in1 - in2;
    
    			std::ostringstream output;
    			output << Erg;
    			Zahlen[i] = output.str();
    			RemoveVectorElement(Zahlen, i+1);
    			RemoveVectorElement(Oper, i);
    			break;
    		}
    	}
    
    	Erg = 0;
    	// Rest zusammenzaehlen - i.d.R. duerfte nur ein Element uebrig bleiben...
    	for (size_t i=0; i<Zahlen.size(); i++)
    	{
    		input1.clear();
    		input1.str(Zahlen[i]);
    		input1 >> in1;
    		Erg += in1;
    	}
    
    	return Erg;
    }
    

    Calc.h

    #ifndef CALC_H
    #define CALC_H
    
    #include "Rechner.h"
    
    class Calc : public SimpleRechner
    {
    public:
    	Calc() : SimpleRechner("") {}	// Standardkonstruktor
    	Calc(const std::string&);	// Allg. konstruktor
    	Calc(const Calc&);			// Kopierkonstruktor
    
    	void SetVarState(bool);
    	bool GetVarState() const;
    	void push_var(const std::string&);
    	bool delete_var(const std::string&);
    	size_t count_var() const;
    	std::string& return_var(size_t);
    
    	virtual bool Check(std::string*);					// ueberschreiben
    	virtual long double GetResult();							// ueberschreiben
    	// Rest erben
    private:
    	std::vector<std::string> Vars;		// Array fuer Variablen, die durch Zahlen ersetzt werden
    	bool VarState;						// ob mit Variablen gerechnet werden soll.
    
    	const std::vector<std::string>& GetVectorVar() const;
    	size_t findLastOpen(const std::string&) const;
    	size_t findFirstClose(const std::string&, size_t pos) const;
    };
    
    inline Calc::Calc(const std::string& rech) : SimpleRechner(rech), VarState(false)
    {}
    
    inline Calc::Calc(const Calc& c)
    {
    	Reset(); Put(c.GetRechnung());
    	SetVarState(c.GetVarState());
    	Vars = GetVectorVar();
    }
    
    #endif
    

    Calc.cpp

    #include "Calc.h"
    
    void Calc::SetVarState(bool state)
    {
    	VarState = state;
    }
    
    bool Calc::GetVarState() const
    {
    	return VarState;
    }
    
    void Calc::push_var(const std::string& var)
    {
    	Vars.push_back(var);
    }
    
    bool Calc::delete_var(const std::string& var)
    {
    	bool found = false;
    
    	std::vector<std::string> vec;
    	for (size_t i=0; i<Vars.size(); i++)
    	{
    		if (Vars[i] != var)
    			vec.push_back(Vars[i]);
    		else
    			found = true;
    	}
    	Vars = vec;
    	return found;
    }
    
    size_t Calc::count_var() const
    {
    	return Vars.size();
    }
    
    std::string& Calc::return_var(size_t pos)
    {
    	assert(pos < Vars.size());
    	return Vars[pos];
    }
    
    size_t Calc::findLastOpen(const std::string& s) const
    {
    	size_t pos = 0;
    	for (size_t i=0; i<s.size(); i++)
    		if (s[i] == '(')
    			pos = i;
    	return pos;
    }
    
    size_t Calc::findFirstClose(const std::string& s, size_t pos=0) const
    {
    	for (size_t i=pos; i<s.size(); i++)
    		if (s[i] == ')')
    			return i;
    	return 0;
    }
    
    bool Calc::Check(std::string* fehlermeldung)
    {
    	std::string news = RemoveWhiteSpaces(GetRechnung());
    	Reset(); Put(news);
    	bool result;
    	std::string rech = GetRechnung();
    	std::string original = GetRechnung();
    	std::string fake = "";
    	size_t klammerauf = 0;
    	size_t klammerzu = 0;
    
    	// Klammernueberpruefung
    	for (size_t i=0; i<rech.size(); i++)
    	{
    		if (rech[i] == '(')
    			klammerauf++;
    		else if (rech[i] == ')')
    		{
    			if (++klammerzu > klammerauf){
    				*fehlermeldung = "Falsche Klammernsetzung!";
    				return false;
    			}
    		}
    	}
    
    	if (klammerauf > klammerzu) {
    		*fehlermeldung = "Mehr oeffnende als schliessende Klammern!";
    		return false;
    	}
    	else if (klammerauf < klammerzu) {
    		*fehlermeldung = "Mehr schliessende als oeffnende Klammern!";
    		return false;
    	}
    
    	rech = "";
    	fake = original;
    	for (size_t i=0; i<fake.size(); i++) {
    		if (fake[i] != '(' && fake[i] != ')' && fake[i] != '%')
    			rech.push_back(fake[i]);
    		if (fake[i] == '(' && fake[i+1] == '-')
    			fake[i+1] = '%';
    	}
    
    	Reset(); Put(rech);
    
    	result = SimpleRechner::Check(fehlermeldung);
    
    	Reset(); Put(original);
    	return result;
    }
    
    long double Calc::GetResult()
    {
    	assert(Check(NULL));
    	std::string rech = GetRechnung();
    	std::string original = rech;
    	std::string teil, neurech;
    	size_t klammerauf, klammerzu;
    	long double Erg;
    
    	while (findFirstClose(rech))
    	{
    		klammerauf = findLastOpen(rech);
    		klammerzu = findFirstClose(rech);
    		while (klammerzu < klammerauf) klammerzu = findFirstClose(rech, klammerzu+1);
    
    		teil = "";
    		for (size_t i=klammerauf+1; i<klammerzu; i++)
    			teil.push_back(rech[i]);
    
    		Reset(); Put(teil);
    		Erg = SimpleRechner::GetResult();
    		std::ostringstream outp;
    		outp << Erg;
    		std::string out = outp.str();
    		if (out.size() > 0 && out[0] == '-')
    		{
    			if (klammerauf != 0) {
    				if (rech[klammerauf-1] == '+')
    					rech[klammerauf-1] = ' ';
    				else if (rech[klammerauf-1] == '-') {
    					out[0] = ' ';
    					rech[klammerauf-1] = '+';
    				}
    				else if (rech[klammerauf-1] == '/' || rech[klammerauf-1] == '*') {
    					std::string first = "";
    					size_t tpos = 0;
    					size_t startp;
    					if (rech[klammerauf-2] == ')') startp = klammerauf-3;
    					else startp = klammerauf-2;
    					for (size_t a=startp; a>0; a--) {
    						if ((rech[a] >= '0' && rech[a] <= '9') || rech[a] == '.' || rech[a] == ',' || rech[a] == ' '
    							|| rech[a] == '-') {
    							first.push_back(rech[a]);
    							tpos = a;
    							if (rech[a] == '-') break;
    						}
    						else
    							break;
    					}
    
    					if (out[0] == '-' && first[first.size()-1] == '-') {
    						out.erase(out.begin());
    						first[first.size()-1] = ' ';
    					}
    					else if (out[0] != '-' && first[first.size()-1] == '-') {
    						first[first.size()-1] = '-';
    					}
    
    					for (size_t a=startp, q=0; a>0 && q<first.size(); a--, q++)
    						rech[a] = first[q];
    
    				}
    
    			}
    		}
    
    		neurech = "";
    		for (size_t i=0; i<rech.size(); i++)
    		{
    			if (i == klammerauf)
    				for (size_t j=0; j<out.size(); j++)
    					neurech.push_back(out[j]);
    			else if (i<klammerauf || i>klammerzu)
    				neurech.push_back(rech[i]);
    		}
    		rech = neurech;
    	}
    	Reset(); Put(rech);
    	Erg = SimpleRechner::GetResult();
    	Reset(); Put(original);
    	return Erg;
    }
    

    Ein kleines Testprogramm:

    #include <iostream>
    #include <string>
    #include "Calc.h"
    
    using namespace std;
    
    int main()
    {
    	string aufgabe, fehlermeldung;
    	Calc Rechner;
    
    	do
    	{
    		cout << "Bitte Term eingeben (\"exit\" zum beenden): ";
    		getline(cin, aufgabe);
    		if (aufgabe != "exit")
    		{
    			Rechner.Reset(); Rechner.Put(aufgabe);
    
    			if (!Rechner.Check(&fehlermeldung))
    				cout << "\nDer Term ist inkorrekt. Beschreibung des Fehlers:\n" << fehlermeldung << "\n\n";
    			else
    				cout << "\nErgebnis: " << Rechner.GetResult() << "\n\n";
    		}
    	} while (aufgabe != "exit");
    
    	return 0;
    }
    

    Ich freue mich schon auf eure Kritik 🙂

    Gruss
    Cartman

    P.S.: Ich hoffe, dass der Thread hier reinpasst.



  • Ich freue mich schon auf eure Kritik

    Warum schreibst Du ein Taschenrechner Programm für einen PC? Versuch's für einen uC. Viele uC haben fast alles notwendige auf dem Chip (Resetgenerator, Oszillator, Flash, RAM). Später kannst Du das Ding in der Hand halten und versuchen, es irgendwie zu bedienen... Dann siehst Du, ob dein Programm gut ist oder nicht und brauchst dann auch keine Kritik von den anderen.



  • abc.w schrieb:

    Warum schreibst Du ein Taschenrechner Programm für einen PC?

    Um die C++ Programmiersprache zu lernen.

    abc.w schrieb:

    Versuch's für einen uC. Viele uC haben fast alles notwendige auf dem Chip (Resetgenerator, Oszillator, Flash, RAM). Später kannst Du das Ding in der Hand halten und versuchen, es irgendwie zu bedienen...

    Damit kenne ich mich ja ueberhaupt nicht aus 😉

    Ich hab die letzten 4 Stunden nur "gedebugt". Und jetzt glaube ich, dass die Klasse perfekt ist. Es funktionieren jetzt uebrigens negative Zahlen zu jedem Ort. Sie muessen nur in runden Klammern eingeschlossen sein (z.B. (-4,8) ). Ich hab den Rechner wirklich total durchgetestet und meine jetzt, dass es absolut bugfrei ist.
    Rechnungen wie diese sind jetzt kein Problem mehr:

    (-98,6*(-32,8/(-5/(-9,43)/(-3))))
    

    oder wie diese:

    (-4)+(-98,7)/(-9)
    

    Wenn ein Tester doch noch einen Fehler findet, lass es mich wissen.

    Nur die Variablengeschichte habe ich noch nicht implementiert (Vars-vector in der abgeleiteten Calc-Klasse). Die bau ich morgen noch ein, ist eher ne Kleinigkeit. Die ist dazu gut, um Variablen zu definieren, die dann im Term durch Zahlen ersetzt werden.
    z.B. koennte man eine "PI"-Variable festlegen, die im Term durch 3,14 ersetzt wird.
    Und dann ist der Rechner perfekt, denk ich 🙂

    Ich finde nur, dass ich das ganze etwas "unschoen" programmiert habe. Es fehlt die Uebersichtlichkeit - ich muss noch viel lernen 🙄 Trotzdem erfuellt mich dieser kleine Erfolg mit Stolz
    Code ist uebrigens geupdatet (bzw. werd ich jetzt machen).

    Gruss
    Cartman



  • Eric Cartman schrieb:

    abc.w schrieb:

    Versuch's für einen uC. Viele uC haben fast alles notwendige auf dem Chip (Resetgenerator, Oszillator, Flash, RAM). Später kannst Du das Ding in der Hand halten und versuchen, es irgendwie zu bedienen...

    Damit kenne ich mich ja ueberhaupt nicht aus 😉

    Bist wohl ein Informatiker oder studierst Du etwa auf einer Uni? (In Anlehnung an die heissen Diskussionen hier http://www.c-plusplus.net/forum/viewforum-var-f-is-61.html 😃 )

    Aber Spass bei Seite... 🙂
    Habe dein Programm aus Neugier bei mir kompiliert (unter VS2008) und mal laufen lassen.

    Bitte einen mathematischen Term eingeben ("exit" zum beenden): 5 / 0

    Ergebnis: 1

    Da läuft scheinbar was schief...
    Danach habe das hier eingegeben:

    Bitte einen mathematischen Term eingeben ("exit" zum beenden): -5 + (-1)
    Assertion failed: Check(NULL), file c:\dokumente und einstellungen\alex\eigene d
    ateien\visual studio 2008\projects\rechner\rechner.cpp, line 158

    Dann öffnet sich ein Dialog von MS2008 "...Visual C++ Debug Library" mit dem Text "Debug error!" usw...
    Das ist nicht gut. Musst noch, wie es aussieht, ein Paar Stunden mehr debuggen 🙂



  • Eric Cartman schrieb:

    Ich finde nur, dass ich das ganze etwas "unschoen" programmiert habe. Es fehlt die Uebersichtlichkeit - ich muss noch viel lernen 🙄

    Da du die Übersichtlichkeit selbst ansprichst und anstrebst, kann ich dir raten, die Aufgabe eines Taschenrechners als Ganzes gedanklich zu abstrahieren und die Teilaufgaben visuell als Flow Charts darzustellen. Diese könntest du dann hier vorstellen und man würde dir so gezielter Tipps zur Umsetzung in C++ geben können.

    Gruß



  • Sehr gut. Das erinnert mich daran, dass ich endlich mal Kopfrechnen lernen sollte 😃 👍



  • abc.w schrieb:

    Bist wohl ein Informatiker oder studierst Du etwa auf einer Uni? (In Anlehnung an die heissen Diskussionen hier http://www.c-plusplus.net/forum/viewforum-var-f-is-61.html 😃 )

    Weder noch. Ich bin Hobby-Programmierer (ich versteh den Witz dabe nicht) 🙂

    abc.w schrieb:

    Aber Spass bei Seite... 🙂
    Habe dein Programm aus Neugier bei mir kompiliert (unter VS2008) und mal laufen lassen.

    Bitte einen mathematischen Term eingeben ("exit" zum beenden): 5 / 0

    Ergebnis: 1

    Da läuft scheinbar was schief...

    Ja das ist mir bekannt, habe den Fehler aber noch nicht gefunden ...

    abc.w schrieb:

    Danach habe das hier eingegeben:

    Bitte einen mathematischen Term eingeben ("exit" zum beenden): -5 + (-1)
    Assertion failed: Check(NULL), file c:\dokumente und einstellungen\alex\eigene d
    ateien\visual studio 2008\projects\rechner\rechner.cpp, line 158

    Dann öffnet sich ein Dialog von MS2008 "...Visual C++ Debug Library" mit dem Text "Debug error!" usw...

    Bei mir funktionierts jetzt... Hab aber noch gestern spaetabends Bugs gefixt, dadurch wird sich dieser Bug wohl auch "gefixt haben". Code update ich gleich.
    Trotzdem danke fuer den Hinweis. Bin fuer jede Fehlermeldung dankbar 🙂

    @/rant/
    lol, ich auch

    Gruss
    Cartman



  • Eric Cartman schrieb:

    abc.w schrieb:

    Bist wohl ein Informatiker oder studierst Du etwa auf einer Uni? (In Anlehnung an die heissen Diskussionen hier http://www.c-plusplus.net/forum/viewforum-var-f-is-61.html 😃 )

    Weder noch. Ich bin Hobby-Programmierer (ich versteh den Witz dabe nicht) 🙂

    abc.w schrieb:

    Aber Spass bei Seite... 🙂
    Habe dein Programm aus Neugier bei mir kompiliert (unter VS2008) und mal laufen lassen.

    Bitte einen mathematischen Term eingeben ("exit" zum beenden): 5 / 0

    Ergebnis: 1

    Da läuft scheinbar was schief...

    Ja das ist mir bekannt, habe den Fehler aber noch nicht gefunden ...

    Ich hab jetzt nur kurz über deine Check Methode drüber gesehen und nicht alles angekuckt aber eventuell solltest du einfach überprüfen ob eine Division durch 0 stattfindet?

    Noch was:

    else
            {
                if (fehlermeldung)
                    *fehlermeldung = "Nicht erlaubtes Zeichen enthalten";
                return false;
            }
    

    Kuck dir mal exceptions an, mit denen kannst du deinen Code merklich reduzieren.



  • Eric Cartman schrieb:

    Weder noch. Ich bin Hobby-Programmierer (ich versteh den Witz dabe nicht) 🙂

    Das ist hier so ne Sache, es gibt hier viele Diskussionen, wo es im Prinzip darum geht, ob Informatik studieren, oder ob Uni besser als Fachhochschule und umgekehrt, oder ob mehr Theorie oder mehr Praxis usw... Kannst die Beiträge durchlesen und eigene Meinung bilden. 😉 Ich habe übrigens Elektrotechnik studiert...

    Bezüglich des Taschenrechnerprogramms hast Du dir eine Aufgabe gestellt, die auf den ersten Blick simpel aber sich doch als komplex rausstellt. Hatte nämlich auch mal mit einem Mikrocontroller einen Taschenrechner zusammengebastelt. Eine Sache unter vielen anderen vergisst man immer: Das Ding muss bedienbar sein. Ich denke, Du hast einen komplizierteren Weg eingeschlagen, indem Du versuchst, einen ganzen String, der alles mögliche enthalten kann, einzulesen und zu interpretieren. Das ist lexikalische Analyse und ist eine Wissenschaft für sich. Und Du versuchst das alles in einer Klasse zu machen und dann zu interpretieren und auszuführen. Du hast also einen kleinen Interpreter oder "Compiler" geschrieben - auch eine Wissenschaft für sich. Musst Du vielleicht überdenken. Bei meinem Taschenrechner hatte ich im Prinzip das gleiche Problem und habe dann meiner Meinung nach einen einfacheren Weg eingeschlagen und das Prinzip der Eingabe nach der umgekehrten polnischen Notation verwendet. Vielleicht als Tip für Dich? 🙂 (Und dran denken: Das Ding muss bedienbar sein. Das Argument "ich lerne bloss C++" greigt hier nicht...)



  • @abc.w
    Klingt wirklich sehr interessant. Danke fuer die sehr aufschlussreiche Antwort. Kannst du vielleicht gute Buecher zu diesem Thema empfehlen (C++ als Sprache und in deutsch waere natuerlich der Optimalfall...)? Wuerde mich gerne mehr in dieses Gebiet der lexikalischen Analyse bzw. Interpreter/Compilerbau reinsteigern, da ich das aeusserst interessant finde 🙂

    @blub
    Das Problem dabei ist, dass ich alles ausrechen muesste, um eine Division durch 0 zu verhindern, was aergerlich waere... mal sehen, ob ich eine andere Loesung finde...

    Gruss
    Cartman



  • Schau dir mal das "Dragon Book" an. Es beschäftigt sich mit Compilerbau und was so alles dazugehört. Ist aber definitiv kein Anfängerthema und mit viel Theorie verbunden. Auch kann es schwer werden wirklich "einfache" Listings zu dem Thema zu finden, da Compilerbau und Lexer und Parser und co sehr verwickelt sind/sein können.



  • Schau dir evtl auch mal den Quellcode von AngelScript an. Das ist eine Scriptsprache die C++ ähnelt mit Java und Python-Einflüssen. Der Parser dürfte für dich auch interessant sein. Ist auch in C++ geschrieben.
    rya.



  • Bei solchen Sachen empfehle ich immer Flex/Bison. Gerade das Taschenrechnerbeispiel wird im online Manual sehr gut und ausfuehrlich behandelt.



  • Und ich empfehl dir einen Blick auf boost::spirit:
    http://www.boost.org/doc/libs/1_38_0/libs/spirit/doc/html/index.html

    In den Examples findest du sogar einen Taschenrechner.



  • Die haben auch nur kopiert. Und irgendwie finde ich es reichlich ueberdimensioniert.



  • Hallo
    Danke fuer eure Empfehlungen. Ich werd mir das alles mal naeher anschauen.
    Das Drachenbuch ist sogut wie gekauft, da ich nur gutes darueber gelesen habe.

    Gruss
    Cartman


Anmelden zum Antworten