home *** CD-ROM | disk | FTP | other *** search
- Text aus PASCAL- Ausgabe 8/87
-
-
- CALC - ein mathematischer Compiler
- von Karsten Gieselmann & Michael Ceol
-
- Beim Umgang mit Programmen, welche benutzerdefinierte Funktionen
- bearbeiten - als Paradebeispiele hierfür betrachte man die
- "Kurvendiskussion" (PASCAL 11/86) und die "Drei-dimensionale Funktions-
- Darstellung" (PASCAL 2/87) - ist es meist sehr lästig, wenn bei Wahl
- einer anderen Funktion ständig der Compiler bemüht werden muß, um den
- um die neue Funktion bereicherten Quelltext zum x-ten Mal zu
- übersetzen.
-
- Bei kürzeren Programmen hat man da schon mal ein Nachsehen mit seinem
- "Rechenknecht". Im Regelfall jedoch sitzt man vor der "Kiste" und
- wartet und wartet.... Das muss aber nicht sein - denn was eine
- Programmier-Sprache schafft, die Übersetzung einer Zeichenfolge in eine
- für den Rechner verständliche Form, sollte auch für uns durch ein
- selbstgeschriebenes Programm machbar sein!
-
- Im Folgenden wird nun eine Pascal-Bibliothek vorgestellt, die dieses
- Problem in recht effizienter Art und Weise löst. Zu langsam ? Manchem
- dämmert es vielleicht noch aus seiner BASIC-Zeit: Irgendwie bekommt man
- seine Funktion schon ins Programm gePEEKed und gePOKEd - wie war das
- noch gleich?
-
- Ja, richtig, diesen Gedanken kann man in Pascal und verwandten Sprachen
- getrost wieder vergessen, denn selbige sind Compiler-Sprachen! In
- diesen kann das lauffähige Programm (auch Object-Code genannt) während
- der Laufzeit höchsten auf Maschinencode-Ebene manipuliert werden (dies
- ist aber auch nicht gerade die "feine englische Art" und äußerst (!)
- kompliziert).
-
- Also bleibt als einzige Möglichkeit, sich einen eigenen "Auswerter" für
- solche Formeln, sprich numerische Ausdrücke (engl.: Expression, wird
- Ihnen im Programmtext noch häufiger begegnen), zu basteln.
-
- Lieber nicht, werden jetzt vielleicht einige von Ihnen sagen, da wird
- mein Rechner ja vom Formel-1-Wagen zum Dreirad degradiert! Nein, da
- nehme ich doch die längeren Übersetzungszeiten des Pascal-Compilers bei
- jedem neuen Ausdruck in Kauf.
-
- Aber weit gefehlt: Mit dem geeigneten Konzept kann man zu einer Lösung
- gelangen, die (bis auf einige Sonderfälle) mit ihren Auswertungen in
- der Geschwindigkeit durchschnittlich nur um etwa 15% hinter dem vom
- Pascal-Compiler erzeugten Maschinencode "hinterherhinkt". Die hier
- vorgestellte Pascal-Bibliothek "CALC" (in Turbo Pascal geschrieben,
- verwendet allerdings nur Standard-Aufrufe!) wertet als Zeichenketten
- (String) vorliegende numerische Ausdrücke aus und liefert - bei
- korrekter Eingabe gemäß Pascal-Syntax (-Grammatik) - den entsprechenden
- Wert des Ausdrucks. Je nach Bedarf können dabei nur eine oder mehrere
- Variablen im Ausdruck verwendet werden!
-
- Dies reicht z.B. für die Funktionswertberechnung in einer
- (eindimensionalen) Kurvendiskussion aus. Wer noch höher hinaus will -
- als 3-D-Grafiker ist man dazu gezwungen - kann das Programm leicht an
- seine Bedürfnisse anpassen bzw. erweitern. Hierauf wurde bei der
- Entwicklung allergrößter Wert gelegt.
-
- Diesem Anliegen Rechnung tragen außerdem die nun folgende detaillierte
- Beschreibung der Funktion und Wirkungsweise der einzelnen Programmteile
- (siehe auch die Kommentare in den Listings!) sowie die Vorstellung der
- beim Programmieren verwendeten Methode -einem wichtigen Hilfsmittel aus
- der Theoretischen Informatik zur Programmentwicklung.
-
- Mit diesen Grundlagen ist ein Typenwechsel (z.B. von REAL auf COMPLEX)
- des "Rechners", eine Funktionserweiterung oder z.B. das "Aufsetzen"
- einer übergeordneten "Programmsteuerung" kein Problem.
-
- Mehr hierzu jedoch später, zunächst wollen wir uns einigen
- Vorüberlegungen widmen.Zu Anfang stellt sich natürlich die Frage, ob
- unser Programm Interpreter-oder Compiler-artig arbeiten soll, also ob
- zur Berechnung eines Wertes jedesmal der gesamte Ausdruck in seiner
- natürlichen (Eingabe-) Form abgearbeitet wird, oder ob eine Art
- Vorübersetzung in eine für den Rechner leichter verständliche Struktur
- erfolgt, die anschließend bei jeder Berechnung nur noch ausgewertet
- wird.
-
- Nun, im Hinblick auf den Verwendungszweck der Bibliothek
- (rechenintensive Programme wie "Kurvendiskussion" oder "3-D-Plotter")
- sollte man nicht mit ein paar Bytes knausern und seinen Programmen im
- Hinblick auf kürzestmögliche Rechenzeit einen Ausdruck-Compiler
- spendieren. Alles in allem verschenkt man hierbei sowieso nicht viel
- Speicherplatz, da der Aufwand zur Auswertung der Zeichenkette bei
- Compiler und Interpreter in etwa gleich groß ist. Der Programmteil des
- Compilers zur Auswertung der vorübersetzten Struktur ist außerdem
- relativ kompakt.
- Arbeiten mit dem CALC.
- Wir wollen unseren "Rechner" so aufbauen, daß seine
- Einsatzmöglichkeiten aus einem anderen Programm heraus so flexibel wie
- möglich sind. Dazu Bedarf es außer dem "Compiler" und "Auswerter" noch
- ein paar anderer "Hilfsmittel", die CALC und das ihn beherbergende
- Programm zur "Verwaltung" der CALC-Daten benötigen. Zu diesen
- Hilfsmitteln bedarf es noch einiger Überlegungen. Was muß CALC
- erfüllen, um vielseitig einsetzbar zu sein? Wie ein richtiger Compiler
- erzeugt der CALC-Compiler aus einem "Programm-Text" einen "Programm
- Code", der dann direkt von einer "Maschine" ausgeführt werden kann
- allerdings beschränkt auf einen numerischen Ausdruck.
-
- Nun kann es ja in einer Anwendung notwendig sein, mehrere Ausdrücke
- "hintereinander" zu verarbeiten, wobei das Ergebnis des einen zur
- Auswertung in den nächsten einfließt. Aus diesem Grund sollte die
- Anzahl der möglichen Ausdrücke und der damit verbundenen "Programme"
- variabel sein: CALC speichert den aus einen Ausdruck erzeugten "Code"
- in einer dynamischen Datenstuktur (s. PASCAL 11/86 u. 1/87:
- "Datenstrukturen mit Zeigern") und teilt dem aufrufenden Programm über
- einen Parameter mit, wo dieses ihn finden kann, um ihn später von der
- "Maschine" abarbeiten zu lassen. So können mehrere übersetzte Ausdrücke
- schnell ausgewertet werden!
-
- Ein solcher Ausdruck kann z.B. konstant sein: (1324-12.75)/50. Er kann
- aber auch eine Variable enthalten: 10*sin(x) - oder auch mehrere:
- a^2+2*a*b+b^2. Nun sollte unsere Lösung nicht die x-te Variation eines
- "Formelauswerters" für nur eine Variable sein, was die
- Einsatzmöglichkeit sehr einschränkt. Vielmehr sollte eine vom
- Programmierer festlegbare Anzahl von Variablen in einem Ausdruck
- erlaubt sein. Aus diesem Grund gehört zu jedem von CALC zu
- verarbeitenden Ausdruck eine " Variablen-Tabelle", in denen die
- Variablen des Ausdrucks mit ihren Bezeichnern (Namen) und aktuellen
- Werten geführt werden.
-
- Damit nun diese von CALC und vom Programmierer komfortabel verwaltet
- werden können, gibt es einige Hilfsfunktionen und -prozeduren, welche
- die Tabellen nach Bedarf erzeugen und löschen bzw. deren Inhalte
- manipulieren. Ebenso gibt es eine Möglichkeit, nicht mehr benötige
- "Programme" wieder zu "löschen", um für neue Platz zu schaffen!
- (s.CALCUTIL.PAS) Übersetzen... Doch wie wird nun ein Ausdruck
- übersetzt? Dafür ist die Prozedur "CompileExpression" zuständig, die
- für ihre Arbeit drei Parameter benötigt und einen Schalter besitzt, der
- ihre Arbeitsweise steuert. Ihr Aufruf gestaltet sich folgendermaßen:
-
- CompileExpression (Expr, VarTable, ExprPtr);
-
- Doch zuerst der Schalter:
-
- Wie bereits erwähnt, sollen mehrere Variablen in einem Ausdruck
- verwendet werden können, wobei ihre Bezeichnung (Namensgebung) frei dem
- Anwender unterliegt. Die einzigste Einschränkung soll sein, daß sie mit
- einem Buchstaben anfangen müssen und nicht mit den "reservierten"
- Bezeichnern der in CALC implementierten Operatoren und Funktionen
- identisch sein dürfen. Wenn der CALC-Compiler bei der Übersetzung nun
- auf eine Zeichenfolge stößt, die nicht mit einem der "reservierten"
- Bezeichner übereinstimmt, soll er annehmen, daß es sich um eine
- Variable handelt.
-
- Seine nächste Aktion ist nun, in der zum Ausdruck gehörenden Variablen-
- Tabelle (s.u.) nachzusehen, ob er den bis jetzt unbekannten Bezeichner
- dort findet - ob es sich um eine in der Tabelle eingetragene Variable
- handelt. Trifft dies zu, ist der Fall mit der Erzeugung des
- entsprechenden Programm-Codes (s.u.) erledigt und er kann mit der
- Übersetzung fortfahren.
-
- Konnte der Bezeichner aber auch nicht anhand der Variablen-Tabelle
- identifiziert werden, so soll der Compiler nun die Möglichkeit haben,
- diesen als neue Variable zu interpretieren, in die Tabelle einzutragen,
- den entsprechenden Code zu erzeugen und - fortzufahren.
-
- Nun ist aber nicht in jedem Fall eine solche Großzügigkeit gefragt ein
- Tippfehler, und schon hat man wie bei BASIC eine neue Variable, die
- Ungereimtheiten aufkommen lassen kann. Hier tritt nun der Schalter in
- Kraft: Die globale boole'sche Variable "CalcDecMod" steuert die
- Verarbeitung der unbekannten Bezeichner. Wie bei Pascal kann man
- verlangen, das die zu verwendenten Variablen im Ausdruck vorher
- vereinbart (deklariert) werden: "CalcDecMod" = TRUE.
-
- Wie geschieht diese Vereinbarung? Bevor ein Ausdruck zur Übersetzung
- vorgelegt wird, muß mittels der Hilfsfunktion "NewVarTab" eine
- Variablen-Tabelle erzeugt werden (falls nicht schon vorhanden) und mit
- "AddToVarTab" der oder (die) gewünschte(n) Variablen-Name(n)
- (in Gorßbuchstaben!) in diese Tabelle eingetragen werden. Ist
- "CalcDecMod" = TRUE, so wird "CompileExpression" nur solche Variablen
- akzeptieren, die schon in der Tabelle eingetragen sind, andernfalls
- wird mit einer entsprechenden Fehlermeldung abgebrochen!
-
- Diesen Modus benutzt auch das Demo-Programm, um nur die Variable "x" im
- auszuwertenden Funktionsausdruck zuzulassen.
-
- Nun zu den Parametern:
-
- Der erste, "Expr", enthält den zu übersetzenden Ausdruck als
- Zeichenkette (String) - z.B. "sin(x);". Die Syntax (Schreibweise)
- entspricht dabei den Pascal-Regeln, wobei das Semikolon das Ende des
- Ausdrucks signalisiert - nachfolgender Text im String wird ignoeriert.
-
- Der zweite, "VarTable" - ein Zeiger (Pointer), zeigt auf die für den
- Ausdruck zu verwendende Variablen-Tabelle. Hier bestehen nun zwei
- Möglichkeiten: Es wird eine schon existierende Tabelle, mit oder ohne
- eingetragenen Variablen, angegeben (VarTable <> NIL, s.a. CalcDecMod).
- Andernfalls erzeugt der Compiler eine neue, leere Tabelle (VarTable =
- NIL), deren Adresse er über diesen zweiten (VAR-) Parameter an das
- aufrufende Programm zurück gibt.
-
- Der dritte Parameter, "ExprPtr" - ebenfalls ein Zeiger, zeigt
- schließlich auf den erzeugten "Programm-Code", dessen Adresse an das
- aufrufende Programm zurück gegeben wird.
-
- Tritt bei der Übersetzung nun ein Fehler auf (fehlende Klammer,
- unbekannte Funktion, unerlaubtes Zeichen usw.), so wird dieser erkannt,
- Art und Position im Ausdruck gemeldet und anhand der boole'schen
- Variablen "CalcResult" dem aufrufenden Programm angezeigt. Ebenso
- entfernt der Compiler wieder die von ihm erzeugten dynamischen Daten,
- um keinen Speicherplatz-fressenden "Müll" zurückzulassen. Konnte ein
- Ausdruck einwandfrei von "CompileExpression" übersetzt werden, so kann
- dieser nun so oft wie nötig von der Funktion "CalcExpression"
- ausgewertet bzw. das erzeugte "Programm" abgearbeitet werden.
-
- Dazu werden von der Funktion zwei Parameter benötigt: Der erste,
- "ExprPtr", enthält den Zeiger (die Adresse) auf das vom Compiler
- erzeugte und nun abzuarbeitende Programm. Der zweite, "VarTable", zeigt
- auf die zur Auswertung für diesen Ausdruck zu verwendente Variablen-
- Tabelle, in der nun natürlich die aktuellen Werte der Variablen stehen
- müssen (s. CALCUTIL.PAS und CALCDEMO.PAS):
- Ergebnis :=CalcExpression(ExprPtr,VarTable);
- Sollte während der Auswertung ein Fehler auftreten, z.B. Division durch
- Null, bricht die Funktion die Abarbeitung ab und zeigt diesen Umstand
- wie auch der Compiler anhand von "CalcResult" dem aufrufenden Programm
- an. UPN - Umgekehrt PolnischeNotation Bei fleißigen PASCAL-Lesern
- dürfte beim Stichwort "UPN" schon der Groschen fallen:
-
- Die angesprochene, für den Rechner leichter verständliche Schreibweise
- von Ausdrücken - die UPN-Notation, wurde bereits in PASCAL 1/87 in
- "Datenstrukturen mit Zeigern" besprochen und ihre Vorteile anhand eines
- Beispiels demonstriert.
-
- Da diese Notation wohl eine der gängigsten bei der Implementation von
- Folgen bei Rechenoperationen ist (es gibt sogar Taschenrechner, welche
- mit dem Benutzer über diese Notation kommunizieren) und zudem relativ
- einfach programmiert werden kann, ist auch unser Compiler mit UPN
- ausgerüstet worden: Ein von "CompileExpression" übersetzter Ausdruck,
- das erzeugte "Programm", entspricht also einer Folge von Anweisungen in
- UPN-Notation.
-
- Dieses erzeugte und von "CalcExpression" auszuwertende UPN-Programm
- wird, wie schon erwähnt, dynamisch mit Zeigern realisiert.
-
- Zweckmäßigerweise werden in diesem Programm wahlweise Operanden
- (Zahlen, Variablen) oder Operatoren (+, *, usw., Funktionen)
- abgespeichert, was mit der Definition eines "Programmelements" als
- "varianter" Record möglich ist ( s. CALCTYPE.PAS,TYPE Calc_Instruct).
-
- Hierbei stellt "NextInst" einen Zeigertyp auf das nächste Element des
- Programms dar, "Instruct" enthält den "auszuführenden Befehl" und ist
- vom Typ "Calc_Symbols". Mit diesem Datentyp hat es eine besondere
- Bewandnis:
-
- Da unser Programm die implementierten Rechenfunktionen zum einen beim
- Namen erkennen muß (Übersetzung der Zeichenfolge), zum anderen aber
- diese zur schnellen Ausführung auch in einer geeigneter Form vorliegen
- muß, ist es notwendig, jeder bei der String-Auswertung "erkannten"
- Funktion eine skalare Größe zuzuordnen, über welche diese Funktion bei
- der Berechnung von Werten mit "CalcExpression" in einer CASE-Anweisung
- aufgerufen werden kann.
-
- Diese Korrespondenz realisieren der skalare Typ "Calc_Symbols" und das
- String-ARRAY "Calc_Ids". Ersterer enthält für jede in CALC
- implementierte Funktion eine symbolische Bezeichnung. Das String-ARRAY
- "Calc_Ids" wird durch diese Symbole indiziert, der Zugriff auf ein
- durch "Symbol" indiziertes Element von "Calc_Ids" liefert also den
- entsprechenden String für die durch "Symbol" symbolisierte Funktion.
-
- Ein Beispiel:
- Calc_Ids[Calc_Sqr] = 'SQR' Calc_Ids[Calc_Add] = '+'
- Zwei Elementen von "Calc_Symbols" kommt eine besondere Bedeutung zu:
- Enthält ein Element des UPN-Programms in der "Instruct"-Komponente das
- Symbol "Calc_Const", so bedeutet dies, daß hier keine Funktion, sondern
- eine Konstante als Operand gespeichert ist. Lautet dagegen das Symbol
- "Calc_Var", so wird der aktuelle Wert der Variablen aus der Variablen-
- Tabelle eingesetzt, auf die der als Operand gespeicherte Index zeigt.
- Alle anderen Symbole, bis auf die vom Compiler benutzten, bewirken bei
- der Auswertung die Ausführung der entsprechenden Funktion.
-
- Exkurs theoretische Informatik: Backus-Naur-Diagramme
- Die Auswertung der UPN-Struktur ist nun relativ einfach, doch wie
- kommen wir zu unserem "Programm"? Man könnte sich natürlich einen
- Ausdruck aufschreiben und überlegen, welche Fälle hier so auftreten
- können. Doch warum das Rad zweimal erfinden? Jedes Pascal-Buch nimmt
- uns einen riesigen Berg an Arbeit ab, denn im Anhang dieser Bücher ist
- die Syntax von Pascal in Form von Backus-Naur-Diagrammen beschrieben.
-
- Zur Erklärung: Backus-Naur-Diagramme sind Konstrukte, um die Syntax von
- Sprachen zu beschreiben. Dies müssen nicht unbedingt Computersprachen
- sein, auch natürliche, menschliche Sprachen lassen sich mittels dieser
- Diagramme beschreiben.
-
- Begriffe, welche durch "< >" umklammert sind, heißen Variable. Für jede
- Variable existiert genau eine Regel, d.h. einer Variable wird eine
- Folge von weiteren Vari ablen bzw. sogenannten Terminalsymbolen (dies
- sind Begriffe ohne Klammerung) zugeordnet. Das Zuordnungszeichen ist
- "::=".
-
- Ziel eines solchen Diagramms ist es natürlich, eine Variable in
- Abhängigkeit des zugrundegelegten Alphabets darzustellen. Eine solche
- Darstellung erhält man, indem man in die, für eine Variable zugehörige
- Regel nacheinander die Regeln für, die auf der rechten Seite
- spezifizierten Variablen einsetzt.
-
- Hierbei gilt:
-
- - bei durch "|" getrennten Variablen wird jeweils nur eine gewählt
- - Mit "{ }" umklammerte Teile sind beliebig oft wiederholbar
-
- Das praktische an einem solchen Backus-Naur-Diagramm ist nun, daß es
- denkbar einfach ist, es in ein Programm umzusetzen.
-
- Jede auf der rechten Seite einer Regel spezifizierte Variable
- entspricht einem Unterprogramm innerhalb des durch die Variable auf der
- linken Seite der Regel gegebenen (Unter-) Programms. Da Regeln sich
- auch selbst enthalten können (entweder direkt oder auf Umwegen), muß
- die Programmiersprache, in welcher das Diagramm codiert werden soll,
- rekursive Prozeduraufrufe erlauben. In PASCAL ist dies bekanntlich der
- Fall. Dieses kann, wie schon erwähnt, praktisch aus dem Pascal-Handbuch
- mit einigen kleinen Änderungen (CALC muß nicht alles kennen, was Pascal
- kennt!) übernommen und mittels oben angegebener Programmiertechnik
- direkt in die Prozedur "CompileExpression" umgesetzt werden. Bei der
- Übersetzung wird das Diagramm beginnend von "Expression" solange
- durchlaufen, bis ein Terminalsymbol (dies entspricht einem Element von
- "Calc_Ids") erreicht ist. Das zugehörige Element von "CalcSymbols" wird
- dann an das Programm angehängt.
- Auswertung zur Laufzeit
- Nachdem das kompliziert erscheinende Problem der Übersetzung mit
- verhältnismäßig geringem Entwicklungsaufwand gelöst ist, wollen wir uns
- der Auswertung des UPN-Programms widmen.
-
- Zur Berechnung von Funktionswerten mittels "CalcExpression" wird dort
- ein Akkumulator und ein Rechenstapel benötigt. Der Akkumulator "X"
- dient wie das Rechenwerk eines Prozessors zum Rechnen: Sein Inhalt ist
- der Operand einer Berechnung, nach selbiger enthält er das Ergebnis.
- Der Rechenstapel wird benötigt, um bei komplizierten Ausdrücken
- (Klammern) Zwischenergebnisse und Operanden aus dem Programm abzulegen,
- die wegen UPN nicht sofort zu einer Berechnung herangezogen werden.
- Dieser ist als ein Feld von "Calc_Operand" mit einer festen Größe
- (StackSize) realisiert, die für die meisten Fälle ausreichend sein
- dürfte.
-
- Die UPN ist eine Postfix-Notation, d.h. ein Operator folgt immer nach
- (post) den zugehörigen Operanden. Für die Realisierung in unserem
- Programm heißt dies, daß das UPN-Programm der Reihe nach durchlaufen
- wird: Tritt ein "Calc_Cons" bzw. "Valc_Var" auf, so wird der aktuelle
- Wert von "X" auf den Rechenstapel geschoben und "X" mit dem Wert der
- Konstanten bzw. Variablen "geladen". Im Falle einer Funktion wird der
- Inhalt von "X" und, wenn diese zwei Operanden benötigt, das oberste
- Stapelelement geholt und die Funktion hierauf angewandt.
-
- Um eventuell auftretende Fehler (bei Ausführung einer Funktion ist kein
- Element mehr auf dem Stapel etc.) brauchen wir uns keine Sorgen zu
- machen, diese werden bereits bei der Übersetzung des Ausdrucks erkannt.
- Anders sieht es bei unzulässigen Operationen wie Division durch Null
- etc. aus. Diese Möglichkeiten müssen vor der Ausführung der
- entsprechenden Funktion geprüft und vom Programm abgefangen werden, um
- keinen "vollständigen" Programmabbruch ( Rückkehr zum Betriebsystem) zu
- riskieren (s. CALC.PAS)
- Implementierte Funktionen
- Nachdem wir jetzt ausführlich auf die Ablaufmechanismen von CALC
- eingegangen sind, wollen wir uns noch anschauen, was dieses Programm
- überhaupt zu leisten im Stande ist.
-
- Neben den in Pascal vorhandenen Standard-Funktionen (+, -, *, /, abs,
- sqr, sqrt, sin, cos, arctan, exp, ln) wurden zur Vollständigkeit und
- auch zur Demonstration der Erweiterungsmöglichkeit zusätzlich die
- Funktionen der Bibliothek MATHFUNC. PAS aus PASCAL 3/87 implementiert.
- Diese wurde wegen der Fehlerbehandlung noch einmal überarbeitet, kann
- aber hier aus Platzgründen nicht gedruckt werden. Sie ist aber auf der
- DATABOX-Diskette zu dieser PASCAL-Ausgabe nochmals enthalten. Wer weder
- die Ausgabe 3/87 sein Eigen nennt, noch in die DATABOX zu dieser
- Ausgabe investieren möchte, der kann die Funktionsvielfalt von CALC
- wieder leicht "abspecken":
-
- In der "großen" CASE-Struktur von "CalcExpression" brauchen lediglich
- die Fälle entfernt werden (besser: in Kommentar-Klammern einschließen),
- welche für die nicht-Standard-Funktionen zuständig sind. In dem Fall
- wird statt des Funktionsaufru f eine entsprechende Fehlermeldung von
- "CalcExpression" getätigt.
-
- Beim Eingeben eines Ausdrucks als String sollte man auf folgendes
- achten: zwischen Groß- und Kleinschreibung wird nicht unterschieden,
- Leerzeichen werden ignoriert, können aber zur Begrenzung von Operatoren
- wie z.B. MOD von anderen Buchstab enfolgen notwendig sein. Parameter
- von Funktionen werden mit "( )" eingeschlossen, Operatoren (auch
- Potenzierung!) werden wie in PASCAL üblich gehandhabt.
-
- Leistungsfähigkeit:
- Um ein Bild der Schnelligkeit von CALC zu erhalten, wurde ein
- Vergleichstest mit einem Ausdruck-Interpreter (dieser entspricht der
- Prozedur "CompileExpression", anstelle der Programmerzeugung wird
- jedoch gleich gerechnet) sowie dem direkt compilierten Turbo-Code
- durchgeführt. Um ein einheitliches Niveau zu erreichen, wurden in allen
- Fällen die gleichen Nicht-Standard-Funktionen verwendet. Die zu
- testenden Ausdrücke wurden in vier Kategorien differenziert:
-
- a) einfache Funktionsaufrufe von Standard-Funktionen
- b) einfache Funktionsaufrufe von benutzerdefinierten Funktionen
- c) einfache Grundrechenarten
- d) komplexe, lange Ausdrücke
-
- Globale Aussagen über den Vergleich können angesichts der stark
- streuenden Zeitdifferenzen nicht gemacht werden, unter Berücksichtigung
- der erzielten Ergebnisse in den einzelnen Kategorien kann man jedoch
- sagen, daß sich der Einsatz von CALC in allen den Fällen bezahlbar
- macht, in denen nicht übermäßig viele (oder ausschließlich)
- Grundrechenoperationen vorkommen. Dies liegt an der Tatsache, daß bei
- diesen Operationen der Verwaltungsaufwand (Zeiger etc.) den Aufwand an
- Rechenoperationen bei weitem übersteigt. Dieser Effekt wird beim
- Interpreter aufgrund seiner Arbeitsweise noch deutlicher.
-
- Sobald im Ausdruck rechenintensive Funktionen (exp,sin...) auftauchen,
- reduziert sich der Laufzeit-Unterschied für eine Funktionswertberech-
- nung zwischen CALC und compiliertem Code ganz erheblich, in diesen
- (wohl auch am häufigsten vorkommen den) Fällen ist der Maschinencode
- nur noch bis etwa 15% schneller.
-
- Dies stellt ein wohl nicht erhofftes, sehr brauchbares Resultat dar,
- denn die 30 Sekunden, die man z.B. beim Erstellen einer 5-Minuten-3-D-
- Grafik verschenkt, werden durch den Komfort, die Funktionsvorschrift
- über die Tastatur direkt ins Programm eingeben zu können und die
- Ersparung von minutenlangem Compilieren/Linken bei weitem wieder auf-
- gewogen.
-
- Zudem wird als "Abfallprodukt" sozusagen eine Erweiterung des benutzten
- PASCALs geliefert: stellt man seine Rechenprogramme auf die Berechnung
- von Funktionswerten durch CALC um, so ist praktisch möglich, Funktionen
- (nämlich den Ausdruck als String) als Parameter an andere
- Unterprogramme zu übergeben!
-
- Diese Möglichkeit ist zwar in Standard-Pascal generell vorgesehen,
- jedoch bei den wenigsten PASCAL-Implementationen realisiert.
- Vorgehen bei Erweiterungen
- Abschließend noch ein paar Tips für all jene, die den CALC für andere
- Zwecke "mißbrauchen" oder weiter ausbauen wollen.
-
- Der Einbau von weiteren Funktionen geschieht durch das Erweitern des
- Typs "Calc_Symbols" um die entsprechenden Symbole sowie die Angabe der
- jeweiligen Funktionsnamen (die Bezeichnungen im Ausdruck-String!) in
- "Calc_Ids" (Reihenfolge beachten!). Zusätzlich muß natürlich mitgeteilt
- werden, wie diese Funktionen wirken sollen. Dies wird innerhalb der
- CASE-Anweisung in "CalcExpression" eingefügt.
-
- Bei einem Typenwechsel des Rechners, zum Beispiel auf komplexe Zahlen,
- COMPLEX (hierzu siehe auch PASCAL 3/87), wird der Typ "Calc_Operand"
- entsprechend geändert. Natürlich müssen sämtliche REAL-Funktionen dann
- auch durch ihre Äquivalente ersetzt werden. Wem das alles nicht aus-
- reicht und wer noch komplexere Berechnungen durchführen will, der kann
- dies durch Erweiterung des Backus-Naur-Diagramms und anschließende
- Übertragung ins Programm tun. So könnte man wegen des flexiblen
- Konzepts von CALC über diesen weitere "Programm-Strukturen" legen, die
- z.B. Schleifen, Anweisungsfolgen etc. handhaben.
-
- Wir hoffen, das Mitvollziehen der Konstruktion dieses Programms hat
- Ihnen ein wenig Spaß gemacht und auch einiges an Erkenntnissen über die
- Programmentwicklung gebracht. Rechner einschalten und 'drauflostippen'
- ist eben nicht alles!
-