MathPro - Open Source Formelparser

    • Release

    Es gibt 40 Antworten in diesem Thema. Der letzte Beitrag () ist von Damacy.

      Hallo Telcrom.

      Ich habe gerade versucht die DLL in mein Projekt zu laden. Leider sagt mir der Referenzverwalter:

      "Resolved file has a bad image, no metadata, or is otherwise inaccessible. Could not load file or assembly 'C:\Users\...\bin\Debug\MathPro.dll' or one of its dependencies. This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded."
      Ich arbeite noch mit Steinzeit VB 2005 ... gibt es eine Möglichkeit das ich die DLL trotzdem laden kann?
      Die Express Versionen gibt's immer kostenlos. Aber du kannst auch kurz probieren, ob es mit der Lib im Anhang läuft. Da habe ich einfach kurz ein paar Sachen über Suchen & Ersetzen geändert, damit es auch auf dem niedrigen FW läuft.
      Dateien
      • MathProPC.dll

        (16,38 kB, 138 mal heruntergeladen, zuletzt: )
      Hmm das ist komisch, wenn ich genau das eingebe, bekomme ich 479,16 raus, da in dem Term ja hinten noch eine 2 vorhanden ist.

      Den eigentlichen "Termleser" hab ich extra so programmiert, dass ihn möglichst wenig aus der Fassung bringt. Aktuell arbeite ich jedoch an einer Methode, die Fehler im Term erkennt und sie auch nach Möglichkeit korrigiert.

      Dafür müsstest du dann aber Visual Studio mal updaten, da ich persönlich keine Updates mehr für FW 3.0 rausbringe (wäre mir dann einfach zu viel Arbeit, für jede FW Version alles nochmal zu kopieren)

      Telcrome schrieb:

      Den eigentlichen "Termleser" hab ich extra so programmiert, dass ihn möglichst wenig aus der Fassung bringt.
      Das würde ich nicht machen. Falls etwas nicht passt, würde ich sofort eine FormatException schmeissen. Das ist IMO optimal für's Debuggen (und so sollte es auch sein).
      Stell dir mal vor, die IPAddress.Parse()-Funktion aus .NET würde versuchen, falsche Eingaben irgendwie hinzubiegen. Das würde man dann u. U. nicht bemerken und der Client verbindet sich plötzlich mit random Endpoints. Das wäre nicht so geil. :P
      Bei solchen Angelegeneheiten würde ich mir das Verhalten von .NET-Klassen anschauen und es ähnlich oder genauso lösen. Schließlich hält sich das .NET-FW (fast immer) an die MS-Vorgaben. :P

      Telcrome schrieb:

      Aktuell arbeite ich jedoch an einer Methode, die Fehler im Term erkennt und sie auch nach Möglichkeit korrigiert.
      Sowas würde ich nicht in den eigentlichen Parser direkt einbauen, wie ich oben schon geschrieben habe. Lagere das in eine Extra-Klasse aus. Eine Korrigier-Funktion ist IMO auch schon zu viel, da du nicht weißt, was dort alles so eingegeben werden kann (nur Whitespace? Kommt es von einem User? Per HttpWebResponse von einer Webseite, die da noch HTML-Tags reinhauen, und auch noch als Matheoperationen gelten könnten?).
      Ich würde nur eine Klasse bereitstellen, die prüft, ob ein Term valide ist. Alles andere würde ich den User der Library überlassen. Zusätzlich würde ich noch eine TryParse-Funktion bereitstellen, die, statt eine FormatException zu schmeissen, einfach False returnt. Das Ergebnis kann man dann über einen out-Parameter zurückgeben (halt so wie beim standard Parse/TryParse-Pattern aus .NET).
      Von meinem iPhone gesendet
      @Artentus
      Danke :thumbup:

      @nikeee13
      Ich stelle mir die Fehlererkennung eher so vor:

      VB.NET-Quellcode

      1. if istFehlervorhanden == true then
      2. Parser.Mache Rechnungen()
      3. else
      4. Parser.Spring aus dem Fenster()
      5. end if

      Da die Rechnungen ja auch 400 automatische Werteberechnungen sein könnten (Graphenzeichner usw.) und bei solchen Anwendungsszenarien würde man dann ja auch immer (unnötigerweise) die Fehlererkennung durchlaufen lassen.
      Abgesehen davon sind Fehleingaben mit Buchstaben keine wirklichen Fehleingaben, da diese später als Variablen erkannt werden sollen. (x,y,z -> Laufvariablen und alles andere Formvariablen).
      Dies ist z.B. für die Berechnung von Ableitungen wichtig.

      Eine TryParse Funktion würde im Prinzip das gleiche bieten wie der Pseudocode oben. Funktionen, die so schnell zu implementieren sind, packe ich später in eine weitere Klasse und deklariere diese Funktionen dann als static. Aber während ich noch einigermaßen viel ändere wäre das nur Zeitverschwendung.

      Das würde ich nicht machen. Falls etwas nicht passt, würde ich sofort eine FormatException schmeissen. Das ist IMO optimal für's Debuggen (und so sollte es auch sein).
      Stell dir mal vor, die IPAddress.Parse()-Funktion aus .NET würde versuchen, falsche Eingaben irgendwie hinzubiegen. Das würde man dann u. U. nicht bemerken und der Client verbindet sich plötzlich mit random Endpoints. Das wäre nicht so geil. :P

      Das wäre in der Tat nicht so lustig^^ Aber unter einer Autokorrektur stelle ich mir auch eher so etwas vor:

      Quellcode

      1. (3+3)*(4+4

      wird zu:

      Quellcode

      1. (3+3)*(4+4)

      Das Problem beim Debuggen bleibt natürlich, aber eine solche Funktion würde ich auch nicht direkt in den Parser integrieren und das Error beim Fehlen einer Klammer übernimmt VS bereitwillig :D

      Telcrome schrieb:

      Aber unter einer Autokorrektur stelle ich mir auch eher so etwas vor:
      Und was ist, wenn der Fehler nicht die fehlende schließende Klammer ist, sondern die versehentlich gesetzte öffnende? Dann wäre der gewollte Term (3+3)*4+4 und nicht (3+3)*(4+4).
      Ich würde - gerade bei sowas - die Ansicht "lieber gar kein Ergebnis statt einem falschen" vertreten.

      Edit:
      Was ich auch noch spannend finden würde:
      Ich weiß nicht ob dein Parser unbekannte Variablen unterstützt. Wenn du das aber mal einbauen solltest oder vielleicht schon hast, wäre das folgende ziemlich nett:
      Stelle einen delegaten bereit, der bei der Verwendung den Term mit eingesetzen Unbekannten (bzw. ohne) auswertet. Beispiel:

      C-Quellcode

      1. // eineFunktion nimmt 2 double und gibt einen double zurück.
      2. Func<double, double, double> eineFunktion = MathParser.GetDelegate("2*x+24.8/y", "x", "y");
      3. // Verwendung:
      4. var ergebnis = eineFunktion(10.0, 2.0); // parser setzt 10 und 2 für X und Y ein, parst, evaluiert und gibt das Ergebnis zurück
      5. //Wäre von der Funktionsweise genau das hier:
      6. double x = 10.0;
      7. double y = 2.0;
      8. var ergebnis = MathParser.Parse(string.Format("2*{0}+24.8/{1}", x, y))
      Natürlöich könntest du es vorerst auch ohne Unbekannte machen, aber das ist lange nicht so spannend.

      (@Telcrome:)
      Von meinem iPhone gesendet

      Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „nikeee13“ ()

      nikeee13 schrieb:


      C-Quellcode

      1. // eineFunktion nimmt 2 double und gibt einen double zurück.
      2. Func<double, double, double> eineFunktion = MathParser.GetDelegate("2*x+24.8/y", "x", "y");
      3. // Verwendung:
      4. var ergebnis = eineFunktion(10.0, 2.0); // parser setzt 10 und 2 für X und Y ein, parst, evaluiert und gibt das Ergebnis zurück
      5. //Wäre von der Funktionsweise genau das hier:
      6. double x = 10.0;
      7. double y = 2.0;
      8. var ergebnis = MathParser.Parse(string.Format("2*{0}+24.8/{1}", x, y))

      Natürlöich könntest du es vorerst auch ohne Unbekannte machen, aber das ist lange nicht so spannend.

      Bisher werden Unbekannte noch gar nicht unterstützt^^ Kommt aber bald hinzu

      Dein Vorschlag ist eine schöne Sache für das Ausrechnen der eingegebenen Funktion bzw. die Berechnung von Funktionswerten an bestimmten Stellen.
      Ich gucke mal wie ich die Variablen am besten in den Parser reinkriege.
      (wenn kein Wert zugewiesen ist muss die Variable ja auch so wieder zurückkommen (-2+3b+4 -> 3 * b + 2). Hintergrund ist, dass ich mehr ermöglichen möchte als das einfache Ausrechnen der Funktion.

      Das Ziel dieser Lib ist eigentlich auch gar keine Windows-Anwendung, sondern eine Win8 App bzw. WP8 App mit der Möglichkeit zum Ausrechnen der Kurvendiskussion (idealerweise mit menschlichem Rechenweg) und allgemein das Abnehmen von einem Teil der Schulmathematik. (Zielgruppe sind also Schüler der Klassen 5-12).
      Da das eine ganze Menge Arbeit ist, ist das hier auch mein Langzeitprojekt an dem ich immer mal wieder 1-2 Stunden arbeite^^

      Telcrome schrieb:

      Ich gucke mal wie ich die Variablen am besten in den Parser reinkriege.
      Bvor du das machst, kannst du auch gleich überlegen, ob du nicht nur Variablen, sondern auch ganze Unterfunktionen einbaust. D. h. dass ein Platzhalter nicht nur ein fester Wert sein kann, sondern auch eine ganze Expression-Instanz, die dann auch noch evaluiert wird. Z. B:

      C-Quellcode

      1. var exp1 = new MainExpression("sin(y-5)", "y");
      2. var exp2 = new MainExpression("asin(x)+4", "x");
      3. var res = exp2.Eval(
      4. new KeyValuePair("x", exp1), // die gesamte Expression exp1 für x einsetzen
      5. new KeyValuePair("y", 10.0) // nur die Zahl 10 für y einsetzen
      6. );
      7. //Evaluiert wird anschließend:
      8. // asin((sin(10.0-5)))+4

      Das wäre dann ähnlich dem Konzept "let", wie es u. A. in der Mathe-Sprache F# realisiert ist.

      Das kombiniert mit den Delegates wäre das schon ein relativ mächtiges Werkzeug.
      Von meinem iPhone gesendet
      Das ist auch eine gute Idee, das könnte ich bei den Vektoren eigentlich auch machen. Die einzelnen Werte können dann auch noch aus Funktionen bestehen^^ Zumindest bei den Variablen muss ich dann aktuell nur den Typ von double zu Expression ändern. Bei den Vektoren gucke ich mal, wo ich überall noch Änderungen machen muss.

      So sieht die Liste aus, nachdem der Term 9a+4-2 eingelesen wurde.
      Bilder
      • ExpressionList.png

        13,19 kB, 524×188, 157 mal angesehen

      nikeee13 schrieb:

      Und was ist, wenn der Fehler nicht die fehlende schließende Klammer ist, sondern die versehentlich gesetzte öffnende? Dann wäre der gewollte Term (3+3)*4+4 und nicht (3+3)*(4+4)

      Dann gib mal "(3+3)*(4+4" in den Taschenrechner ein. Der wirft nämlich auch keinen Fehler, sondern fügt eine abschließende Klammer hinzu. Warum sollte es hier anders sein?

      Was mir echt gefallen würde ist, wenn man die einzelnen Terme der Formel, in einer Liste oder ähnlichem hat. Also jeden einzelnen Teil der Rechnung, aufgeteilt und verarbeitbar in einer List(of Term) oder so. :)

      Bei folgender Rechnung wird eine Exception geworfen:

      VB.NET-Quellcode

      1. Dim ex As New MathPro.Expression()
      2. ex.Infix = "(2+3)*(2+4"
      3. ex.Eval()
      4. MsgBox(ex.Infix)

      Aber keine "Kann den String nicht Parsen oder ausrechnen"-Exception, sondern eine NullReferenceException. Das sollte wohl nicht so sein oder?

      Ansonst, tolle Arbeit. :)
      SWYgeW91IGNhbiByZWFkIHRoaXMsIHlvdSdyZSBhIGdlZWsgOkQ=

      Weil einfach, einfach zu einfach ist! :D

      BiedermannS schrieb:

      Dann gib mal "(3+3)*(4+4" in den Taschenrechner ein. Der wirft nämlich auch keinen Fehler, sondern fügt eine abschließende Klammer hinzu. Warum sollte es hier anders sein?
      Der Windows 7-Taschenrechner hat bei der Termeingabe keine Textbox, sondern irgendetwas anderes. Das heißt, dass wenn die Klammer falsch geöffnet wurde, der komplette Term bis dort hin rückgängig gemacht werden müsste, um sie zu entfernen. Deshalb kann man dort davon ausgehen, dass hinter der Klammer eine Intention steckt. Wenn man hingegen in einer normalen Textbox einen Term eingibt, kann man jeden Buchstaben wahlfrei bearbeiten, was dazu führt, dass man zur Korrektur nicht den kompletten Term bis zur Klammer wieder entfernen muss.

      Du kannst ja mal in C# die eine oder andere Klammer weglassen:

      C-Quellcode

      1. void abc()
      2. {
      3. if(a == b)
      4. {
      5. defg();
      6. }
      Denn wenn eine Klammer fehlt, ist die Syntax schlichtweg falsch. Ein Expressionparser ist eher mit einem Interpreter einer Programmiersprache zu vergleichen, als dem Windows-Taschenrechner.
      Von meinem iPhone gesendet

      nikeee13 schrieb:

      Der Windows 7-Taschenrechner hat bei der Termeingabe keine Textbox

      Ich rede auch nicht von dem Windows Taschenrechner, sondern von einem normalen Taschenrechner ala Texas Instruments TI-30X IIS.

      Wenn du bei dem eine Klammer nicht zumachst, fügt er diese automatisch mit ein. Darum finde ich, das es der Parser auch können sollte. Man könnte es ja über eine Eigenschaft aktivieren/deaktivieren lassen.

      nikeee13 schrieb:

      Du kannst ja mal in C# die eine oder andere Klammer weglassen:

      Hier gehts um mathematische Eingaben, nicht um das schreiben von Programmen. Da der Parser, welcher die Programmiersprache einliest, wesentlich mehr "verstehen" muss, als ein Parser welcher mit Mathematischen Formeln zurecht kommen muss (heißt nicht, dass die Formeln nicht komplizierter sein können ^^), ist es nerständlich, auf Grund der Komplexität der Programmiersprachen, die Klammer als Pflicht-Zeichen anzusehen.

      Weitergehend muss man hier sagen, dass es sich ja nicht nur um einen reinen Parser handelt, sondern eigentlich um einen Parser mit implementierter Rechenfunktion. Und wenn man die Rechenfunktionen implementiert, sollte man dem User (also demjenigen der die Lib verwendet) die Möglichkeit geben, die Klammern am Ende hinzufügen zu lassen oder nicht. Dann kann jeder für sich selbst entscheiden, ob der Parser versuchen soll, solche "Fehler" auszubessern oder ob eine entsprechende Exception geworfen werden soll. (Z.B.: "Unmatched Parenthesis" Mit evtl. noch einer Angabe, welche Klammer als nicht geschlossen erkannt wird und ob die linke oder rechte Klammer fehlt)
      SWYgeW91IGNhbiByZWFkIHRoaXMsIHlvdSdyZSBhIGdlZWsgOkQ=

      Weil einfach, einfach zu einfach ist! :D
      Aktuell finde ich es am besten, wenn man einfach Methoden für beide Fälle hat.
      Aber das ist etwas, was sehr wenig mit dem eigentlichen Rechnen zutun hat. Von daher werde ich sowas wahrscheinlich erst kurz vor dem Beginn der Appentwicklung implementieren. (Vllt. muss ich ja noch was an MathExpression ändern, dann müsste ich ja solche Methoden auch neu schreiben)

      So stelle ich mir die Nutzung der fertigen Lib vor:
      Für den, der nur Exceptions haben will:

      C-Quellcode

      1. string exception = myExpression.Mistakes;


      Für den, der Autokorrektur haben will:

      C-Quellcode

      1. myExpression.CorrectBrackets();
      2. myExpression.eval();


      Dann kann man sich (ich hoffe mal leicht verständlich) die bevorzugte Methode zum Ausrechnen bauen.