Komplexer Taschenrechner

  • VB.NET

Es gibt 38 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Hallo,

    ich habe letztens einen FormelParser programmiert, der Klammerausdrücke inkl. Sin und Cos und Wurzeln und Potenzen korrekt ausrechnet - weitere Operatoren können leicht hinzugefügt werden.
    Schau mal hier: github.com/florian03-1/taschenrechner-florian03 - ist das das wonach du suchst?

    LG Florian
    ----

    WebApps mit C#: Blazor
    @florian03 Das ist was ich suche. :thumbsup: Aber ich will es nicht kopieren, viel mehr es selber machen und brauch daher nur ein Schubser im Sinne einer Erklärung, dass ich es verstehe damit ich es selber machen kann. Sonst wäre ich nicht besser als meine Klassenkameraden. Denn wenn ich etwas einmal verstanden habe, verlerne ich das nicht so schnell. ^^ Was der Formelparser macht habe ich schon verstanden, auch die Infix und Postfix-Notation, genauso wie den Stack also wo alles quasi auf "einen Haufen geschmissen" wird und dann abgearbeitet. Nur wie man das programmiert, da fehlt mir noch der Ansatz.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Moritz Zander“ ()

    Ja das ist ein klein wenig komplizierter zu erklären. Es gibt die so genannte Postfix Notation, in der man mathematische Ausdrücke aufschreiben kann. Diese ist dann für einen Computer viel einfacher abzuarbeiten, da es keine Klammern mehr gibt und die "Operatoren" hintereinander stehen.
    Um einen Term in der Infix Schreibweise (die "normale") in die Postfix Variante zu bringen, gibt es den so genannten Shunting-Yard Algorithmus.

    Ein paar Links, die dir helfen könnten:
    de.wikipedia.org/wiki/Umgekehrte_polnische_Notation
    de.wikipedia.org/wiki/Shunting-yard-Algorithmus

    PS: ich glaube @ErfinderDesRades hat auch mal einen Formelparser auf der Basis gebaut und das Vorgehen ein wenig erklärt hat (ich glaube aber, dass er nicht Funktionen wie sin() und cos() behandelt hat), den müsste ich aber erst suchen...

    LG Florian
    ----

    WebApps mit C#: Blazor

    Moritz Zander schrieb:

    Ich möchte erst die Rechnung eingeben bevor ich rechne.
    Das kannst Du halten wie Du willst.
    Ich empfehle Dir, sofort bei Eingabe die Eingabe zu zerpflücken, also:
    4+2*8
    =>
    4
    +
    2
    *
    8
    ============
    So hast Du Operationen und Operanden sofort einzeln aufgelistet.
    Nun kannst Du intern machen, was zu machen ist: Erkennen von Klammern, Funktionsaufrufen, Operationen und deren Rangfolge.
    Praktisch feddich. :thumbsup:
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Moritz Zander schrieb:

    Was der Formelparser macht habe ich schon verstanden, auch die Infix und Postfix-Notation, genauso wie den Stack also wo alles quasi auf "einen Haufen geschmissen" wird und dann abgearbeitet. Nur wie man das programmiert, da fehlt mir noch der Ansatz.
    naja, nach dieser Aussage hast du ja alles wesentliche bereits verstanden, und jeglichen Ansatz.

    Für "wie man das programmiert" weiss ich nun nicht, wie diese "Ansatz-Frage" beantworten. (Und scheinbar die anderen auch nicht, weil die hühnern alle auf den Dingen herum, die du ja schon verstanden hast)
    Also "wie man das programmiert" - Man braucht dafür einen Computer, Windows muss installiert sein, eine Tastatur, und dann mit den Fingern immer auf die Tasten drücken, in richtiger Reihenfolge ...

    Das wäre also die Antwort auf deine Frage, aber sicherlich nicht nützlich.
    Folglich verstehe ich die Frage wohl nicht, und vlt. probierst du nochmal verständlicher zu fragen, wofür du einen Ansatz brauchst.
    Weil Formelparser, Infix, Postfix und den Stack hast du ja schon verstanden - was also nicht?
    Ich habe es jetzt über 2 Funktionen "gelöst".

    VB.NET-Quellcode

    1. Public Function Calculate(Formel As String) As Double
    2. Dim Ergebnis As Double = 0
    3. Dim numberNeeded As Boolean = True
    4. Dim adding As Boolean = True
    5. Dim Multiply As Boolean = True
    6. Dim Rechnen As Boolean = True
    7. For i As Integer = 0 To Formel.Length - 1
    8. If numberNeeded Then
    9. Dim nextNum As Double = GetZahl(Formel, i)
    10. If Rechnen Then
    11. If Multiply Then
    12. If Ergebnis = 0 Then
    13. Ergebnis = nextNum
    14. Else
    15. Ergebnis *= nextNum
    16. End If
    17. Else
    18. If Ergebnis = 0 Then
    19. Ergebnis = nextNum
    20. Else
    21. Ergebnis /= nextNum
    22. End If
    23. End If
    24. Else
    25. If adding Then
    26. Ergebnis += nextNum
    27. Else
    28. Ergebnis -= nextNum
    29. End If
    30. End If
    31. Else
    32. Dim c As Char = Formel(i)
    33. If c = "*"c Then
    34. Multiply = True
    35. Rechnen = True
    36. ElseIf c = "/"c Then
    37. Multiply = False
    38. Rechnen = True
    39. ElseIf c = "+"c Then
    40. adding = True
    41. Rechnen = False
    42. ElseIf c = "-"c Then
    43. adding = False
    44. Rechnen = False
    45. Else
    46. Throw New Exception("Rechenzeichen nicht vorhanden")
    47. End If
    48. End If
    49. numberNeeded = Not numberNeeded
    50. Next
    51. If numberNeeded Then
    52. Throw New Exception("Die Rechnung ist falsch eingegeben")
    53. End If
    54. Return Ergebnis
    55. End Function
    56. Public Function GetZahl(Formel1 As String, ByRef index As Double) As Double
    57. Dim startIndex As Double = index
    58. While index < Formel1.Length
    59. If Not Char.IsNumber(Formel1(index)) Then
    60. If index = startIndex Then
    61. Throw New Exception("Eine Zahl wurde nicht eingegeben")
    62. Else
    63. index -= 1
    64. Return Double.Parse(Formel1.Substring(startIndex, index - startIndex + 1))
    65. End If
    66. End If
    67. index += 1
    68. End While
    69. If index = startIndex Then
    70. Throw New Exception("Eine Zahl wurde nicht eingegeben.")
    71. End If
    72. Return Double.Parse(Formel1.Substring(startIndex))
    73. End Function

    Ist jetzt zwar nicht wunderschön, aber es funktioniert soweit, bis auf Punkt vor Strich (Potenzen und Wurzeln hinzuzufügen ist jetzt auch nicht das Problem mehr, praktisch dasselbe Schema). Wie kann ich quasi diese Regel hinzufügen bzw. wie muss ich das hier um schreiben, dass er auch Punkt vor Strich anwendet?
    Und Klammern kann er auch schon? Also das bei folgenden Inputs verschiedenes rauskommt?
    • 9 - (3 + 1)
    • (9 - 3) + 1

    Moritz Zander schrieb:

    Wie muss ich das hier um schreiben, dass er auch Punkt vor Strich anwendet?
    Das geht nicht.
    Dein Ansatz geht einfach nicht.
    Es wurden dir zwei funktionierende Ansätze genannt. Der eine ist sehr anspruchsvoll, der andere ist einfach, kann aber leider keine Potenzierung.


    @Moritz Zander Poste mal ein paar Beispiele, wie Dein Formel-String aussieht, um das nachvollziehen zu können.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @RodFromGermany 12+12*12 Das wäre ja normal 156, er rechnet aber 288 aus, weil er quasi erst 12+12 rechnet und das dann mal 12, da er Zahl für Zahl durch geht. Er rechnet die Zahl dem Ergebnis hinzu, müsste das also anders machen.

    @ErfinderDesRades Ich habe ja hier praktisch Zahl für Zahl eingelesen. Statt das in eine Variable zu packen, sollte es doch möglich sein, das auf einen Stack zu packen, oder?

    Moritz Zander schrieb:

    müsste das also anders machen.
    Hast Du Dich auch mal mit Post #25 beschäftigt?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @ErfinderDesRades Nein, mein Algo kann das nicht.

    @RodFromGermany

    RodFromGermany schrieb:

    Hast Du Dich auch mal mit Post #25 beschäftigt?

    Ja, habe ich, aber ich bin nicht weiter gekommen, weil ich nicht weiß, was ich mit den Operatoren (Rechenzeichen) machen soll bzw. um es korrekt auszudrücken ich kann sie zwar einspeichern und erkennen, aber ich kann sie nicht zum Rechnen verwenden / in die Rechnung einbauen.
    @Moritz Zander Wie hast Du denn bisher Deine Operatoren bedient?
    Was ist denn der Unterschied zwischen Deiner bisherigen und meiner mutmaßlichen Behandlung?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Wie hast Du denn bisher Deine Operatoren bedient?

    Z.B. Wenn "+" vorhanden ist, dann Wert1 + Wert2 oder Ergebnis += Nächster Wert

    RodFromGermany schrieb:

    Was ist denn der Unterschied zwischen Deiner bisherigen und meiner mutmaßlichen Behandlung?

    Vielleicht denke ich zu kompliziert, aber ich verstehe dich so: 4+5*6 -> Wert1 = 4, Operator1 = "+", Wert2 = 5, Operator2 = "*", Wert3 = 6 -> Operator2 hat mehr Priorität, daher Wert2 * Wert3, anschließend + Wert1. Aber das wäre ja so alles vorgegeben. Wie kann ich Variablen dem Operator zuweisen, wenn ich vorher nicht weiß, wo multipliziert wird? Ich muss ja quasi der Funktion "sagen", dass sie die Variablen, die vor und nach dem "*" Zeichen miteinander multiplizieren soll. Da aber das "*" Zeichen an unterschiedlicher Stelle sein kann, weiß ich nicht wie ich das machen soll. Oder ich denke mal wieder viel zu kompliziert.
    Na ja genau deswegen musst du den Term in eine von einem Computer verarbeitbare Form bringen -> die so genannte Posfix Notation. Diese kannst du dann relativ einfach der Reihe nach ausrechnen.
    Siehe dazu meinen vorherigen Post...
    ----

    WebApps mit C#: Blazor
    Keine einfache Sache.

    Damit das richtig erkannt wird, muss das zuerst für den Parser korrekt lesbar dargestellt werden.

    4
    5
    6
    *
    +

    Weil jetzt hat der Parser auch die Möglichkeit 2er-Gruppen zu bilden



    Zuerst durchiterieren bis zum ersten Operator
    4,5,6 >> *

    In der Stack (Werteübernahme) siehts dann so aus
    6
    5
    4

    2er Gruppe bilden
    5,6 * >> 30

    die neue Stack sieht dann so aus
    30
    4

    4,30 + >> 34

    die neue Stack sieht dann so aus
    34

    Resultat OK
    @Moritz Zander Meine Intention zielt darauf:
    Sofort bei der Eingabe erkennst Du, ob numerischer Wert oder Operator eingegeben wird.
    Dort trennst Du die Strings und packst sie in eine String-Liste.
    Aus der String-Liste generierst Du Operanden und Operatoren, da kannst Du gleich einen Syntax-Check machen, z.B. nicht zwei Zahlen oder zwei binäre Operatoren nacheinander (bei *-5 ist * ein binärer, - ein unärer Operator).
    Dann gehst Du die Operatoren durch: Klammerausdrücke von innen nach außen finden und rekursiv lösen (d.h., Du gehst mit einer kürzeren Liste in dieselbe Prozedur);
    Funktionen berechnen (sin(), cos(), ...), die Lösung ersetzt die Klammer bzw. die Funktion.
    Wenn alle Klammern alle aufgelöst und alle Funktionen berechnet sind, bleiben nur noch +-*/ übrig, da rechnest Du dann nach Punktrechnung vor Strichrechnung
    und ersetzt Operand1 Operator Operand2 durch dessen Lösung.
    Und das so lange, bis nur noch ein Term übrig bleibt, die Gesamt-Lösung.
    Feddich.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!