Autor |
Beitrag |
→Tobi
      
Beiträge: 23
|
Verfasst: Sa 07.02.09 21:50
Ich habe schon versucht ein solches Projekt umzusetzen. Also einen Parser in C#, der eine mathematische Gleichung auflöst. Ich habe zwar schon ein paar Grundkonzepte gefunden, aber noch nicht umsetzen können.
Also ich möchte zuerst die eingeklammerten Ausdrücke suchen. Diese muss ich dann ersetzen. Allerdings weiß ich da schon nicht weiter: Durch was soll ich diese ersetzen? Wie soll ich die ersetzen?
Also wenn ich beispielsweise die Zeichenkette "2 + 3 - (5 + 2)" habe. Ich wäre für einen konkreten Code dankbar.
|
|
CarpeDiem
      
Beiträge: 128
WIN XP
Borland C++ 2002, CodeGear C++ 2007, C# (VS 2008)
|
Verfasst: Sa 07.02.09 22:03
_________________ Stefan - Carpe Diem
Es ist keine Schande zu fallen, eine Schande ist nur nicht wieder aufzustehen
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Sa 07.02.09 22:04
Was willst du jetzt eigentlich? Selbst einen Parser schreiben oder einen fertigen Code?
Fertige Parser findest du im Internet viele, z.B. hier:
www.bestcode.com/html/bcparser_net.html
www.codeproject.com/KB/cs/MathParser.aspx
|
|
→Tobi 
      
Beiträge: 23
|
Verfasst: Sa 07.02.09 23:05
Also der erste Link hat mir schon geholfen. Vom Verständnis her. Leider ist der Code in Java und ich weiß nicht ob es für jeden Befehl ein Synonym in C# gibt.
Ich möchte eigentlich einen eigenen Parser entwickeln.
Kennt jemand eine Übersicht über die Operatoren und Methoden von Strings?
Edit: Ok. Dumme Frage. Wofür gibts IntelliSense.
|
|
→Tobi 
      
Beiträge: 23
|
Verfasst: So 08.02.09 12:48
Weiß jemand wie ich die Zahl vor und nach einem bestimmten Zeichen bestimmen kann, diese einklammere oder etwas in der Art. Zum Beispiel:
( 200 - ( 25 + 15 * 5 ) ) --> ( 200 - ( 25 + ( 15 * 5 ) ) )
|
|
Kha
      
Beiträge: 3803
Erhaltene Danke: 176
Arch Linux
Python, C, C++ (vim)
|
Verfasst: So 08.02.09 14:00
Hab mir den Java-Artikel nicht durchgelesen, aber da müsste das doch behandelt werden?
Wobei die String-Ersetzen-Methode natürlich etwas naiv ist; falls du tiefer in die Materie eintauchen willst, könntest du dir ein Skript anschauen, das miniC# vor kurzem gepostet hat: www.itu.dk/people/kfl/parsernotes.pdf (kein Java  )
_________________ >λ=
|
|
→Tobi 
      
Beiträge: 23
|
Verfasst: So 08.02.09 14:04
Ich hab schon ne Idee: mit einer Schleife das nächste Zeichen links und rechts vom gesuchten Zeichen suchen und diesen Teil ( zwei Zahlen und ein Zeichen ) einklammern und ausrechnen lassen. Das Suchen nach den beiden Zeichen mach ich wie gesagt mit einer Schleife und einem Vergleich des jeweiligen Zeichens mit einem Array, in dem die definierten Zeichen ( Rechenoperatoren ) enthalten sind.
Alles noch Theorie. Aber ich werds versuchen.
Welchen Namen hat eigentlich die Vorgehensweise dieses Algorithmus?
|
|
Kha
      
Beiträge: 3803
Erhaltene Danke: 176
Arch Linux
Python, C, C++ (vim)
|
Verfasst: So 08.02.09 15:13
→Tobi hat folgendes geschrieben : | Welchen Namen hat eigentlich die Vorgehensweise dieses Algorithmus? |
Ich glaube nicht, dass es einen gibt. Alle Standardalgorithmen suchen nicht gezielt nach einem Token wie "*", sondern gehen stur von links nach rechts (nämlich LL oder LR) durch die Eingabe. Der einfachste standardmäßige Parser für mathematische Terme ist ein Recursive Descent (bzw. Predictive) Parser, wird wahrscheinlich auch in dem Skript von oben verwendet.
_________________ >λ=
|
|
AdrianK
      
Beiträge: 56
Kubuntu 9.04 Jaunty
Mono 2.4 + MonoDevelop 2.0; Qt Creator
|
Verfasst: So 08.02.09 17:14
Hallo, ich entwickle gerade auch einen Formelparser in C# und ich hab's so gelöst:
Als erstes wird der Term in eine List<String> aufgeteilt, z.B. so:
Quelltext 1: 2:
| 3+43*5 = [3][+][4][3][*][5] |
Danach werden Zahlen und Kommas zusammengefasst:
Quelltext
Jetzt ist es kein Problem mehr den Term zu berechen, einfach nach allen *, /, + und - suchen und sie berechen. Ungefähr so:
C#-Quelltext 1: 2: 3: 4:
| int position = term.IndexOf("*"); term[position-1] = Convert.ToString(Convert.ToDouble(term[position-1])*Convert.ToDouble(term[position-1])); term.RemoveAt(position); term.RemoveAt(position); |
Mit Rekursion sind dann auch Klammern kein Problem mehr. Hoffe ich konnte dir helfen 
|
|
JüTho
      
Beiträge: 2021
Erhaltene Danke: 6
Win XP Prof
C# 2.0 (#D für NET 2.0, dazu Firebird); früher Delphi 5 und Delphi 2005 Pro
|
Verfasst: So 08.02.09 17:27
@Adrian
Das Verfahren gefällt mir überhaupt nicht, weil ständig zwischen String und Zahl konvertiert werden muss; beachte außerdem die "zwangsläufigen" Rundungsfehler bei Berechnungen mit double.
Wenn überhaupt, dann sind zwei Listen zu verwenden: List<decimal> mit den Zahlen und List<char> mit den Rechenzeichen und Klammern. Aber vermutlich gibt es in den vielen Links gute/bessere Vorschläge.
Gruß Jürgen
|
|
→Tobi 
      
Beiträge: 23
|
Verfasst: So 08.02.09 20:04
Mhh... Ja, die Idee hatte ich auch schon. Ich war mir nur nicht ganz sicher wie ich dann die drei Elemente im Array durch eines, nämlich das Ergebnis ersetze. Wobei ich natürlich nicht ständig mit diesem Array arbeiten muss. Ich müsste es nur nach jedem Durchgang (zwei Zahlen miteinander verrechnen) neu bilden. Dauert das nicht ziemlich lange?
Wenn nicht: Gute Idee. Wäre einen Versuch wert, vor allem weil es mein erster konkreter Ansatz mit Beispielcode wäre.
Danke.
|
|
JüTho
      
Beiträge: 2021
Erhaltene Danke: 6
Win XP Prof
C# 2.0 (#D für NET 2.0, dazu Firebird); früher Delphi 5 und Delphi 2005 Pro
|
Verfasst: So 08.02.09 21:15
→Tobi hat folgendes geschrieben : | Ich war mir nur nicht ganz sicher wie ich dann die drei Elemente im Array durch eines, nämlich das Ergebnis ersetze. Wobei ich natürlich nicht ständig mit diesem Array arbeiten muss. Ich müsste es nur nach jedem Durchgang (zwei Zahlen miteinander verrechnen) neu bilden. Dauert das nicht ziemlich lange? |
Richtig. Deshalb solltest Du auch keinesfalls Arrays verwenden, sondern List<T>, wo Du jederzeit Elemente entfernen und z.B. 2 Zahlen durch das Ergebnis der aktuellen Teilrechnung ersetzen kannst (genauer: eine Zahl wird durch das Ergebnis ersetzt, die zweite Zahl gestrichen, der Operator in der zweiten Liste ebenfalls).
Jürgen
|
|
UGrohne
      

Beiträge: 5502
Erhaltene Danke: 220
Windows 8 , Server 2012
D7 Pro, VS.NET 2012 (C#)
|
Verfasst: So 08.02.09 22:12
Mal ein kleiner Hinweis am Rande: Vor ein paar Tagen bin ich einige C#-Features durchgegangen und da sind mir Ausdrucksbäume aufgefallen. Und ich habe mir so gedacht, dass sich das eventuell eignen würde, einen eigenen Formelparser zu schreiben.
Vielleicht will sich ja mal jmd mit dem Thema beschäftigen und das erruieren?! 
|
|
Kha
      
Beiträge: 3803
Erhaltene Danke: 176
Arch Linux
Python, C, C++ (vim)
|
Verfasst: So 08.02.09 23:43
Expression Trees, hach... Ja, da hab ich schon länger etwas geplant  . Projekt Abi hat aber leider erstmal höhere Priorität  .
Zur Klarstellung: Beim Parsen helfen die nichts. Der große Nutzen liegt im eingebauten LCG[meta]Lightweight Code Generation - DynamicMethod[/meta]-Compiler, mit dem sich ein interessanter kompilierender Matheparser bauen lassen sollte.
_________________ >λ=
|
|
UGrohne
      

Beiträge: 5502
Erhaltene Danke: 220
Windows 8 , Server 2012
D7 Pro, VS.NET 2012 (C#)
|
Verfasst: Mo 09.02.09 09:45
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mo 09.02.09 11:11
Kha hat folgendes geschrieben : | Der große Nutzen liegt im eingebauten LCG[meta]Lightweight Code Generation - DynamicMethod[/meta]-Compiler, mit dem sich ein interessanter kompilierender Matheparser bauen lassen sollte. |
Ja, das geht. Als ich das hier gelesen hatte und MSDN über Expression Trees befragt hatte, habe ich mich vorhin mal drangesetzt und das ausprobiert. Jetzt (2 Stunden später  ) habe ich das so weit verstanden und einen Parser hinbekommen, der die Grundrechenarten ohne Klammern oder Punkt vor Strich in einen Expression Tree übersetzt und dann mit verschiedenen Variablenwerten gefüttert werden kann.
Ich überlege noch wie ich die Variablenwerte einfacher in der GUI einfüttern lassen kann (also variabel was die Anzahl etc. angeht).
Leider fehlen mir noch etliche Kenntnisse in C#, deshalb habe ich ein wenig Probleme mit einer guten Umsetzung. Z.B. wusste ich bis heute Morgen auch nicht was delegates sind, wie ich Eigenschaften genau definiere, usw., aber ich werde im Laufe des Tages vielleicht noch ein wenig Zeit haben da weiter zu basteln und dann das Ergebnis posten. 
|
|
AdrianK
      
Beiträge: 56
Kubuntu 9.04 Jaunty
Mono 2.4 + MonoDevelop 2.0; Qt Creator
|
Verfasst: Mo 09.02.09 17:24
→Tobi hat folgendes geschrieben : | Mhh... Ja, die Idee hatte ich auch schon. Ich war mir nur nicht ganz sicher wie ich dann die drei Elemente im Array durch eines, nämlich das Ergebnis ersetze. Wobei ich natürlich nicht ständig mit diesem Array arbeiten muss. Ich müsste es nur nach jedem Durchgang (zwei Zahlen miteinander verrechnen) neu bilden. Dauert das nicht ziemlich lange?
Wenn nicht: Gute Idee. Wäre einen Versuch wert, vor allem weil es mein erster konkreter Ansatz mit Beispielcode wäre.
Danke. |
Geht nicht sehr lange: einfache Berechnungen auf nem P4 2,4 Ghz (z.B. 3^2,43345+4,345) unter 1ms, kompliziertere mit mehreren klammern etc. so um die 10-30ms.
|
|
→Tobi 
      
Beiträge: 23
|
Verfasst: Mo 09.02.09 18:04
Danke für die vielen guten Antworten.
Also ich denke ich gebe meine Idee mit dem Parser, der nach den Zeichen mit höchster Priorität sucht auch. Der ist doch sehr viel komplizierter zu realisieren als ich dachte. Ich habe es jetzt endlich mit Mühe und Not geschafft eine halbwegs funktionierende Punkt-Vor-Strich-Rechnung zu integrieren. Aber der Code ist bestimmt 300 Zeilen lang und absolut verbugt. Außerdem denke ich, dass die andere Methode effektiver ist (sonst hätte man meine Idee ja schon irgendwann mal umgesetzt ^^).
Also noch mal zusammenfassend:
Ich erzeuge eine Variable Liste mit Strings, die dann nach und nach in das Ergebnis umgeformt wird. Diese Liste geht der Parser durch. Wenn er nun eine öffnende Klammer findet geht fängt er dort erst mal an sie zu lösen. Wenn er eine Buchstabenkette findet, überprüft er ob damit eine Funktion (z.B. Sinus) gemeint ist, oder eine Variable.
Was genau ist so ein LCG-Compiler? Ist der für einen simplen Compiler wichtig? (Ich will überhaupt erst mal einen hinbekommen!^^)
Das ist zwar noch sehr, sehr hochgegriffen - aber wäre es möglich ein Programm zu schreiben, dass eine Formel vereinfacht? Zum Beispiel "225x - 175x + (-45x)". Der Zweck meines Programms ist nämlich einen Graphen zu zeichnen. Und der Parser soll später für ein bestimmtes Intervall die y-Werte in Abhängigkeit von x bestimmen. Da wäre eine verkürzte Formel ja wesentlich effektiver.
Also dann werd ich mal versuchen diese Idee umzusetzen.
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mo 09.02.09 18:13
→Tobi hat folgendes geschrieben : | Was genau ist so ein LCG-Compiler? Ist der für einen simplen Compiler wichtig? (Ich will überhaupt erst mal einen hinbekommen!^^) |
Sowas brauchst du nicht, du kannst es auch einfacher machen.
Und das hat mit dem Parsen auch nichts zu tun sondern danach mit der Auswertung.
|
|
AdrianK
      
Beiträge: 56
Kubuntu 9.04 Jaunty
Mono 2.4 + MonoDevelop 2.0; Qt Creator
|
Verfasst: Mo 09.02.09 18:16
Das mit dem Sin Cos Tan Rnd etc. kannst du vereinfachen, indem du bevor du irgendwas berechnest die Sinuse usw. durch z.b. S ersetzt. Meinen Parser nutze ich übrigens auch um Graphen zu zeichnen.
|
|