Taschenrechnerlogik

Für alles, was in den übrigen Lazarusthemen keinen Platz, aber mit Lazarus zutun hat.
Antworten
Marsmännchen
Beiträge: 294
Registriert: So 4. Mai 2014, 21:32
OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10
CPU-Target: 64bit
Wohnort: Oranienburg

Taschenrechnerlogik

Beitrag von Marsmännchen »

Hi,

Zum Warmwerden mit Lazarus/FP baue ich gerade einen Taschenrechner zusammen. In 'Ich-lerne-jetzt-xyz'-Büchern kommt das kurz nach "Hallo Welt" und meist mit zwei Eingabefeldern und einem Button. Das war mir zu billig, ich code schon eine Weile. Also habe ich mir vorgenommen, einen richtigen Taschenrechner zu simulieren. Das klappt auch schon soweit ganz gut, ich habe eine Form mit den ganzen Tasten (0-9, die Grundrechenarten, Komma und gleich). Diese habe ich bewusst dumm gehalten. Sie gibt die Eingabe schlichtweg an ein Rechnerobjekt weiter, dass die ganze Logik macht und lauscht darauf, dass dieses Objekt meldet, dass sich die Ausgabe bei den Lauschern ändern soll (hurra, ich habe ein Observerpattern hinbekommen!). Dann holt es sich bei dem Rechnerobjekt den neuen Output ab und zeigt ihn an.

Soweit so grün, wie wir hier auf dem roten Planeten sagen.

Mein Rechnerobjekt sammelt also die ganzen gemeldeten Eingaben in einem dynamischen Array ein und muss ja nu irgendwas damit machen. Dazu muss ich zuerst prüfen, ob die Eingaben mathematisch überhaupt Sinn machen, bzw. Fehleingaben möglichst abwenden. Daher habe ich mich vor der Implementation der Semantikprüfung hingesetzt und mir überlegt, wie ich überhaupt mit den User-Eingaben umgehen muss. Dabei ist eine Logik-Beschreibung rausgekommen, die dann natürlich in Code gegossen werden muss.
Meine Frage: Würdet ihr mal drüber blicken und mir sagen, ob ich was Wichtiges vergessen habe oder irgendwo ein Brett vor dem Kopf hatte? Falls ihr einen Tipp habt, wie man vor der konkreten Implementation an komplexe Logiksachen besser rangehen kann, bin ich auch dankbar!!!
Meine Überlegungen waren wie folgt:

Die Logik von Usereingaben
-Der Rechner akzeptiert als Eingabe nur solche Zeichen, mit denen er was anfangen kann (0..9, die Operatoren (+, -, *, /, =) usw.). Zuerst wird also geprüft, ob überhaupt ein akzeptables Zeichen eingegeben wurde, sonst wird es ignoriert. Danach wird geprüft, ob das Zeichen im mathematischen Kontext des Rechners Sinn macht.
-Die Eingabe hat mit einem Ausdruck zu beginnen. Mit dem Begriff 'Ausdruck' meine ich eine Zahl oder eine Rechenoperation in Klammern
-Dann abwechselnd Operator und Ausdruck.
-Zum Schluss (vor dem Gleich) kommt wieder ein Ausdruck
-Sieht also wie folgt aus: Ausdruck – Operator – Ausdruck – Operator… - Ausdruck = → Dann Ergebnis berechnen
Zu den Klammern
-Ausdrücke dürfen beliebig mehrfach geklammert werden. Dabei gelten aber folgende Regeln:
-Der Gesamtausdruck muss dann geklammert sein
-Für alle öffnenden Klammern muss es auch schließende Klammern geben.
-Soll eine Klammer geschlossen werden, so wird geprüft, ob sie einen semantisch korrekten Ausdruck enthält (Ausdruck -Operator-Ausdruck). Ist das nicht der Fall, wird das Schließen verweigert. Negativbeispiel: (1+1*) geht nicht. Es fehlt der abschließende Ausdruck. Es gilt bei Klammern: Nach Außen muss ein Operator stehen, nach Innen muss eine Zahl stehen, sofern nicht eine weitere Klammer voransteht/folgt!
Zum Komma
-Der Rechner hat zu wissen, ob bereits ein Komma in eine Zahl eingefügt worden ist. Wenn das der Fall ist, verhindert er die Eingabe weiterer Kommata.
Bei Löschen (Drücken von Backspace):
-Es wird immer das zuletzt eingegebene Zeichen gelöscht (logisch)
-Wenn die Ausgabe (die ja immer nur den aktuellen Ausdruck zeigt) dadurch leer wird, gibt es zwei Möglichkeiten:
a) der Eingabestapel ist komplett leer, dann erscheint eine Null
b) Der Eingabestapel ist nicht leer, dann holt der Rechner den letzten Ausdruck (samt dem letzten Operator) und zeigt ihn an. Der Benutzer hat dann die Möglichkeit, weiter zu löschen oder einen neuen Ausdruck zu beginnen.
Eingabe von Operatoren
-Wird ein Operator eingegeben, so bestehen folgende zwei Möglichkeiten:
a) es wurde gerade schon ein Operator eingegeben (ist zuoberst im Eingabestapel): In diesem Fall geht der Rechner davon aus, dass der User den alten Operator ändern möchte und überschreibt ihn mit dem neuen Operator.
b) bisher wurde nur ein Ausdruck eingegeben. In diesem Fall wird der Operator einfach akzeptiert. Der Rechner erwartet im folgenden einen neuen Ausdruck oder einen Operator (siehe a)
Die Logik von Rechnerausgaben (auf dem Display des Taschenrechners)
-Ein Ausdruck wird Ziffer für Ziffer angezeigt.
-Wird ein Operator angegeben, so wird er hinter dem Ausdruck angezeigt
-Folgt danach ein neuer Ausdruck, wird die bisherige Ausgabe gelöscht und der Beginn des neuen Ausdrucks erscheint
-Wird das '=' gedrückt, gibt die Ausgabe selbstverständlich das Ergebnis aus.

Falls euch das zu blöd ist und ich keine Antwort erhalte: okay. Ansonsten schon mal danke im Voraus. Um ein wenig optische Hilfe zu geben, füge ich mal ein Bild meines Taschenrechners (bisheriger Stand) bei. Code kann auf Wunsch nachgeliefert werden, fängt aber an, schon einige Zeilen zu füllen (deswegen verzichte ich erstmal drauf).
Dateianhänge
Taschenrechner.PNG
Ich mag Pascal...

u-boot
Beiträge: 306
Registriert: Do 9. Apr 2009, 10:10
OS, Lazarus, FPC: Ubuntu 9.10 (L 0.9.28 FPC 2.2.4)
CPU-Target: 32Bit
Wohnort: 785..

Re: Taschenrechnerlogik

Beitrag von u-boot »

Marsmännchen hat geschrieben:Mein Rechnerobjekt sammelt also die ganzen gemeldeten Eingaben in einem dynamischen Array

Ich persönlich würde einfach ein string empfehlen. OK ist auch ein Array....

Bei den Klammern fehlt. dass eine schließende Klammer auch eine geöffnete voraussetzt.

Punkt- vor Strichrechnung kann man wollen, oder auch nicht.

Berücksichtigung von Negativzahlen würde deine Überlegung etwas spannender machen.

3*-2 -> nicht erlaubt?

3*(-2) -> auch nicht erlaubt ?
Ubuntu 9.10 (L 0.9.28 FPC 2.4.x)

Marsmännchen
Beiträge: 294
Registriert: So 4. Mai 2014, 21:32
OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10
CPU-Target: 64bit
Wohnort: Oranienburg

Re: Taschenrechnerlogik

Beitrag von Marsmännchen »

u-boot hat geschrieben:Ich persönlich würde einfach ein string empfehlen. OK ist auch ein Array....
Du hast Recht. Da habe ich zu kompliziert gedacht.

u-boot hat geschrieben:Bei den Klammern fehlt. dass eine schließende Klammer auch eine geöffnete voraussetzt.
Stimmt, das habe ich nicht gesehen...

u-boot hat geschrieben:Punkt- vor Strichrechnung kann man wollen, oder auch nicht.
Autsch! Das hätte ich sehen müssen! :shock: Die unterschiedlichen Prioritäten der Operatoren müssen schon sein. Die Klammern hätten mich schon drauf bringen müssen. Ist schließlich nichts anderes als eine explizite Änderung der Auswertungsreihenfolge.

u-boot hat geschrieben:Berücksichtigung von Negativzahlen würde deine Überlegung etwas spannender machen.

3*-2 -> nicht erlaubt?

3*(-2) -> auch nicht erlaubt ?
Mensch natürlich. Negativzahlen hatte ich gar nicht auf dem Radar :P

Dankeschön! Das sind genau die Löcher, die ich in meinem Kopfbrett brauche. Das frickel ich alles mit ein. Schöne Logikaufgabe!

EDIT: So, ich habe jetzt mal mein persönliches Pflichtenheft ergänzt:
Die Logik von Rechnereingaben
Der Rechner akzeptiert als Eingabe nur solche Zeichen, mit denen er was anfangen kann (0..9, die Operatoren (+, -, *, /, =) usw.). Zuerst wird also geprüft, ob überhaupt ein akzeptables Zeichen eingegeben wurde, sonst wird es ignoriert. Danach wird geprüft, ob das Zeichen im mathematischen Kontext des Rechners Sinn macht.
Die Eingabe hat mit einem Ausdruck zu beginnen. Mit dem Begriff 'Ausdruck' meine ich eine Zahl oder eine Rechenoperation in Klammern. Ausdrücke dürfen mit einem Minus beginnen, um eine negative Zahl darzustellen.
Dann abwechselnd Operator und Ausdruck.
Zum Schluss (vor dem Gleich) kommt wieder ein Ausdruck
Sieht also wie folgt aus: Ausdruck – Operator – Ausdruck – Operator… - Ausdruck = → Dann Ergebnis berechnen

Zu den Klammern
Ausdrücke dürfen beliebig mehrfach geklammert werden. Dabei gelten aber folgende Regeln:
Der Gesamtausdruck muss dann geklammert sein
Für alle öffnenden Klammern muss es auch schließende Klammern geben. Wird eine schließende Klammer eingegeben, muss es dazu auch eine passende öffnende Klammer geben.
Soll eine Klammer geschlossen werden, so wird geprüft, ob sie einen semantisch korrekten Ausdruck enthält (Ausdruck -Operator-Ausdruck). Ist das nicht der Fall, wird das Schließen verweigert. Negativbeispiel: (1+1*) geht nicht. Es fehlt der abschließende Ausdruck. Es gilt bei Klammern: Nach Außen muss ein Operator stehen, nach Innen muss eine Zahl stehen, sofern nicht eine weitere Klammer voransteht/folgt! Dabei ist ein Minus in Ordnung, denn es darf sich auch um eine negative Zahl handeln.

Zum Komma
Der Rechner hat zu wissen, ob bereits ein Komma in eine Zahl eingefügt worden ist. Wenn das der Fall ist, verhindert er die Eingabe weiterer Kommata.

Bei Löschen:
- Hat sich erübrigt, der Rechenausdruck wird in einem String verwaltet, den man hinten leicht abschneiden kann.

Eingabe von Operatoren
Wird ein Operator eingegeben, so bestehen folgende Möglichkeiten:
a) es wurde gerade schon ein Operator eingegeben (ist zuletzt im Berechnungsstring): In diesem Fall wird nur der Minus-Operator als zulässig akzeptiert, um mit negativen Zahlen arbeiten zu können. Alle übrigen Operatoren werden abgelehnt. Die ursprünglich geplante Überschreibung des alten Operators wird verworfen, er ist ggf. explizit mit Backspace zu löschen. Eine Überschreibung würde dazu führen, dass ich hinter einem Operator nicht mit negativen Zahlen arbeiten könnte, weil deren vorangestelltes Minus den eigentlichen Operator überschreiben würde. Wenn ich aber den alten Operator stehen lasse, dann kann es sein, dass der User in Wirklichkeit doch ihn mit dem Minus überschreiben wollte, weil das Programm das so ansonsten bei den Operatoren ja macht. Das Prog würde sich also logisch inkonsistent verhalten. Das will ich nicht.
b) bisher wurde nur ein Ausdruck eingegeben. In diesem Fall wird der Operator einfach akzeptiert.

Die Logik von Rechnerausgaben
Ein Ausdruck wird Ziffer für Ziffer angezeigt.
Wird ein Operator angegeben, so wird er hinter dem Ausdruck angezeigt
Folgt danach ein neuer Ausdruck, wird die bisherige Ausgabe gelöscht und der Beginn des neuen Ausdrucks erscheint
Wird das '=' gedrückt, gibt die Ausgabe selbstverständlich das Ergebnis aus.

Reihenfolge der Auswertung
Wenn geklammert, dann von der innersten Klammer zur äußersten.
Danach Punkt-Berechnungen (*/)
Danach Strichberechnungen (+-)
Ich mag Pascal...

BeniBela
Beiträge: 308
Registriert: Sa 21. Mär 2009, 17:31
OS, Lazarus, FPC: Linux (Lazarus SVN, FPC 2.4)
CPU-Target: 64 Bit

Re: Taschenrechnerlogik

Beitrag von BeniBela »

Marsmännchen hat geschrieben:
u-boot hat geschrieben:Ich persönlich würde einfach ein string empfehlen. OK ist auch ein Array....


Arrays sind eigentlich einfacher

u-boot hat geschrieben:Bei den Klammern fehlt. dass eine schließende Klammer auch eine geöffnete voraussetzt.


Das ist total unpraktisch

man 21 auch als

Code: Alles auswählen

 
1 + 2) * (3 + 4
 

schreiben

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Taschenrechnerlogik

Beitrag von wp_xyz »

BeniBela hat geschrieben:man 21 auch als

Code: Alles auswählen

 
1 + 2) * (3 + 4
 

schreiben

Wo wird denn sowas akzeptiert?

BeniBela
Beiträge: 308
Registriert: Sa 21. Mär 2009, 17:31
OS, Lazarus, FPC: Linux (Lazarus SVN, FPC 2.4)
CPU-Target: 64 Bit

Re: Taschenrechnerlogik

Beitrag von BeniBela »

In Taschenrechnern für Endusers.

Google , bei Suche nach 1 + 2) * (3 + 4 =, Qalculate, ...

Wolframalpha akzeptiert es auch, macht aber 1 + 2 * (3 + 4) draus

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Taschenrechnerlogik

Beitrag von wp_xyz »

BeniBela hat geschrieben:Wolframalpha akzeptiert es auch, macht aber 1 + 2 * (3 + 4) draus

Das demonstriert schön, dass man von nicht allgemein üblicher Syntax die Finger lassen sollte. Zu jeder sich öffnenden Klammer gehört nun mal eine sich schließende.

BeniBela
Beiträge: 308
Registriert: Sa 21. Mär 2009, 17:31
OS, Lazarus, FPC: Linux (Lazarus SVN, FPC 2.4)
CPU-Target: 64 Bit

Re: Taschenrechnerlogik

Beitrag von BeniBela »

wp_xyz hat geschrieben:
BeniBela hat geschrieben:Wolframalpha akzeptiert es auch, macht aber 1 + 2 * (3 + 4) draus

Das demonstriert schön, dass man von nicht allgemein üblicher Syntax die Finger lassen sollte. Zu jeder sich öffnenden Klammer gehört nun mal eine sich schließende.



Die sind auch da

Man muss einfach davon ausgehen, dass vor und hinter dem Ausdruck eine unendliche Anzahl von Klammern sind. (((((((((((((((((((((((((((((((((((((((((((((((((((((((...(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( 1 + 2) * (3 + 4 )))))))))))))))))))))))))))))))))))))))))))))))))))))...))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))

Dann passt es auch. Jede der unendlichen ( hat einen Match bei den unendlichen ). Und endlich viele Klammern dazwischen ändern nichts, da bleiben immer noch unendlich viele übrig.

Marsmännchen
Beiträge: 294
Registriert: So 4. Mai 2014, 21:32
OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10
CPU-Target: 64bit
Wohnort: Oranienburg

Re: Taschenrechnerlogik

Beitrag von Marsmännchen »

BeniBela hat geschrieben:(((((((((((((((((((((((((((((((((((((((((((((((((((((((...(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( 1 + 2) * (3 + 4 )))))))))))))))))))))))))))))))))))))))))))))))))))))...))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))


Hmmmm..... ich als Mathe-Normalo verstehe zwar die Logik hinter deinem Argument, aber würde das eigentlich nicht bei einem Taschenrechner erwarten. Deswegen wundert es mich, dass das so in Taschenrechner für Enduser implementiert ist. Ob die das wissen??
Ich mag Pascal...

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6200
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Taschenrechnerlogik

Beitrag von af0815 »

Wenn man in die Definition max. X Ebenen aufnimmt, ist das auch kein Problem. Hatten die Taschenrechner nicht max 7 Ebenen ?
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Marsmännchen
Beiträge: 294
Registriert: So 4. Mai 2014, 21:32
OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10
CPU-Target: 64bit
Wohnort: Oranienburg

Re: Taschenrechnerlogik

Beitrag von Marsmännchen »

Im Endeffekt bedeutet es für meine Implementation, dass ich Klammern ganz am Anfang und ganz am Ende der Eingabe ignorieren könnte. Das vereinfacht die Sache aber technisch nicht, weil Freepascal so eine Formel nicht frisst (hab's grade ausprobiert). Somit müsste ich dann die entsprechende Anzahl Klammern programmtechnisch wieder hinzufügen. Dann kann ich eigentlich auch erwarten, dass der User seine Ausdrücke korrekt klammert. Oder sehe ich das falsch?
Ich mag Pascal...

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6200
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Taschenrechnerlogik

Beitrag von af0815 »

IMHO Fehler in Syntax ausgeben oder/und auf die erste beanstandete Stelle springen.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

u-boot
Beiträge: 306
Registriert: Do 9. Apr 2009, 10:10
OS, Lazarus, FPC: Ubuntu 9.10 (L 0.9.28 FPC 2.2.4)
CPU-Target: 32Bit
Wohnort: 785..

Re: Taschenrechnerlogik

Beitrag von u-boot »

Marsmännchen hat geschrieben:Im Endeffekt bedeutet es für meine Implementation, dass ich Klammern ganz am Anfang und ganz am Ende der Eingabe ignorieren könnte. Das vereinfacht die Sache aber technisch nicht, weil Freepascal so eine Formel nicht frisst (hab's grade ausprobiert). Somit müsste ich dann die entsprechende Anzahl Klammern programmtechnisch wieder hinzufügen. Dann kann ich eigentlich auch erwarten, dass der User seine Ausdrücke korrekt klammert. Oder sehe ich das falsch?


Meine Meinung dazu:

User sind wohl sehr unterschiedlich.... also musst erst rausfinden was für user du hast oder willst ;)
Ubuntu 9.10 (L 0.9.28 FPC 2.4.x)

Antworten