Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Funktionsplotter (https://www.delphipraxis.net/77753-funktionsplotter.html)

furby 24. Sep 2006 13:49


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;

Dax 24. Sep 2006 13:56

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.

furby 24. Sep 2006 13:59

Re: Funktionsplotter
 
Zitat:

Aber: den Parser als Funktion zu implementieren ist Schwachfug Wink 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.
hehe tut mir leid... Aber ich kann mir darunter nichts vorstellen. Besser gesagt ich habe noch nie etwas dergleichen gemacht. Wäre nett wenn du mir das nochmal irgendwie für Neulinge näher erläutern könntest.

Dax 24. Sep 2006 14:12

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:
1
2.3
Am weitesten unten bedeutet als erstes vom Stack geholt, wohlgemerkt ;)

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:
3.3
Werden wir mal ein wenig komplizierter: "1 + 2 * 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:
1
2
3
Der *-Operator nimmt sich also die beiden letzten von Stack, multipliziert und schreibt zurück. Dann addiert der Add-Operator den Rest.

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 denHier im Forum suchenCQParser und vielleicht noch den Hier im Forum suchenECQP oder sogar Hier im Forum suchenHAM an. Letzteres würde ich dir als Neuling allerdings nicht so ganz ans Herz legen, denn der ist schon wirklich komplizierter ;)

jakobwenzel 24. Sep 2006 14:57

Re: Funktionsplotter
 
Ganz andere Methode:
Funktion in ne DLL einkompilieren und dann dynamisch linken.

Dax 24. Sep 2006 15:00

Re: Funktionsplotter
 
Zitat:

Zitat von jakobwenzel
Ganz andere Methode:
Funktion in ne DLL einkompilieren und dann dynamisch linken.

Ginge. Aber dazu müsste man den Delphi-Compiler mitliefern (Lizenztechnisch nicht möglich), oder den FreePascal-Compiler.

furby 24. Sep 2006 15:05

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 22:17 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz