![]() |
Funktionsplotter
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo alle zusammen...
Ich bin gerade dabei einen ziemlich umfangreichen funktionsplotter zu schreiben. Dafür habe ich jetzt einen Matheparser programmiert, der auch schon ziemlich gut funktioniert. Das Zeichnen von Funktionen geht auch wunderbar. Jedoch dauert mir das alles viel zu lange, da ich wenn ich zu einem x wert den y wert ausrechne, jedesmal wieder meine funktion (ein string) parse... Jetzt wollte ich fragen ob und wie ich das vielleicht anders machen könnte um das plotten von Funktionen zu optimieren. Denn eigentlich müsste es doch reichen den string nur 1 mal zu parsen. Ich habe nämlich schon viele plotter gesehen, die viel schneller zeichnen als meiner. Und an meinem Parser liegt die Geschwindigkeit nicht, da dieser eigentlich im Vergleich sehr schnell ist. Mein Plotter ist ürbigens im Anhang angehängt, damit man sich vielleicht mal ein Bild machen kann. Hier aber erstmal meine Prozedur.
Delphi-Quellcode:
procedure ZeichneGraph(s:string;image:TImage);
const dx = 0.001; var x,y : real; function f(x : real) : extended; begin result := ParsestringX(s, x); // Hier kommt mein parser zum Einsatz end; begin if functions[arraycount - 1].visible then begin x := xmin; y := f(x); image.canvas.MoveTo(ut(x),vt(y)); while x < xmax do begin y := f(x); if (y < ymax) and (y > ymin) and (not error.boo) then // Wenn der Wert darstellbar und kein Fehler // z.b. 1/x u. x = 0 begin image.canvas.Pen.color := functions[arraycount-1].farbe; image.canvas.LineTo(ut(x),vt(y)); x := x+dx; end else begin x := x +dx; y := f(x); image.canvas.MoveTo(ut(x),vt(y)); end; end; end; end; |
Re: Funktionsplotter
Ohne Parsercode kann ich dir da leider nicht helfen.
Aber: den Parser als Funktion zu implementieren ist Schwachfug ;) Bastel dir eine Klasse außenrum, die die Formel einmal in einen Bytecode kompiliert. Dann musst du nachher nur noch die Parameter und den Bytecode in einen Interpreter werfen, dann wirds wieder. |
Re: Funktionsplotter
Zitat:
|
Re: Funktionsplotter
Wenn du einen Parser schreibst, zerlegst du die reingeworfene Formel ja erst mal. Sagen wir, du willst "1 + 2.3" parsen. Dann würdest du das in drei Teile zerlegen: "1", "+" und "2.3". Da sich der Rechner darunter noch nicht vorstellen kann, musst du dem Rechner beibringen, was er sich darunter vorzustellen hat. Normalerweise macht man so etwas mit Opcodes. Du könntest einen Opcode für die Addition einführen, der zwei Parameter nimmt. Da man Matheparser oder Parser allgemein gerne als Stackmaschinen implementiert, nimmt sich der Opcode eben die zwei Parameter vom Stack.
Dazu musst du noch einen Schritt vorschalten: die Formel so umbasteln, das man sie sinnvoll auf einen Stack pushen kann; also: das der Rechner den Add-Opcode ohne Probleme anwenden kann. Da die Parameter des Opcodes vor der Operation auf dem Stack sein müssen, müsste dein Stack vor der Addition so aussehen:
Code:
Am weitesten unten bedeutet als erstes vom Stack geholt, wohlgemerkt ;)
1
2.3 Der Additionsopcode schnappt sich die beiden (popt sie vom Stack), addiert sie und pusht das Ergebnis zurück. Der Stack sieht dann so aus:
Code:
Werden wir mal ein wenig komplizierter: "1 + 2 * 3"
3.3
Der Parser zerlegt das Ding wieder in Atome (Konstanten und Operatoren), die du am Ende in die sogenannte UPN(umgekehrte polnische Notation)-Form umwandeln kannst. Das läuft nach Operatorvorrang ab. Der *-Operator ist "wichtiger" als der +-Operator und darf daher zuerst an den Stack. Seine Parameter "2" und "3" müssen aber trotzdem auf dem Stack sein. Deshalb sieht dein Zahlenstack so aus:
Code:
Der *-Operator nimmt sich also die beiden letzten von Stack, multipliziert und schreibt zurück. Dann addiert der Add-Operator den Rest.
1
2 3 Sprich: es gibt nicht nur den Numeralstack für Konstanten, Variable und Ergebnisse, sondern auch einen Opstack mit den Operatoren. Der sähe im letzten Beispiel so aus:
Code:
Weiter unten bedeutet wieder: als erstes dran.
+
* Die Urpsrungsform der Stacks kann man getrost als Bytecode des Parsers bezeichnen. Wenn du dir diesen Bytecode speicherst und den später nur noch durch die Auswertungsmethode des Parsers rennen lässt, ist der Performanceunterschied zu deinem jetzigen Parser gigantisch. Wenn du dich nicht scheust, dir etwas anzusehen, was es schon gibt, und daraus zu lernen, dann sieh dir unbedingt den ![]() ![]() ![]() |
Re: Funktionsplotter
Ganz andere Methode:
Funktion in ne DLL einkompilieren und dann dynamisch linken. |
Re: Funktionsplotter
Zitat:
|
Re: Funktionsplotter
Ich bin gerade dabei den erstgenannten Parser zu studieren. :D. Dabei ist mir aber aufgefallen, dass dieser genaus funktioniert wie meiner. Nur dass Das Ergebnis nicht gleich ausgerechnet wird, wie du glaub ich versucht hast zu erklären. Ich werd mal versuchen meinen so umzubauen. Vllt krieg ichs ja hin :)
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:55 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz