Einzelnen Beitrag anzeigen

Benutzerbild von dizzy
dizzy

Registriert seit: 26. Nov 2003
Ort: Lünen
1.932 Beiträge
 
Delphi 7 Enterprise
 
#58

Re: Mathem. Parser -- bitte testen

  Alt 7. Jul 2004, 23:54
Zitat von neolithos:
Grund: Wenn du die Formel zu einer schnell durchrechenbaren Sprache umcompilierst und opimierst, vermute ich, dass das Teil schneller wird.
Die Sprache meines Parsers heisst "Baum"

Zitat von neolithos:
Bei Meinem Formel-Parser (der langsamer ist, durch Typenkonvertierungen und OOP) gehe ich wie folgt vor.
Ist meiner nicht OOP? Und hast du dir mal die ganzen Typecasts angesehen die ich da zwischen drin sitzen hab?

Zitat von neolithos:
(1 + sin(A))^3 * (1+1)

Erster Schritt:

zerlegen in Token (Teile):

"1" "+" "sin" "(" "A" ")" "^" "3" "*" "(" "1" "+" "1" ")"
Dies erledigt eine while Schleife gekoppelt mit einer Matrix.

Baue aus den Tokens einen Parsebaum:

Dazu wird ein vor definierter Syntax-Graph durchlaufen.
-> Wieder nur eine while-Schleife
Das mache ich z.B. in nur einer Rekursion (procedure TTF(...)). Die dort enthaltene irre große "if..then..else"-Verzweigung legt in ihrer Struktur und Reihenfolge den "virtuellen" Sytaxgraphen fest, und das Ende dieser einen Rekursion die sich direkt den zu parsenden String vornimmt ist der (fast) fertige Baum.

Zitat von neolithos:
Optimiere den Code:
in dem Fall wird 1+1 zusammengefasst.
Deswegen oben das "fast" in Klammern: Das mache ich in 2 rekursiven Schritten. Erst wird von den Blättern anfangend gekennzeichnet welche Knoten überhaupt "zusammenfassungswürdig" sind, also nicht Variablen oder von solchen abhängig sind. Im 2. Durchlauf findet die eigentliche Optimierung statt, die alles was möglich ist zusammen fasst.
(Diese Funktion ist in dem hier eingestellten glaub ich noch nicht vorhanden. Bei mir aber sehr wohl )

Zitat von neolithos:
Erzeuge Code:

Code:
push 1
push a
sin
and
push 3
power
push 2
mul
Und die Ausführung dieses Code's könnte man extrem Optimieren.
Array für Stack.
Sprung-Tabelle für Proceduren.
Und genau das spare ich mir. Dafür müsste man den Baum ein mal rekursiv durchlaufen um den Stack zu bilden, und dann muss man noch mal ran und den Stack lösen. Ich löse direkt den Baum, so dass diese rekursive Funktion als Rückgabewert das fertige Ergebnis liefert.

Ich hatte mal testweise 2 verschiedene Stackprinzipe eingebaut: Ein mal mit nur einem Stack, und mal mit getrennten Operanden- Werte- und Ergebnisstack.
Beide Versuche führten zu einer erheblichen Verlangsamung. (Man muss hierbei beachten, dass die Stärke meines Parsers ist, ein und die selbe, ein einziges Mal geparste Formel, immer und immer wieder zu lösen, mit veränderten Variablen. Also zum Plotten von Funktionen gut geeignet, oder halt für Fraktalrechnungen )
Bei der Variante mit einem Stack war das Problem, dass ich für jedes Mal Lösen den gesamten Stack "echt" kopieren musste. Und das hat einfach viel zu lange gedauert.
Bei der Variante mit 3 Stacks fiel der Kopieraufwand weg, aber der Overhead durch die Stackverwaltung in Form von Unterscheidung von Unären und Binären Operatoren, und der Indexverwaltung an sich, war wohl größer als der Overhead einer Rekursion. Zumindest hatten das meine Messungen ergeben.
Von daher bin ich beim Baum geblieben.

Was auch noch mit reinspielt ist, dass ich gerne die 3 Zahlentypen strukturell möglichst gleich abhandeln wollte. Und die Rechnerei mit komplexen Zahlen/Quaternionen gestaltet sich ja noch etwas anders als mit double, da man ja nicht die normalen Operatoren verwenden kann.
Weiteres Schmankerl ist in dieser Hinsicht auch das Variablenhandling. Es gibt 8 Variablen, denen es völlig egal ist ob sie nun ein double-Wert sind, ne komplexe Zahl, oder was auch immer. Daher bleibt die "usability" für den "Endbenutzer" (also Programmierer...) gut. Um mit dem Teil etwas mit double-Werten zu rechnen reicht folgendes:
Delphi-Quellcode:
var p: TCQParser;
    ergebnis: double;
.
.
p := TCQParser.Create;
p.Parse('(1 + sin(A))^3 * (1+1)');
p.SetVariable(A, pi);
p.Solve(ergebnis);
Naja, und ich bin (noch *g*) der Meinung dass ich meine beiden obersten Ziele (Speeeed und einfache Bedienbarkeit) einigermaßen gut erreicht habe.
Wenn du es aber hinbekommen solltest das Dingen noch schneller zu machen, dann bin ich der erste dem die Kinnlade runterfällt *provozier* .

Ich werde die Tage (in 1-2 Wochen) die neue Version posten. Ich will ein Parser-Battle


guts Nächtle,
dizzy
Fabian K.
INSERT INTO HandVonFreundin SELECT * FROM Himmel
  Mit Zitat antworten Zitat