Oft findet man in Computerzeitschriften und -büchern Programme in allen möglichen Sprachen, die Berechnungen oder graphische Darstellungen mit mathematischen Funktionen durchführen. Meist sind jedoch die Formeln fest im Programm „eingebaut“, und Änderungen können zum Teil mit erheblichem Aufwand verbunden sein, insbesondere wenn man mit Compilern arbeitet.
Wer mit einem BASIC-Interpreter herkömmlicher Art arbeitet, der über die Befehle CHAIN und MERGE verfügt, kann sich mit einem Trick aus der Patsche helfen. Wie in Listing 1 gezeigt ist es möglich, über den Umweg einer Diskettendatei ein sich selbst veränderndes Programm zu schreiben.
Programmiert man jedoch in einer Compilersprache oder z. B. mit dem enorm leistungsfähigen GfA-BASIC-Interpreter, kommt man mit solchen Kniffen nicht mehr weiter.
Hier hilft alles nichts, man muß sich sozusagen zu Fuß durch eine als Zeichenkette eingegebene Formel „beißen“ und Zeichen für Zeichen analysieren.
So aufwendig, wie das auf den ersten Blick scheinen mag, ist dies nun auch wieder nicht; das Zauberwort, das hier weiterhilft heißt Rekursion.
Schauen wir uns doch einmal eine Formel - einen arithmetischen Ausdruck wie die Mathematiker sagen - genauer an. Zur graphischen Verdeutlichung sollen hier sogenannte Syntax-Diagramme, ähnlich wie man sie zur Definition von PASCAL-Befehlen verwendet, weiterhelfen. Bild 1 erklärt die möglichen Symbole. Syntax-Diagramme werden in Pfeilrichtung auf Flußlinien durchlaufen; diese können sich an verschiedenen Stellen aufspalten und so Möglichkeiten zur Verzweigung und Schleifenbildung beschreiben. Die Kästchen mit den abgerundeten Ecken dienen nur der Namensgebung des jeweiligen Diagramms und können einfach übergangen werden.
Unsere Formel kann nun als Kette von Zeichen, die sich in bestimmte Teilketten zerlegen läßt, mit diesem Schema formal beschrieben werden.
So, wie man den arithmetischen Ausdruck von links nach rechts zu Papier bringt, kann man den Flußlinien folgen und, gemäß der mathematischen Hierarchie der Rechenregeln, von einer Grobdarstellung zu immer weiteren Unterteilungen gelangen.
Wohlgemerkt, Syntax-Diagramme sind weder Flußdiagramme noch dienen sie zur exakten Darstellung eines Programmablaufs, jedoch lassen sich die durch sie beschriebenen Regeln leicht in entsprechende Programm-Algorithmen umsetzen.
In Bild 2 ist nun allgemein ein arithmetischer Ausdruck dargestellt, der aus beliebig vielen einfachen Ausdrücken besteht, die über das Plus- oder Minuszeichen verknüpft sein können. Dies ist durch die Abzweigung hinter dem Käsen „Einfacher Ausdruck“ verdeutlicht, was bedeutet, daß hier abgebogen wird solange einem Teilausdruck ein Plus- oder Minusoperator folgt.
Beispiel:
6.89 + (3/7.8-5)*5 - sin(exp(-3*7/8)) + ...
6.89, (3/7.8 - 5)5, sin(exp(-37/8)) sind Teilausdrücke des o. g. arithmetischen Audrucks
Jeder einfache Ausdruck setzt sich aus einzelnen Termen zusammen, die über Multiplikation oder Division verknüpft sind (Bild 3).
Ein Term ist ein vorzeichenbehafteter Faktor, der gegebenenfalls wiederum mit einem vorzeichenbehafteten Faktor potenziert sein kann (Bild 4).
Ein vorzeichenbehafteter Faktor besteht aus einem vorzeichenlosen (positiven) Faktor mit eventuell vorangestelltem negativem Vorzeichen (Bild 5).
Wie in Bild 6 zu sehen, ist nun ein Faktor entweder eine positive reelle Zahl (die sich aus Ziffern, eventuell Dezimalpunkt und/oder Exponent zusammensetzt), ein in Klammern stehender arithmetischer Ausdruck, eine Variable (hier als „x“ bezeichnet) oder eine mathematische Funktion, deren Argument selbst wieder ein Faktor ist.
Listing 2 zeigt ein GfA-BASIC Programm, welches das Modul FORMEL. MRG enthält. Hier wird gezeigt, wie sich konsequent oben genannte Regeln ine ine Hochsprache umsetzen lassen. GfA-BASIC ist hierzu ebenso geeignet wie PASCAL oder C. Eine Umsetzung des Moduls in eine dieser Sprachen sollte daher keine Probleme bereiten. Bei FORMEL.MRG handelt es sich um ein universell benutzbares Teilprogramm, das leicht an unterschiedliche Anwendungen angepaßt werden kann.
Das eigentliche Hauptprogramm mit der Procedure Def_funktion dient nur der Demonstration von FORMEL.MRG. Das hier vorgestellte Modul kann sehr vielseitig verwendet werden, z. B. für einen selbstprogrammierten Taschenrechner, ein Funktionsplotprogramm oder die Analyse von mathematischen Funktionen. Erweiterungen mit beliebig komplexen Funktionen sind leicht durchzuführen, wie im Programmtext an entsprechender Stelle kommentiert wurde.
Ich hoffe, FORMEL.MRG gibt viele Anregungen für eigene Ideen und wünsche viel Spaß beim Programmieren.
(H. Bauch)
'****************************************************
'* *
'* --- ST_FORM.BAS --- *
'* Beispiel zur Berechnung beliebiger Formeln *
'* mit dem ST-BASIC-Interpreter *
'* *
'* (c) 22.11.1986 H. Bauch *
'* *
'****************************************************
Clearw 2:Fullw 2
Print
Print " F O R M E L B E R E C H N U N G "
Print " ---------------------------------"
Print
gosub Funktion.def
'
Anfang:
Input " x = ",xS
If x$<>"N" and x$<>"n" Then
Print " f(x) = ";fny(val(x$)),"<N> für neue Funktion !"
Else Gosub Funktion.def
Goto Anfang
'
'
Funktion.def:
Print
Print " Definition einer beliebigen Funktion in BASIC-Syntax"
Print
Input " f(x) = ",Formel$
Open "O",#1,"FKT.BAS"
Print#1,"200 Def fny(x) = "+Formel$
Close #1
Chain Merge "FKT.BAS",200
' Die nächste Zeile wird vom Programm verändert:
Def fny(x) = 1/(sqr(1-x^2))
Return
' ***********************************************************
' * *
' * -- FORMULA.BAS -- *
' * Ein Testprogramm für das Modul FORMEL.MRG *
' * *
' * (c) 23.11.1936 H. Bauch *
' * Taunusstr. 96 *
' * 6237 Liederbach *
' * *
' ***********************************************************
'
' --- Konscantendefinitionen
'
True:=-1
False!=0
'
'
' ----------------------------------------------------------------
' ---------- Beginn des Hauptprogramms zum -----------------------
' ---------- Testen des Moduls FORMEL.MRG: -----------------------
' ----------------------------------------------------------------
'
Cls
Print
Print
Print " F O R M E L B E R E C H N U N G "
Print " ---------------------------------"
Print
Print " (c) 1986 H. Bauch"
Print
First!=True!$
@Def_funktion
'
Do
Input " x = ",X$
If Val?(X$)<>0 X=Val(X$)
@Berechnung(Formel$,X)
Print " f(x) = ";Berechnung,
Print "Für neue Funkion <N> eingeben !"
Print
Else
If Upper$(X$)="N"
@Def_funktion
Endif
Endif
Loop
'
' -------------------------------------------------------
' ------------- Ende des Hauptprogramms -----------------
' -------------------------------------------------------
'
'
Procedure Def_funktion
'
' ----------------------------------------------------------------
' Dieses Unterprogramm dient nur zu Testzwecken; es ermöglicht
' erstmaliges und wiederholtes Definieren einer Formel/Funktion in
' Form eines Strings. Funktionen können auch konstant sein, d. h.
' es kommt keine Variable (hier "x") in ihr vor.
' ----------------------------------------------------------------
'
' Globale Variable: True!,False!,First!,F_max%,Std_funktion$(),
' Formel$, Fehler_positon%
Local I%,Xtest,Fehleraus$
'
If First!
@Init_berechnung
First!=False!
Print
Print " Definition einer beliebiger. Funktion in BASIC-Syntax"
Print
Print " Standardfunktionen: "
Print
Print " / ";
For I%=1 To F_max%
Print Std_funktion$(1%);" / ";
Next I%
Endif
Print
Print
Repeat
Input " f(x) = ",Formel$
' -----------------------------------------------------------------
' An dieser Stelle wird die @Berechnung() zunächst mit einem Test-
' wert aufgerufen; in einigen Fällen kann es bei dem hier gewählten
' Wert zum Fehlerabbruch kommen, weil sich z. B- Division durch
' Null oder Wurzel aus negativer Zahl ergibt.
' Ein einfacher Syntaxfehler wird so jedoch aufgedeckt.
' -----------------------------------------------------------------
Xtest=0.0123
@Berechnung(Formel$,Xtest)
If (Fehler_position%<>0)
Fehleraus$="Fehler in der Formel!an Position "+Str$(Fehler_position%)
Alert 1,Fehleraus$,1," Weiter ", Dummy
Endif
Until (Fehler_position%=0)
Return
'
'
' ***********************************************************
' * *
' * --- FORMEL.MRG--- *
' * Berechnung einer beliebigen Funktion als String *
' * *
' ***********************************************************
'
Procedure Init_berechnung
'
' ------------------------------------------------------------------
' Dieses Unterprogramm dient zur Initialisierung der benutzbaren
' Standardfunktionen für die Procedure Faktor; Erweiterungen können
' dort leicht eingefügt werden, Funktionsnamen müssen in der DATA-
' Zeile ergänzt werden und F_max% muß angepaßt werden.
' Init_berechnung muß stets vor der Procedure Berechnung aufgerufen
' werden.
' ------------------------------------------------------------------
'
' Globale Variable: F_max%,
' Std_funktion$()
Local I%
'
' Anzahl z.Zt. implementierter Standardfunktionen:
F_max%=9
'
' Standardfunktionen festlegen
Dim Std_funktion$(F_max%)
Data ABS,SQR,SIN,COS,TAN,ATN,LN,LOG,EXP
Restore
For I%=1 To F_max%
Read Std_funktion$(I%)
Next I%
Return
'
'
Procedure Berechnung(Funktion$,X)
'
' ------------------------------------------------------------------
' Dies ist die Hauptroutine des Moduls FORMEL.MRG; nach Aufruf von
' von Init_berechnung wird stets hier eingesprungen. In einem Haupt-
' programm können verschiedene Formeln benötigt werden; diese werden
' einfach jeweils neu durch den Stringparameter Funktion$ übergeben.
' Z. Zt. ist nur eine Variable (X) in der Procedure Faktor vorge-
' sehen, Erweiterungen auf beliebig viele Variable sind möglich
' durch Ergänzung der Parameterliste und Erweiterung der Procedure
' Faktor an der dort bezeichneten Stelle.
'
' Sollte in Funktion$ ein Syntax-Fehler gefunden werden, so wird
' Fehler position% als Global zurückgegeben. '
'
' Da GfA-BASIC keine Function im Sinne von PASCAL besitzt, werden die
' Ergebnisse einer Procedure als Variable mit dem Namen der Proce-
' dure zurückgegeben.
' ------------------------------------------------------------------
'
' Globale Variable: True!, False!, F_max%, Berechnung, Fehler_position%
' Std_funktion$(), Berechnung
'
Local Zeil_ende$,Position,Zei$,Ausdruck
'
Zeil_ende$=Chr$(0)
Position%=0
@N_zeichen
@Ausdruck
Berechnung=Ausdruck
If Zei$=Zeil_ende$
Fehler_position%=0
Else
Fehler_position%=Position%
Endif
Return
'
'
Procedure Ausdruck
'
Local E,Einf_ausdruck,Operator$
'
@Einf_ausdruck
E=Einf_ausdruck
While Zei$="+" Or Zei$="-"
Operator$=Zei$
@N_zeichen
If Operator$="+"
@Einf_ausdruck
E=E+Einf_ausdruck
Endif
If Operator$="-"
@Einf_ausdruck
E=E-Einf_ausdruck
Endif
Wend
Ausdruck=E
Return
'
'
Procedure Einf_ausdruck
'
Local S,Term,Operator$
'
@Term
S=Term
While Zei$="*" Or Zei$="/"
Operator$=Zei$
@N_zeichen
If Operator$="*"
@Term
S=S*Term
Endif
If Operator$="/"
@Term
S=S/Term
Endif
Wend
Einf_ausdruck=S
Return
'
'
Procedure Term
'
Local T,Vorz_faktor
@Vorz_faktor
T=Vorz_faktor
While Zei$="*"
@N_zeichen
@Vorz_faktor
T=T^Vorz_faktor
Wend
Term=T
Return
'
'
Procedure Vorz_faktor
'
Local Faktor
'
If Zei$="-"
@N_zeichen
@Faktor
Vorz_faktor=-Faktor
Else
@Faktor
Vorz_faktor=Faktor
Endif
Return
'
'
Procedure Faktor
'
Local F,I%,L%,Beginn%
'
If Zei$>="0" And Zei$<="9"
' --------------------------------------------------------
' "Zusammenbasteln" einer Zahlen-Konstante
' --------------------------------------------------------
Beginn%=Position%
Repeat
@N_zeichen
Until Not (Zei$>-"0" And Zei$<="9")
If Zei$="."
Repeat
@N_zeichen
Until Not (Zei$>="0" And Zei$<="9")
Endif
If Upper$(Zei$)="E"
@N_zeichen
Repeat
@N_zeichen
Until Not (Zei$>="0" And Zei$<="9")
Endif
F=Val(Mid$(Funktion$,Beginn%,Position%-Beginn%))
Else
If Zei$="("
' ----------------------------------------------------
' Faktor ist ein geklammerter Ausdruck, d.h.
' rekursiver Aufruf der Procedure Ausdruck
' ----------------------------------------------------
@N_zeichen
@Ausdruck
F=Ausdruck
If Zei$=")"
@N_zeichen
Else
Fehler_position%=Position%
Endif
Else
If Upper$(Zei$)="X"
' -----------------------------------------------------
' Variable der Formel gefunden!
' Hier können Erweiterungen für beliebig viele Variable
' "eingebaut" werden; siehe auch Kommentar zu Beginn der
' Hauptroutine Procedure Berechnung().
' -----------------------------------------------------
@N_zeichen
F=X
Else
' -----------------------------------------------------------
' Suche nach Standardfunktions-Namen wie bei
' Procedure Init_Berechnung in der Data-Zeile vereinbart!
' Hier können noch beliebig viele Funktionen ergänzt werden
' (z.B. arcsin, arccos, Fakultit, hyperbol. Funktion usw.).
' -----------------------------------------------------------
Erfolg!=False!
For I%=1 To F_max%
If Erfolg!=False!
L%=Len(Std_funktion$(1%))
If Upper$(Mid$(Funktion$,Position%,L%))=Std_funktion$(1%)
Position%=Position%+L%-1
@N_zeichen
@Faktor
F=Faktor
If I%=1
F=Abs(F)
Endif
If I%=2
F=Sqr(F)
Endif
If I%=3
F=Sin(F)
Endif
If I%=4
F=Cos(F)
Endif
If I%=5
F=Tan(F)
Endif
If I%=6
F=Atn(F)
Endif
If I%=7
F=Log(F)
Endif
If I%=8
F=Log10(F)
Endif
If I%=9
F=Exp(F)
Endif
Erfolg!=True!
Endif
Endif
Next I%
If Not Erfolg!
Fehler_position%*=Position%
Endif
Endif
Endif
'
Endif
Faktor=F
Return
'
'
Procedure N_zeichen
'
Repeat
Inc Position%
If Position%<=Len(Funktion$)
Zei$=Mid$(Funktion$,Position%,1)
Else
Zei$=Zeil_ende$
Endif
Until Zei$<>" "
Return
'
'
' -----------------------------------------------------------
' ------------- Ende des Moduls FORMEL.MRG ------------------
' -----------------------------------------------------------