Ausgewählte Artikel zur Programmierung mit Delphi

Werner Voigt

 für 


Schnell berechnet

Autor: Dipl.-Math. Werner Voigt

veröffentlicht in Toolbox
Copyright 2002 © Werner Voigt und Toolbox
Bitte beachten Sie auch die Hinweise zur kommerziellen Nutzung

Problem:

Ohne Neuübersetzung sollen Formeln in einem Delphiprogramm zur Laufzeit ausgetauscht werden können.

Lösung:

Angeregt durch die Leseranfrage von Herrn Skirl in Toolbox 6/2002 habe ich in meinen alten Quelltexten gestöbert. Da die Formel nicht hart codiert werden soll und die Herkunft der zu verrechnenden Daten außerhalb unserer Lösung liegen soll, ist das Ziel ein dazu passender Formelinterpreter.

Ausgangspunkt meiner Realisierungsgedanken war, das Fahrrad nicht ganz neu zu erfinden. Ich hatte 1987/88 für die Humboldt-Universität einen speziellen Datenbankpool unter Turbo-Pascal 2.0 erstellt. Darin war auch ein Abschnitt enthalten, mit dem die Anwender zu Analysezwecken eigenständig Daten selektieren und verrechnen konnten. Dazu gehörte auch, frei wählbare "private" Formeln auf die Daten in der Datenbank anwenden zu können. Schon damals kam eine gute Unterstützung von Borland. Es gab ein Programm von Borland namens MicroCalc, dessen Zweck in der Demonstration der Leistungsfähigkeit von Turbo-Pascal bestand.
Da eine Tabellenkalkulation von Haus aus Formeln vorgesetzt bekommt und die Daten durch Bezüge auf spezielle Variablen beschafft werden, bietet der Quelltext von MicroCalc eine gute Know-How-Quelle. Im Borland-Museum steht das File tp302.zip zum Download bereit. Darin ist unter anderem auch die Quelltextdatei Calc.inc enthalten. Uns interessiert darin der Modul 4.

Aber natürlich hat sich die Programmierung seit der Entstehung von MicroCalc weiterentwickelt. MicroCalc war ursprüglich für 8-Bit-PC's unter CPM konzipiert. Deshalb ergaben sich mit der Umstellung auf objektorientierte Programmierung, der Anpassung an veränderte Technik, andere Typen und Gedanken zur funktionalen Erweiterung massive Änderungen. So entstand die unit XFormula.pas.

Ziel der Arbeit war es, auf einfache Weise eine möglichst flexible Unterstützung für erst zur Laufzeit des Programms vorliegende Formeln zu schaffen. Dabei sollten auch Daten aus verschiedensten Datenquellen als Variable ausgewertet werden. Damit wird es möglich, leicht erweiterbare Formelsammlungen zu programmieren oder für einen bestimmten Sachverhalt die Auswirkung irgendwelcher Parameteränderungen wie bei einer Tabellenkalkulation live zu studieren.

Die Formelinterpretation soll dem Syntaxdiagramm entsprechend realisiert werden.

 
    

Im Syntaxdiagramm fehlende Definitionen sind hier erklärt.
Als Standardfunction werden gegenwärtig die Zeichenketten 'SQRT', 'SQR', 'SIN', 'LOG', 'LN', 'EXP', 'COS', 'ARCTAN' und 'ABS' erkannt. Die fallende Sortierung verhindert Erkennungsfehler. Man könnte natürlich auch die Liste wie üblich steigend sortieren und bei der Erkennung rückwärts durch die Liste marschieren.

 Beispiele:

sqr(sin(pi/4))
(1+2)*(3-4)
ln(myVar1/myVar2)
    

Die Realisierung

Das Objekt TXFormula wird direkt von TObject abgeleitet. Eine Nutzung durch Einbindung einer Unit ist einfacher unter verschiedenen Delphi-Versionen, als eine nicht-visuelle Komponente. Gegenwärtig enthält XFormula nichts, was die Einstellung via Objektinspektor sinnvoll macht. Das kann sich bei der weiteren Qualifizierung von XFormula natürlich noch ändern. Es gibt für den Kontakt mit der Außenwelt folgende Methoden und Eigenschaften:

1. Methoden
procedure SetFormula(const aFormula: string);
Setzt eine Formel für nachfolgende Berechnungen.

function Calc(var aResult:NumberType): boolean;
Berechnet den Wert der zuletzt gesetzten Formel und liefert bei Erfolg true.

function IsFunction(Name: string): Standardfunction;
Liefert den Index in der Liste der Standardfunktionsnamen oder 0.

function CalcFormula(const aFormula: string; var aResult:NumberType): boolean;
Ruft nacheinander SetFormula und Calc auf und liefert das Ergebnis von Calc.

function ErrorMsg: string;
Liefert im Fehlerfall einen Fehlermeldungstext.
    
2. Eigenschaften
property ErrorText: string
Enthält im Fehlerfall einen Fehlermeldungstext mit ungefährer Ortsangabe

property Formula: string
Die Formel, aber durch XFormula modifiziert

property Variables: TStringList
Eine Liste der erkannten Variablennamen

property OnGetVariable: TGetVariable 
Anknüpfungspunkt für eine benutzerdefinierte Routine, die zu einem 
Variablennamen den aktuellen Wert an XFormula liefert.
    
3. Typen

Als Vorbereitung auf weitere Änderungen ist die Typfestlegung für alle Ergebnisse/Zwischenergebnisse mit

NumberType = double;
    

zu sehen. Mit

 TGetVariable = function(Varname: string; var f: NumberType):boolean; of object;
    

wird der Typ für die Eigenschaft OnGetVariable festgelegt.

Das Innenleben

Vorab werden in der Formel vorhandenen Buchstaben mit UpCase umgewandelt. Das vereinfacht die Erkennung der Bezeichner der Standardfunktionen, Konstanten und Variablen. Obwohl als String deklariert, wird zur einfacheren Arbeit als zusätzliche Endekennung noch #0 angehängt. Dies dient nur der einfacheren Programmierung einiger Details, um zusätzliche Endeabfragen zu vermeiden.

Die Interpretation der Formel erfolgt durch eine Gruppe privater Methoden, deren Namen mit dem Syntaxdiagramm korrespondieren. Fundament der Arbeitsweise ist ausgiebige Nutzung rekursiver Aufrufe dieser Methoden. Durch diese Rekursionen werden Teile der Formel zeitweilig je nach Rekursionstiefe zur Seite gelegt, um die Rangfolge der Operationen entsprechend den üblichen Regeln zu realisieren. Wird bei der Interpretation ein Bezeichner entdeckt, so sind drei Fälle zu betrachten. Es kann sich um den Namen einer fest eingebauten Standardfunktion handeln, oder um den Bezeichner einer Konstanten. Momentan ist nur die Konstante "PI" programmiert. Im dritten Fall wird der fragliche Bezeichner in einer Zeichenkette an eine externe Ereignisbehandlungsroutine übergeben, um von dieser Routine den zugehörigen Argumentwert abzufordern.

Da die Formeln erst zur Laufzeit des Programms zur Verfügung stehen, müssen syntaktische Fehler ebenso wie "echte" Laufzeitfehler von XFormula an das Programm gemeldet werden. Dazu muß bei der Interpretation entsprechend vorsichtig vorgegangen werden. Deshalb wird in einer späteren Version sicherlich eine separate Syntaxprüfung vorhanden sein müssen. Auftretende Fehler (sowohl syntaktische Fehler, wie auch Laufzeitfehler) werden mehr oder weniger qualifiziert angezeigt.

Zur Umwandlung einer in der Formel enthaltenen Zahl wird die Funktion Val benutzt. Daraus ergeben sich ein paar kleine Probleme. "Val" verlangt als Dezimalzeichen einen Punkt. Unter Windows dient dazu das unter dem Namen Decimalseparator vorliegende Zeichen. Gegenwärtig wird vor dem Aufruf von Val einfach Decimalseparator durch einen Punkt ersetzt. Allerdings könnte jemand zusätzlich auch noch Thousandseparator verwenden...

Die praktische Benutzung

In das jeweilige Programm wird die Unit XFormula.pas per uses-Anweisung eingebunden. Dann ist nur noch vor der ersten Benutzung eine Formel-Objektinstanz zu erzeugen. An diese wird dann die jeweilige Formel via SetFormula als Zeichenkette übergeben. Nachfolgende Aufrufe von Calc liefern dann im Team mit den aktuellen Variablenwerten das Ergebnis der Formelberechnung.

Durch die Datenübergabe via OnGetVariable ist XFormula recht flexibel einsetzbar. Damit wird es egal, ob die Variablenwerte aus Edit-Feldern kommen (Formelsammlung o.ä.) oder einer Datenbank entstammen.

Mögliche Erweiterungen:

Im Beispielprogramm können Sie erste Bekanntschaft mit XFormula machen. Es gibt ein Editfeld, das eine Formel aufnehmen kann. Dazu ein Feld zur Ergebnisanzeige. Der Button MakeEdits dient zur Übergabe der Formel vom Editfeld an Xformula. Falls die Formel Variablennamen enthält, werden Editfelder mit einem passenden Label erzeugt. Nach der Eingabe von numerischen Werten wird mit dem Button Calc die eigentliche Berechnung gestartet. Experimentieren Sie einfach mit verschiedenen Formeln.

Die unit XFormula wurde mit Delphi 4 und 5 getestet. Nach meiner Auffassung dürfte sie ohne Änderung auch mit anderen Versionen lauffähig sein.

Quellenhinweise:
TP 3.02 und MicroCalc findet sich als ein Zipfile (167k) unter
http://community.borland.com/article/0,1410,20792,00.html

Beschreibungen weiterer evtl. geeigneter Formel-Interpreter/Compiler gibt es unter http://homepages.borland.com/efg2lab/Library/Delphi/MathFunctions/Parsers.htm

zurückzurück
Copyright © 2003 by  EDVoigt  (Werner Voigt).