Große Probleme beim Programmieren eines Taschenrechner
-
Operator != 'q' && 'Q'Diese Bedingung tut definitiv etwas anderes als du denkst.
Die Bedingung besteht aus zwei Teilbedingungen: Operator ist nicht 'q', und 'Q' ist nicht 0.
Ein Buchstabe wird immer als Zahl gespeichert.
Und wenn man als logischen Ausdruck einfach eine Zahl angibt, bzw. einen skalaren Typen wie char oder int, wird der Wert zutrue, wenn er nicht 0 ist, undfalsewenn er 0 ist.'Q' ist laut ASCII irgendwas bei 75 oder so. Daher wird die zweite Teilbedingung immer true sein, und die erste nur false wenn du ein kleines q eingibst - dass heißt, wenn du ein großes Q eingibst, bricht er nicht ab.
while( Operator != 'q' && Operator != 'Q')Oder noch besser:
while( std::tolower(Operator) != 'q' )Weitere Macken sind bspw. das
switch-Statement - völlig deplatziert, weil du dann aus einer Zeile für jeden Buchstaben vier machst.
Ich finde, wir fangen mal anders an.Dein Taschenrechner ist relativ einfach, dennoch wäre es doch schöner, wir würden einfache Dinge wie 9+8+7 eingeben können, nicht?
Das geht ganz einfach, dank den C++-Streams.Erst überlegen wir uns folgendes: Wie sieht das Format aus?
Wir entscheiden uns für etwas einfacheres:
An den Enden ist also immer ein Wert und zwischen den Werten immer ein Operator.Das können wir leicht parsen. Erstmal ist es wichtig, zu wissen wie die Streams funktionieren. Dazu sind folgende Funktionen von istream wichtig:
istream::operator bool(): Gibt zurück, ob der Stream einen Fehler hat (also ob irgendetwas nicht gelesen werden konnte, o.ä.) oder ob der Stream am Ende des Zeichenstroms angekommen ist (keine weitere Eingabe mehr).
Dieser operator ist ein Konvertierungsoperator, sprich, ein Objekt vom Typ istream lässt sich (hier implizit) in einen boolschen Wert konvertieren. Siehe auch im Beispiel.istream::operator>>: Mit diesem überladenen Operator extrahieren wir einen Skalar aus dem Stream - also ein int, o.ä.
Merke: Dieser Operator extrahiert formatiert, sprich wenn die Zahl im falschem Format im Zeichenstrom steht, oder es gibt keine Zahl im Zeichenstrom, geht der Stream in den Fehlerzustand.
#include <iostream> #include <stdexcept> // für die exception-Typen - invalid_argument, logic_error #include <vector> // für vector using namespace std; /// Hier alle Bezeichner von std einführen, sodass nicht immer davor stehen muss. // Solte natürlich in größeren Dingern nicht genutzt werden, weil es zu Namenskonflikten kommt. Hier ist es aber in Ordnung. typedef int value_type; // Hier legen wir einen anderen Namen für int fest. Dann können wir den Typ hier ändern. bool contains( string const& str, char c ) { return str.find(c) != string::npos; } value_type compute( value_type lhs, char c, value_type rhs ) { if( c == '*' ) return lhs * rhs; if( c == '/' ) return lhs / rhs; if( c == '-' ) return lhs - rhs; if( c == '+' ) return lhs + rhs; throw invalid_argument("Invalid operator!"); // Fehler } void computeAll( string const& str, // Der String mit den Operatoren vector<value_type>& values, vector<char>& operators ) { for( unsigned ct = 0; ct < operators.size(); ++ct ) if( contains(str, operators[ct]) ) /// Wenn der String mit den Operatoren den aktuellen Operator enthält, dann... { /// Dem linken Operanden den Wert des Ausdrucks zuweisen values[ct] = compute( values[ct], operators[ct], values[ct + 1] ); /// Den rechten Operanden löschen, und den Operator löschen values.erase( values.begin() + ct + 1 ); operators.erase( operators.begin() + ct ); --ct; /// Den Zähler zurückstellen, damit im nächsten Schritt auch der direkt folgende Operator nicht übersprungen wird } } #include <limits> // numeric_limits int main() { vector<value_type> values; // Die Werte vector<char> operators; // Die Operatoren for(;;) try { values.resize(1); // platz für den ersten einzulesenden Wert operators.clear(); cin >> values.back(); // ersten Wert einlesen char op; value_type val; while( cin.peek() != '\n' /// solange der user noch nicht enter gedrückt hat... && cin >> op >> val ) // nächsten operator und zahl einlesen. Hier kommt istream::operator bool() zum Einsatz. Wenn es also keinen Fehler gibt, ist es true, sonst false. { values.push_back(val); operators.push_back(op); } cin.clear(); cin.ignore( numeric_limits< streamsize >::max(), '\n' ); /// Stream zurücksetzen. Fehlerzustand zurücksetzen und alle weiteren Zeichen im Buffer wegschmeißen. computeAll( "/*", values, operators ); /// Punkt vor Strich! computeAll( "+-", values, operators ); /// ... jetzt erst die "Strich"-Operatoren if( !operators.empty() ) throw logic_error("Invalid operator supplied!"); cout << "Result: " << values[0] << '\n'; } catch( exception& e ) { cerr << e.what() << '\n'; /// Fehlerbehandlung hier! Es wird nur der Fehler ausgegeben, lässt sich auf weiteres erweitern. } }Das geht noch viel schöner. Das ist auf die Schnelle zusammengeschustert.
Jetzt bist also du dran. Es können die Features wie gespeicherte Werte usw. ergänzt werden.
Wahrscheinlich kommt gleich eh wieder volkard mit der Schaufel.
:duck-und-weg:
-
Sone schrieb:
Ohne dir zu erklären, wieso
Du bist ja gemein!
Schubst Du das "wieso" gleich nach oder magst Du Dein Posting löschen oder nimmst Du an, daß der Lerner sich über so eine Hülse freut?
-
volkard schrieb:
Sone schrieb:
Ohne dir zu erklären, wieso
Du bist ja gemein!
Schubst Du das "wieso" gleich nach oder magst Du Dein Posting löschen oder nimmst Du an, daß der Lerner sich über so eine Hülse freut?Das Wieso ist unten. Sieh mal nach. Direkt über deinem Post. Mit Sternchen.

Edit: Na gut, da hast du.
-
Nee, jetzt nicht mehr?

Edit: Ah, habs gefunden.
-
...
-
ich wollt mich zunächst mal für eure Zeit und Mühe bedanken.
Leider überfordert mich die meisten Tips die ihr mir gegeben habt und da ich auch erklären muss warum ich was gemacht habe kann ich im Moment wenig "übernehmen". :p
Vllt. könnt ihr mir aber no ein paar einfache Fragen beantworten.
- Wann benutzt man endl; und wann \n und was ist überhaupt der Unterschied
- Da ich selber nicht weiß was ich mein, weiß ich auch nicht wie ich das mit dem "q" und "Q" machen kann
-was ist denn der Unterschied zwischen do while und while
- ich weiß ich habs die ganze Zeit gemacht, aber was zum Teufel macht eig. genau das break
- @ swordfish: was meinst du mit dem// hinterher steht noch ein '\n' in cin, also leeren:
cin.clear();
cin.ignore( numeric_limits< streamsize >::max(), '\n' );
-
- Wann benutzt man endl; und wann \n und was ist überhaupt der Unterschied
'\n' macht nur einen Zeilenumbruch.
Intern gibt es jedoch einen Buffer, also einen Zwischenspeicher für jedes einzelne Zeichen was noch in die Endstation (Konsole, Datei) kommen soll.
Nutzt du endl, wird nicht nur ein Zeilenumbruch erzeugt - dieser Buffer wird (überflush()) geleert und es wird "sofort" jedes Zeichen das noch ausstehend ist auf die Konsole geschrieben, oder wohin auch immer (bspw. bei fstream halt in die Datei).Im Allgemeinen nutzt man für Zeilenumbrüche einfach
'\n'. An bestimmten Situationen will man, dass der Text sofort ausgegeben wird. Bspw. bei Fehlerbehandlungen -std::cerrist ungepuffert, da gibt es keinen Sinn endl zu verwenden.- Da ich selber nicht weiß was ich mein, weiß ich auch nicht wie ich das mit dem "q" und "Q"
machen kannWie ich gezeigt habe, mit
std::tolower.-was ist denn der Unterschied zwischen do while und while
Bei do-while wird das Statement immer zuerst einmal ausgeführt, ohne die Bedingung zu beachten. Bei while wird schon vor dem ersten Ausführen geprüft.
- ich weiß ich habs die ganze Zeit gemacht, aber was zum Teufel macht eig. genau das break
Bei Schleifen unterbricht es diese. Bei switch-Statements springt es aus dem switch-Statement raus (also es unterbricht sozusagen die Prüfung).[/quote]
-
Arctic Monkeyyy schrieb:
- Wann benutzt man endl; und wann \n und was ist überhaupt der Unterschied
- Da ich selber nicht weiß was ich mein, weiß ich auch nicht wie ich das mit dem "q" und "Q" machen kann
-was ist denn der Unterschied zwischen do while und while
- ich weiß ich habs die ganze Zeit gemacht, aber was zum Teufel macht eig. genau das break- endl = \n (neue Zeile) + flush (alles rausschreiben). endl brauchst du fast nie, weil du flush fast nie brauchst, weil es meist immer automatisch geschieht.
- Es gibt kopfgesteuerte und fußgesteuerte Schleifen. Kopfgesteuerte sind for und while. Dabei wird immer zuerst auf eine Bedingung geprüft, falls diese erfolgreich ist, wird die Schleife ausgeführt. Bei der fußgesteuerten Schleife do-while wird die Schleife immer zuerst ausgeführt und erst dann wird die Bedingung geprüft, d.h. auch, sie läuft immer min. 1x durch.
- break = hier abbrechen
-
...
-
out schrieb:
endl = \n (neue Zeile) + flush (alles rausschreiben). endl brauchst du fast nie, weil du flush fast nie brauchst, weil es meist immer automatisch geschieht.
endl brauchst du nie.
Eigentlich immer will man nur '\n'. Hinzu kommt, dass cout automatisch flusht, wenn etwas von cin eingelesen wird. endl braucht man nur in Ausnahmefällen.
Diese sollte man dann entsprechend markieren und'\n' << std::flushschreiben.