PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [C++] Taschenrechner (Anfängerprojekt? Orly?)



blackberry
24.09.2011, 03:32
Die Funktionsweise ist ganz simpel. Man gibt die zwei Operanden ein und wählt die Rechenoperation aus. Das Ergebnis wird einem anschließend angezeigt.
Verfügbare Operationen sind Addition, Subtraktion, Multiplikation und Division (bei Division durch 0 stürzt das Programm leider ab; keine Ahnung wieso!).

Download:
http://img6.imagebanana.com/img/jysdjlpc/trollmeme.png

Okay, Scherz beiseite. Implementiert habe ich eine Teilform des Shunting-Yard-Algorithmus (ohne Unterstützung für Funktionen).
Unterstützt werden die vier Grundrechenarten zusammen mit Potenzierung (habe allerdings keine Konstanten eingebaut; sowas wie "e^123" ist also nicht möglich) und Klammerung.

"Punkt vor Strich" usw. wird befolgt und wenn ein Ausdruck mal nicht ausgewertet werden kann, dann wird einfach NAN (not a number) zurückgegeben; das printf gibt entsprechend dann "= nan" als Ergebnis aus.

Unäre Minuszeichen (Minus als Vorzeichen, statt als Operation; beispielsweise bei "-1") und die Konvention den Malpunkt vor einer Klammer weglassen zu können sind ebenfalls implementiert.

Der Sourcecode ist gleichzeitig sein eigenes Bash Compile-Script (wollte das mal wieder ausprobieren; ist nicht meine Idee -- habe ich vor ein paar Jahren mal irgendwo gelesen).

Das ganze sieht dann etwa so aus:
http://img7.imagebanana.com/img/vz3r7vpc/calcdemo.png

Für eine Beschreibung des Shunting-Yard-Algorithmus verweise ich an der Stelle einfach mal auf Wikipedia (dort finden sich auch ein paar schöne Diagramme, die die Funktionsweise anhand einiger Beispiele gut verdeutlichen).
Das Programm arbeitet in drei Schritten, die alle von "sy_eval_expression" initiiert werden.
Im ersten Schritt wird der übergebene Ausdruck in Tokens aufgeteilt. Bei den Operatoren/Klammern ist das geradezu langweilig und eigentlich nicht mal erwähnenswert. Schwieriger wird die Angelegenheit mit den Zahlen, die ja oft aus mehr als nur einer Ziffer bestehen. Da mit doubles gearbeitet wird kommt auch noch das Behandeln von Nachkommastellen hinzu.
Im Prinzip implementiert Schritt 1 also eine Art atof-Funktion, die einfach aufhört auszuwerten, wenn sie auf etwas trifft, das nicht mehr zum double gehört.
Hinzu kommen dann eben noch ein paar kleine Tricks, um auch ein Vorzeichen von einem Minus als Operator unterscheiden zu können und das Weglassen von Mal-Zeichen in Ausdrücken wie "2(3)" (statt "2*(3)") zu unterstützen.
In Zeile 70 wird dann eben überprüft, ob es sich beim aktuell eingelesenen Zeichen um eine aufgehende Klammer handelt und falls ja, ob unmittelbar davor eine Klammer geschlossen wurde (wie etwa in "(1+2)(3+4)"), oder ob davor eine Zahl stand (etwa "2(3)"). Wenn ja, so wird ein Mal-Zeichen eingefügt.
In Zeile 73 läuft das ganze für eine geschlossene Klammer ab. Dabei wird nur überprüft, ob dahinter eine Zahl steht (wie etwa "(2)3"; ob dahinter eine Klammer aufgeht wird nicht überprüft, da die aufgehende Klammer ja schon ein Mal-Zeichen einfügen würde [vgl. Zeile 70]).
In Zeile 68 entsteht der Support für das Minus als Vorzeichenindikator. Ist der Operator nämlich ein Minus und steht unmittelbar hinter einem der Zeichen "(*/^", so wird das Minus als Vorzeichen gedeutet und der Code in den Zeilen 70-75 wird nicht ausgeführt; insbesondere nicht das "break" in Zeile 75, also wird wie das eben bei switch so ist munter weiter Code ausgeführt... dieser Code ist dann aber zum Einlesen der Zahlen zuständig; dieser behandelt auch gleich den Fall, dass ein Minuszeichen davor steht.

Im zweiten Schritt wird der nun vorliegende Tokenstream, der den Ausdruck in infix-Notation repräsentiert mit dem Shunting-Yard-Algorithmus in die postfix-Notation überführt (also die Tokens entsprechend umsortiert; Rückgabewert ist wieder ein Stack mit Tokens).

Der dritte Schritt wertet nun den Ausdruck aus (Zeile 187ff.).

Wie auch immer... hier der Sourcecode:
http://pastie.org/2582205 (alt)
https://pastebin.com/hY0rgLJM (neu)


MfG. blackberry

EDIT: kleiner Nachtrag von mir, da es mir erst jetzt aufgefallen ist. Wenn es bei euch nicht compiliert (strchr, isdigit nicht definiert, etc.), dann inkludiert noch cstring und cctype.

Bonkers
01.06.2012, 00:04
Auch wenn der Post schon etwas älter ist hab ich ihn erst jetzt gefunden und dachte mir etwas Rückmeldung kann nicht schaden ;)
Kompilieren und benutzen des Programms waren kein Problem, funktioniert soweit alles prima. Die Terme die ich ausprobiert habe wurden auch richtig berechnet.
Trotzdem ist klar dass es für die gleiche Tätigkeit bessere und meist vorinstallierte Tools gibt...

Beispiele:

Blackberry:



bonkers@belial:~$ ./blackberry
$ (5*5)/25
= 1


Python:



bonkers@belial:~$ python
Python 2.7.3 (default, Apr 20 2012, 22:39:59)
[...]
>>> (5*5)/25
1


bc:



bonkers@belial:~$ bc
bc 1.06.95
[...]
(5*5)/25
1


Ruby:



bonkers@belial:~$ irb
irb(main):001:0> (5*5)/25
=> 1


Das soll deine Arbeit allerdings nicht abwerten. Siehe das Zitat in meiner Signatur ;)

SFX
07.06.2012, 23:51
dc:


sfx@catbox:~$ dc
5 5 * 25 / p
1
sfx@catbox:~$

cby
08.06.2012, 10:17
bash:


$ echo $(((5*5)/25))
1

C0SiNUS
08.06.2012, 16:47
awk:
awk 'BEGIN {print (5*5/25)}'

h4XX0r-7
08.06.2012, 17:42
Noch keiner einen Brainfuck-Code? :D

cby
09.06.2012, 07:15
Eigentlich wollte ich es mit den Mitteln machen die bei einer Linux Distribution wie Archlinux von Haus aus dabei ist. bc oder dc fällt da schon mal weg.
Die Lösung mit awk gefällt mir sehr.

blackberry
10.06.2012, 03:36
Dexx:

blackberry: Was ist 5*5/25?
Dexx: 1?
blackberry: brav.

U mad?

cby
12.06.2012, 10:53
Jetzt müsste man noch mit "time" schauen was schneller ist ;)

blackberry
12.06.2012, 11:06
Sicher nicht Dexx. Der hat selbst für die kleine Rechnung ziemlich lange gebraucht; allerdings schöpft das ja auch noch bei weitem nicht alle Möglichkeiten von Dexx aus.

Dexx versteht Mathematica, MATLAB und normale Sprache, integriert dir Funktionen, löst dir Differentialgleichungen, unterstützt Eingabe auf Deutsch und Englisch und schickt dir die Ausgabe direkt im Chat, oder wahlweise auch per Post in gedruckter Form zu.

Ihr seht also, dass Geschwindigkeit nicht alles sein kann; was Funktionalität betrifft haut Dexx alle eure Programme in die Tonne.

Btw: Dexx wird von Lob und Keksen angetrieben und kann optional in Keks-freien Zeiten auch auf ein integriertes Feenstaub-Antriebsmodul umgeschaltet werden. Insgesamt schont Dexx also beim Betrieb auch noch die Umwelt!

Und da ihr euch nun sicher fragt "Wo kann ich Dexx runterladen?" -- Nirgendwo. Dexx wird nicht "runtergeladen". Das tut Dexx weh :X

SFX
12.06.2012, 22:41
Ist Dexx eigentlich turing complete?

blackberry
12.06.2012, 23:12
Jup -- und zwar im eigentlichen Sinne der Definition und nicht nur im "theoretisch ja, wenn ich unendlich RAM Module kaufen würde"-Sinn.

Dexx hat unendlich viel Speicher.