home *** CD-ROM | disk | FTP | other *** search
/ Turbo Toolbox / Turbo_Toolbox.iso / spezial / 04 / calctext.887 < prev    next >
Encoding:
Text File  |  1988-10-26  |  24.0 KB  |  452 lines

  1. Text aus PASCAL- Ausgabe 8/87
  2.  
  3.  
  4. CALC - ein mathematischer Compiler
  5. von Karsten Gieselmann & Michael Ceol
  6.  
  7. Beim Umgang mit Programmen, welche benutzerdefinierte Funktionen
  8. bearbeiten - als Paradebeispiele hierfür betrachte man die
  9. "Kurvendiskussion" (PASCAL 11/86) und die "Drei-dimensionale Funktions-
  10. Darstellung" (PASCAL 2/87) - ist es meist sehr lästig, wenn bei Wahl
  11. einer anderen Funktion ständig der Compiler bemüht werden muß, um den
  12. um die neue Funktion bereicherten Quelltext zum x-ten Mal zu
  13. übersetzen.
  14.  
  15. Bei kürzeren Programmen hat man da schon mal ein Nachsehen mit seinem
  16. "Rechenknecht". Im Regelfall jedoch sitzt man vor der "Kiste" und
  17. wartet und wartet.... Das muss aber nicht sein - denn was eine
  18. Programmier-Sprache schafft, die Übersetzung einer Zeichenfolge in eine
  19. für den Rechner verständliche Form, sollte auch für uns durch ein
  20. selbstgeschriebenes Programm machbar sein!
  21.  
  22. Im Folgenden wird nun eine Pascal-Bibliothek vorgestellt, die dieses
  23. Problem in recht effizienter Art und Weise löst. Zu langsam ? Manchem
  24. dämmert es vielleicht noch aus seiner BASIC-Zeit: Irgendwie bekommt man
  25. seine Funktion schon ins Programm gePEEKed und gePOKEd - wie war das
  26. noch gleich?
  27.  
  28. Ja, richtig, diesen Gedanken kann man in Pascal und verwandten Sprachen
  29. getrost wieder vergessen, denn selbige sind Compiler-Sprachen! In
  30. diesen kann das lauffähige Programm (auch Object-Code genannt) während
  31. der Laufzeit höchsten auf Maschinencode-Ebene manipuliert werden (dies
  32. ist aber auch nicht gerade die "feine englische Art" und äußerst (!)
  33. kompliziert).
  34.  
  35. Also bleibt als einzige Möglichkeit, sich einen eigenen "Auswerter" für
  36. solche Formeln, sprich numerische Ausdrücke (engl.: Expression, wird
  37. Ihnen im Programmtext noch häufiger begegnen), zu basteln.
  38.  
  39. Lieber nicht, werden jetzt vielleicht einige von Ihnen sagen, da wird
  40. mein Rechner ja vom Formel-1-Wagen zum Dreirad degradiert! Nein, da
  41. nehme ich doch die längeren Übersetzungszeiten des Pascal-Compilers bei
  42. jedem neuen Ausdruck in Kauf.
  43.  
  44. Aber weit gefehlt: Mit dem geeigneten Konzept kann man zu einer Lösung
  45. gelangen, die (bis auf einige Sonderfälle) mit ihren Auswertungen in
  46. der Geschwindigkeit durchschnittlich nur um etwa 15% hinter dem vom
  47. Pascal-Compiler erzeugten Maschinencode "hinterherhinkt". Die hier
  48. vorgestellte Pascal-Bibliothek "CALC" (in Turbo Pascal geschrieben,
  49. verwendet allerdings nur Standard-Aufrufe!) wertet als Zeichenketten
  50. (String) vorliegende numerische Ausdrücke aus und liefert - bei
  51. korrekter Eingabe gemäß Pascal-Syntax (-Grammatik) - den entsprechenden
  52. Wert des Ausdrucks. Je nach Bedarf können dabei nur eine oder mehrere
  53. Variablen im Ausdruck verwendet werden!
  54.  
  55. Dies reicht z.B. für die Funktionswertberechnung in einer
  56. (eindimensionalen) Kurvendiskussion aus. Wer noch höher hinaus will -
  57. als 3-D-Grafiker ist man dazu gezwungen - kann das Programm leicht an
  58. seine Bedürfnisse anpassen bzw. erweitern. Hierauf wurde bei der
  59. Entwicklung allergrößter Wert gelegt.
  60.  
  61. Diesem Anliegen Rechnung tragen außerdem die nun folgende detaillierte
  62. Beschreibung der Funktion und Wirkungsweise der einzelnen Programmteile
  63. (siehe auch die Kommentare in den Listings!) sowie die Vorstellung der
  64. beim Programmieren verwendeten Methode -einem wichtigen Hilfsmittel aus
  65. der Theoretischen Informatik zur Programmentwicklung.
  66.  
  67. Mit diesen Grundlagen ist ein Typenwechsel (z.B. von REAL auf COMPLEX)
  68. des "Rechners", eine Funktionserweiterung oder z.B. das "Aufsetzen"
  69. einer übergeordneten "Programmsteuerung" kein Problem.
  70.  
  71. Mehr hierzu jedoch später, zunächst wollen wir uns einigen
  72. Vorüberlegungen widmen.Zu Anfang stellt sich natürlich die Frage, ob
  73. unser Programm Interpreter-oder Compiler-artig arbeiten soll, also ob
  74. zur Berechnung eines Wertes jedesmal der gesamte Ausdruck in seiner
  75. natürlichen (Eingabe-) Form abgearbeitet wird, oder ob eine Art
  76. Vorübersetzung in eine für den Rechner leichter verständliche Struktur
  77. erfolgt, die anschließend bei jeder Berechnung nur noch ausgewertet
  78. wird.
  79.  
  80. Nun, im Hinblick auf den Verwendungszweck der Bibliothek
  81. (rechenintensive Programme wie "Kurvendiskussion" oder "3-D-Plotter")
  82. sollte man nicht mit ein paar Bytes knausern und seinen Programmen im
  83. Hinblick auf kürzestmögliche Rechenzeit einen Ausdruck-Compiler
  84. spendieren. Alles in allem verschenkt man hierbei sowieso nicht viel
  85. Speicherplatz, da der Aufwand zur Auswertung der Zeichenkette bei
  86. Compiler und Interpreter in etwa gleich groß ist. Der Programmteil des
  87. Compilers zur Auswertung der vorübersetzten Struktur ist außerdem
  88. relativ kompakt.
  89. Arbeiten mit dem  CALC.
  90. Wir wollen unseren "Rechner" so aufbauen, daß seine
  91. Einsatzmöglichkeiten aus einem anderen Programm heraus so flexibel wie
  92. möglich sind. Dazu Bedarf es außer dem "Compiler" und "Auswerter" noch
  93. ein paar anderer "Hilfsmittel", die CALC und das ihn beherbergende
  94. Programm zur "Verwaltung" der CALC-Daten benötigen. Zu diesen
  95. Hilfsmitteln bedarf es noch einiger Überlegungen. Was muß CALC
  96. erfüllen, um vielseitig einsetzbar zu sein? Wie ein richtiger Compiler
  97. erzeugt der CALC-Compiler aus einem "Programm-Text" einen "Programm
  98. Code", der dann direkt von einer "Maschine" ausgeführt werden kann
  99. allerdings beschränkt auf einen numerischen Ausdruck.
  100.  
  101. Nun kann es ja in einer Anwendung notwendig sein, mehrere Ausdrücke
  102. "hintereinander" zu verarbeiten, wobei das Ergebnis des einen zur
  103. Auswertung in den nächsten einfließt. Aus diesem Grund sollte die
  104. Anzahl der möglichen Ausdrücke und der damit verbundenen "Programme"
  105. variabel sein: CALC speichert den aus einen Ausdruck erzeugten "Code"
  106. in einer dynamischen Datenstuktur (s. PASCAL 11/86 u. 1/87:
  107. "Datenstrukturen mit Zeigern") und teilt dem aufrufenden Programm über
  108. einen Parameter mit, wo dieses ihn finden kann, um ihn später von der
  109. "Maschine" abarbeiten zu lassen. So können mehrere übersetzte Ausdrücke
  110. schnell ausgewertet werden!
  111.  
  112. Ein solcher Ausdruck kann z.B. konstant sein: (1324-12.75)/50. Er kann
  113. aber auch eine Variable enthalten: 10*sin(x) - oder auch mehrere:
  114. a^2+2*a*b+b^2. Nun sollte unsere Lösung nicht die x-te Variation eines
  115. "Formelauswerters" für nur eine Variable sein, was die
  116. Einsatzmöglichkeit sehr einschränkt. Vielmehr sollte eine vom
  117. Programmierer festlegbare Anzahl von Variablen in einem Ausdruck
  118. erlaubt sein. Aus diesem Grund gehört zu jedem von CALC zu
  119. verarbeitenden Ausdruck eine " Variablen-Tabelle", in denen die
  120. Variablen des Ausdrucks mit ihren Bezeichnern (Namen) und aktuellen
  121. Werten geführt werden.
  122.  
  123. Damit nun diese von CALC und vom Programmierer komfortabel verwaltet
  124. werden können, gibt es einige Hilfsfunktionen und -prozeduren, welche
  125. die Tabellen nach Bedarf erzeugen und löschen bzw. deren Inhalte
  126. manipulieren. Ebenso gibt es eine Möglichkeit, nicht mehr benötige
  127. "Programme" wieder zu "löschen", um für neue Platz zu schaffen!
  128. (s.CALCUTIL.PAS) Übersetzen... Doch wie wird nun ein Ausdruck
  129. übersetzt? Dafür ist die Prozedur "CompileExpression" zuständig, die
  130. für ihre Arbeit drei Parameter benötigt und einen Schalter besitzt, der
  131. ihre Arbeitsweise steuert. Ihr Aufruf gestaltet sich folgendermaßen:
  132.  
  133. CompileExpression (Expr, VarTable, ExprPtr);
  134.  
  135. Doch zuerst der Schalter:
  136.  
  137. Wie bereits erwähnt, sollen mehrere Variablen in einem Ausdruck
  138. verwendet werden können, wobei ihre Bezeichnung (Namensgebung) frei dem
  139. Anwender unterliegt. Die einzigste Einschränkung soll sein, daß sie mit
  140. einem Buchstaben anfangen müssen und nicht mit den "reservierten"
  141. Bezeichnern der in CALC implementierten Operatoren und Funktionen
  142. identisch sein dürfen. Wenn der CALC-Compiler bei der Übersetzung nun
  143. auf eine Zeichenfolge stößt, die nicht mit einem der "reservierten"
  144. Bezeichner übereinstimmt, soll er annehmen, daß es sich um eine
  145. Variable handelt.
  146.  
  147. Seine nächste Aktion ist nun, in der zum Ausdruck gehörenden Variablen-
  148. Tabelle (s.u.) nachzusehen, ob er den bis jetzt unbekannten Bezeichner
  149. dort findet - ob es sich um eine in der Tabelle eingetragene Variable
  150. handelt. Trifft dies zu, ist der Fall mit der Erzeugung des
  151. entsprechenden Programm-Codes (s.u.) erledigt und er kann mit der
  152. Übersetzung fortfahren.
  153.  
  154. Konnte der Bezeichner aber auch nicht anhand der Variablen-Tabelle
  155. identifiziert werden, so soll der Compiler nun die Möglichkeit haben,
  156. diesen als neue Variable zu interpretieren, in die Tabelle einzutragen,
  157. den entsprechenden Code zu erzeugen und - fortzufahren.
  158.  
  159. Nun ist aber nicht in jedem Fall eine solche Großzügigkeit gefragt ein
  160. Tippfehler, und schon hat man wie bei BASIC eine neue Variable, die
  161. Ungereimtheiten aufkommen lassen kann. Hier tritt nun der Schalter in
  162. Kraft: Die globale boole'sche Variable "CalcDecMod" steuert die
  163. Verarbeitung der unbekannten Bezeichner. Wie bei Pascal kann man
  164. verlangen, das die zu verwendenten Variablen im Ausdruck vorher
  165. vereinbart (deklariert) werden: "CalcDecMod" = TRUE.
  166.  
  167. Wie geschieht diese Vereinbarung? Bevor ein Ausdruck zur Übersetzung
  168. vorgelegt wird, muß mittels der Hilfsfunktion "NewVarTab" eine
  169. Variablen-Tabelle erzeugt werden (falls nicht schon vorhanden) und mit
  170. "AddToVarTab" der oder (die) gewünschte(n) Variablen-Name(n)
  171. (in Gorßbuchstaben!) in diese Tabelle eingetragen werden. Ist
  172. "CalcDecMod" = TRUE, so wird "CompileExpression" nur solche Variablen
  173. akzeptieren, die schon in der Tabelle eingetragen sind, andernfalls
  174. wird mit einer entsprechenden Fehlermeldung abgebrochen!
  175.  
  176. Diesen Modus benutzt auch das Demo-Programm, um nur die Variable "x" im
  177. auszuwertenden Funktionsausdruck zuzulassen.
  178.  
  179. Nun zu den Parametern:
  180.  
  181. Der erste, "Expr", enthält den zu übersetzenden Ausdruck als
  182. Zeichenkette (String) - z.B. "sin(x);". Die Syntax (Schreibweise)
  183. entspricht dabei den Pascal-Regeln, wobei das Semikolon das Ende des
  184. Ausdrucks signalisiert - nachfolgender Text im String wird ignoeriert.
  185.  
  186. Der zweite, "VarTable" - ein Zeiger (Pointer), zeigt auf die für den
  187. Ausdruck zu verwendende Variablen-Tabelle. Hier bestehen nun zwei
  188. Möglichkeiten: Es wird eine schon existierende Tabelle, mit oder ohne
  189. eingetragenen Variablen, angegeben (VarTable <> NIL, s.a. CalcDecMod).
  190. Andernfalls erzeugt der Compiler eine neue, leere Tabelle (VarTable =
  191. NIL), deren Adresse er über diesen zweiten (VAR-) Parameter an das
  192. aufrufende Programm zurück gibt.
  193.  
  194. Der dritte Parameter, "ExprPtr" - ebenfalls ein Zeiger, zeigt
  195. schließlich auf den erzeugten "Programm-Code", dessen Adresse an das
  196. aufrufende Programm zurück gegeben wird.
  197.  
  198. Tritt bei der Übersetzung nun ein Fehler auf (fehlende Klammer,
  199. unbekannte Funktion, unerlaubtes Zeichen usw.), so wird dieser erkannt,
  200. Art und Position im Ausdruck gemeldet und anhand der boole'schen
  201. Variablen "CalcResult" dem aufrufenden Programm angezeigt. Ebenso
  202. entfernt der Compiler wieder die von ihm erzeugten dynamischen Daten,
  203. um keinen Speicherplatz-fressenden "Müll" zurückzulassen. Konnte ein
  204. Ausdruck einwandfrei von "CompileExpression" übersetzt werden, so kann
  205. dieser nun so oft wie nötig von der Funktion "CalcExpression"
  206. ausgewertet bzw. das erzeugte "Programm" abgearbeitet werden.
  207.  
  208. Dazu werden von der Funktion zwei Parameter benötigt: Der erste,
  209. "ExprPtr", enthält den Zeiger (die Adresse) auf das vom Compiler
  210. erzeugte und nun abzuarbeitende Programm. Der zweite, "VarTable", zeigt
  211. auf die zur Auswertung für diesen Ausdruck zu verwendente Variablen-
  212. Tabelle, in der nun natürlich die aktuellen Werte der Variablen stehen
  213. müssen (s. CALCUTIL.PAS und CALCDEMO.PAS):
  214.  Ergebnis :=CalcExpression(ExprPtr,VarTable);
  215. Sollte während der Auswertung ein Fehler auftreten, z.B. Division durch
  216. Null, bricht die Funktion die Abarbeitung ab und zeigt diesen Umstand
  217. wie auch der Compiler anhand von "CalcResult" dem aufrufenden Programm
  218. an. UPN - Umgekehrt PolnischeNotation Bei fleißigen PASCAL-Lesern
  219. dürfte beim Stichwort "UPN" schon der Groschen fallen:
  220.  
  221. Die angesprochene, für den Rechner leichter verständliche Schreibweise
  222. von Ausdrücken - die UPN-Notation, wurde bereits in PASCAL 1/87 in
  223. "Datenstrukturen mit Zeigern" besprochen und ihre Vorteile anhand eines
  224. Beispiels demonstriert.
  225.  
  226. Da diese Notation wohl eine der gängigsten bei der Implementation von
  227. Folgen bei Rechenoperationen ist (es gibt sogar Taschenrechner, welche
  228. mit dem Benutzer über diese Notation kommunizieren) und zudem relativ
  229. einfach programmiert werden kann, ist auch unser Compiler mit UPN
  230. ausgerüstet worden: Ein von "CompileExpression" übersetzter Ausdruck,
  231. das erzeugte "Programm", entspricht also einer Folge von Anweisungen in
  232. UPN-Notation.
  233.  
  234. Dieses erzeugte und von "CalcExpression" auszuwertende UPN-Programm
  235. wird, wie schon erwähnt, dynamisch mit Zeigern realisiert.
  236.  
  237. Zweckmäßigerweise werden in diesem Programm wahlweise Operanden
  238. (Zahlen, Variablen) oder Operatoren (+, *, usw., Funktionen)
  239. abgespeichert, was mit der Definition eines "Programmelements" als
  240. "varianter" Record möglich ist ( s. CALCTYPE.PAS,TYPE Calc_Instruct).
  241.  
  242. Hierbei stellt "NextInst" einen Zeigertyp auf das nächste Element des
  243. Programms dar, "Instruct" enthält den "auszuführenden Befehl" und ist
  244. vom Typ "Calc_Symbols". Mit diesem Datentyp hat es eine besondere
  245. Bewandnis:
  246.  
  247. Da unser Programm die implementierten Rechenfunktionen zum einen beim
  248. Namen erkennen muß (Übersetzung der Zeichenfolge), zum anderen aber
  249. diese zur schnellen Ausführung auch in einer geeigneter Form vorliegen
  250. muß, ist es notwendig, jeder bei der String-Auswertung "erkannten"
  251. Funktion eine skalare Größe zuzuordnen, über welche diese Funktion bei
  252. der Berechnung von Werten mit "CalcExpression" in einer CASE-Anweisung
  253. aufgerufen werden kann.
  254.  
  255. Diese Korrespondenz realisieren der skalare Typ "Calc_Symbols" und das
  256. String-ARRAY "Calc_Ids". Ersterer enthält für jede in CALC
  257. implementierte Funktion eine symbolische Bezeichnung. Das String-ARRAY
  258. "Calc_Ids" wird durch diese Symbole indiziert, der Zugriff auf ein
  259. durch "Symbol" indiziertes Element von "Calc_Ids" liefert also den
  260. entsprechenden String für die durch "Symbol" symbolisierte Funktion.
  261.  
  262. Ein  Beispiel:
  263. Calc_Ids[Calc_Sqr] = 'SQR' Calc_Ids[Calc_Add] = '+'
  264. Zwei Elementen von "Calc_Symbols" kommt eine besondere Bedeutung zu:
  265. Enthält ein Element des UPN-Programms in der "Instruct"-Komponente das
  266. Symbol "Calc_Const", so bedeutet dies, daß hier keine Funktion, sondern
  267. eine Konstante als Operand gespeichert ist. Lautet dagegen das Symbol
  268. "Calc_Var", so wird der aktuelle Wert der Variablen aus der Variablen-
  269. Tabelle eingesetzt, auf die der als Operand gespeicherte Index zeigt.
  270. Alle anderen Symbole, bis auf die vom Compiler benutzten, bewirken bei
  271. der Auswertung die Ausführung der entsprechenden Funktion.
  272.  
  273. Exkurs theoretische Informatik: Backus-Naur-Diagramme
  274. Die Auswertung der UPN-Struktur ist nun relativ einfach, doch wie
  275. kommen wir zu unserem "Programm"? Man könnte sich natürlich einen
  276. Ausdruck aufschreiben und überlegen, welche Fälle hier so auftreten
  277. können. Doch warum das Rad zweimal erfinden? Jedes Pascal-Buch nimmt
  278. uns einen riesigen Berg an Arbeit ab, denn im Anhang dieser Bücher ist
  279. die Syntax von Pascal in Form von Backus-Naur-Diagrammen beschrieben.
  280.  
  281. Zur Erklärung: Backus-Naur-Diagramme sind Konstrukte, um die Syntax von
  282. Sprachen zu beschreiben. Dies müssen nicht unbedingt Computersprachen
  283. sein, auch natürliche, menschliche Sprachen lassen sich mittels dieser
  284. Diagramme beschreiben.
  285.  
  286. Begriffe, welche durch "< >" umklammert sind, heißen Variable. Für jede
  287. Variable existiert genau eine Regel, d.h. einer Variable wird eine
  288. Folge von weiteren Vari ablen bzw. sogenannten Terminalsymbolen (dies
  289. sind Begriffe ohne Klammerung) zugeordnet. Das Zuordnungszeichen ist
  290. "::=".
  291.  
  292. Ziel eines solchen Diagramms ist es natürlich, eine Variable in
  293. Abhängigkeit des zugrundegelegten Alphabets darzustellen. Eine solche
  294. Darstellung erhält man, indem man in die, für eine Variable zugehörige
  295. Regel nacheinander die Regeln für, die auf der rechten Seite
  296. spezifizierten Variablen einsetzt.
  297.  
  298. Hierbei gilt:
  299.  
  300. - bei durch "|" getrennten Variablen wird jeweils nur eine gewählt
  301. - Mit "{ }" umklammerte Teile sind beliebig oft wiederholbar
  302.  
  303. Das praktische an einem solchen Backus-Naur-Diagramm ist nun, daß es
  304. denkbar einfach ist, es in ein Programm umzusetzen.
  305.  
  306. Jede auf der rechten Seite einer Regel spezifizierte Variable
  307. entspricht einem Unterprogramm innerhalb des durch die Variable auf der
  308. linken Seite der Regel gegebenen (Unter-) Programms. Da Regeln sich
  309. auch selbst enthalten können (entweder direkt oder auf Umwegen), muß
  310. die Programmiersprache, in welcher das Diagramm codiert werden soll,
  311. rekursive Prozeduraufrufe erlauben. In PASCAL ist dies bekanntlich der
  312. Fall. Dieses kann, wie schon erwähnt, praktisch aus dem Pascal-Handbuch
  313. mit einigen kleinen Änderungen (CALC muß nicht alles kennen, was Pascal
  314. kennt!) übernommen und mittels oben angegebener Programmiertechnik
  315. direkt in die Prozedur "CompileExpression" umgesetzt werden. Bei der
  316. Übersetzung wird das Diagramm beginnend von "Expression" solange
  317. durchlaufen, bis ein Terminalsymbol (dies entspricht einem Element von
  318. "Calc_Ids") erreicht ist. Das zugehörige Element von "CalcSymbols" wird
  319. dann an das Programm angehängt.
  320. Auswertung    zur    Laufzeit
  321. Nachdem das kompliziert erscheinende Problem der Übersetzung mit
  322. verhältnismäßig geringem Entwicklungsaufwand gelöst ist, wollen wir uns
  323. der Auswertung des UPN-Programms widmen.
  324.  
  325. Zur Berechnung von Funktionswerten mittels "CalcExpression" wird dort
  326. ein Akkumulator und ein Rechenstapel benötigt. Der Akkumulator "X"
  327. dient wie das Rechenwerk eines Prozessors zum Rechnen: Sein Inhalt ist
  328. der Operand einer Berechnung, nach selbiger enthält er das Ergebnis.
  329. Der Rechenstapel wird benötigt, um bei komplizierten Ausdrücken
  330. (Klammern) Zwischenergebnisse und Operanden aus dem Programm abzulegen,
  331. die wegen UPN nicht sofort zu einer Berechnung herangezogen werden.
  332. Dieser ist als ein Feld von "Calc_Operand" mit einer festen Größe
  333. (StackSize) realisiert, die für die meisten Fälle ausreichend sein
  334. dürfte.
  335.  
  336. Die UPN ist eine Postfix-Notation, d.h. ein Operator folgt immer nach
  337. (post) den zugehörigen Operanden. Für die Realisierung in unserem
  338. Programm heißt dies, daß das UPN-Programm der Reihe nach durchlaufen
  339. wird: Tritt ein "Calc_Cons" bzw. "Valc_Var" auf, so wird der aktuelle
  340. Wert von "X" auf den Rechenstapel geschoben und "X" mit dem Wert der
  341. Konstanten bzw. Variablen "geladen". Im Falle einer Funktion wird der
  342. Inhalt von "X" und, wenn diese zwei Operanden benötigt, das oberste
  343. Stapelelement geholt und die Funktion hierauf angewandt.
  344.  
  345. Um eventuell auftretende Fehler (bei Ausführung einer Funktion ist kein
  346. Element mehr auf dem Stapel etc.) brauchen wir uns keine Sorgen zu
  347. machen, diese werden bereits bei der Übersetzung des Ausdrucks erkannt.
  348. Anders sieht es bei unzulässigen Operationen wie Division durch Null
  349. etc. aus. Diese Möglichkeiten müssen vor der Ausführung der
  350. entsprechenden Funktion geprüft und vom Programm abgefangen werden, um
  351. keinen "vollständigen" Programmabbruch ( Rückkehr zum Betriebsystem) zu
  352. riskieren (s. CALC.PAS)
  353. Implementierte   Funktionen
  354. Nachdem wir jetzt ausführlich auf die Ablaufmechanismen von CALC
  355. eingegangen sind, wollen wir uns noch anschauen, was dieses Programm
  356. überhaupt zu leisten im Stande ist.
  357.  
  358. Neben den in Pascal vorhandenen Standard-Funktionen (+, -, *, /, abs,
  359. sqr, sqrt, sin, cos, arctan, exp, ln) wurden zur Vollständigkeit und
  360. auch zur Demonstration der Erweiterungsmöglichkeit zusätzlich die
  361. Funktionen der Bibliothek MATHFUNC. PAS aus PASCAL 3/87 implementiert.
  362. Diese wurde wegen der Fehlerbehandlung noch einmal überarbeitet, kann
  363. aber hier aus Platzgründen nicht gedruckt werden. Sie ist aber auf der
  364. DATABOX-Diskette zu dieser PASCAL-Ausgabe nochmals enthalten. Wer weder
  365. die Ausgabe 3/87 sein Eigen nennt, noch in die DATABOX zu dieser
  366. Ausgabe investieren möchte, der kann die Funktionsvielfalt von CALC
  367. wieder leicht "abspecken":
  368.  
  369. In der "großen" CASE-Struktur von "CalcExpression" brauchen lediglich
  370. die Fälle entfernt werden (besser: in Kommentar-Klammern einschließen),
  371. welche für die nicht-Standard-Funktionen zuständig sind. In dem Fall
  372. wird statt des Funktionsaufru f eine entsprechende Fehlermeldung von
  373. "CalcExpression" getätigt.
  374.  
  375. Beim Eingeben eines Ausdrucks als String sollte man auf folgendes
  376. achten: zwischen Groß- und Kleinschreibung wird nicht unterschieden,
  377. Leerzeichen werden ignoriert, können aber zur Begrenzung von Operatoren
  378. wie z.B. MOD von anderen Buchstab enfolgen notwendig sein. Parameter
  379. von Funktionen werden mit "( )" eingeschlossen, Operatoren (auch
  380. Potenzierung!) werden wie in PASCAL üblich gehandhabt.
  381.  
  382. Leistungsfähigkeit:
  383. Um ein Bild der Schnelligkeit von CALC zu erhalten, wurde ein
  384. Vergleichstest mit einem Ausdruck-Interpreter (dieser entspricht der
  385. Prozedur "CompileExpression", anstelle der Programmerzeugung wird
  386. jedoch gleich gerechnet) sowie dem direkt compilierten Turbo-Code
  387. durchgeführt. Um ein einheitliches Niveau zu erreichen, wurden in allen
  388. Fällen die gleichen Nicht-Standard-Funktionen verwendet. Die zu
  389. testenden Ausdrücke wurden in vier Kategorien differenziert:
  390.  
  391. a) einfache Funktionsaufrufe von Standard-Funktionen
  392. b) einfache Funktionsaufrufe von benutzerdefinierten Funktionen
  393. c) einfache Grundrechenarten
  394. d) komplexe, lange Ausdrücke
  395.  
  396. Globale Aussagen über den Vergleich können angesichts der stark
  397. streuenden Zeitdifferenzen nicht gemacht werden, unter Berücksichtigung
  398. der erzielten Ergebnisse in den einzelnen Kategorien kann man jedoch
  399. sagen, daß sich der Einsatz von CALC in allen den Fällen bezahlbar
  400. macht, in denen nicht übermäßig viele (oder ausschließlich)
  401. Grundrechenoperationen vorkommen. Dies liegt an der Tatsache, daß bei
  402. diesen Operationen der Verwaltungsaufwand (Zeiger etc.) den Aufwand an
  403. Rechenoperationen bei weitem übersteigt. Dieser Effekt wird beim
  404. Interpreter aufgrund seiner Arbeitsweise noch deutlicher.
  405.  
  406. Sobald im Ausdruck rechenintensive Funktionen (exp,sin...) auftauchen,
  407. reduziert sich der Laufzeit-Unterschied für eine Funktionswertberech-
  408. nung zwischen CALC und compiliertem Code ganz erheblich, in diesen
  409. (wohl auch am häufigsten vorkommen den) Fällen ist der Maschinencode
  410. nur noch bis etwa 15% schneller.
  411.  
  412. Dies stellt ein wohl nicht erhofftes, sehr brauchbares Resultat dar,
  413. denn die 30 Sekunden, die man z.B. beim Erstellen einer 5-Minuten-3-D-
  414. Grafik verschenkt, werden durch den Komfort, die Funktionsvorschrift
  415. über die Tastatur direkt ins Programm eingeben zu können und die
  416. Ersparung von minutenlangem Compilieren/Linken bei weitem wieder auf-
  417. gewogen.
  418.  
  419. Zudem wird als "Abfallprodukt" sozusagen eine Erweiterung des benutzten
  420. PASCALs geliefert: stellt man seine Rechenprogramme auf die Berechnung
  421. von Funktionswerten durch CALC um, so ist praktisch möglich, Funktionen
  422. (nämlich den Ausdruck als String) als Parameter an andere
  423. Unterprogramme zu übergeben!
  424.  
  425. Diese Möglichkeit ist zwar in Standard-Pascal generell vorgesehen,
  426. jedoch bei den wenigsten PASCAL-Implementationen realisiert.
  427. Vorgehen bei Erweiterungen
  428. Abschließend noch ein paar Tips für all jene, die den CALC für andere
  429. Zwecke "mißbrauchen" oder weiter ausbauen wollen.
  430.  
  431. Der Einbau von weiteren Funktionen geschieht durch das Erweitern des
  432. Typs "Calc_Symbols" um die entsprechenden Symbole sowie die Angabe der
  433. jeweiligen Funktionsnamen (die Bezeichnungen im Ausdruck-String!) in
  434. "Calc_Ids" (Reihenfolge beachten!). Zusätzlich muß natürlich mitgeteilt
  435. werden, wie diese Funktionen wirken sollen. Dies wird innerhalb der
  436. CASE-Anweisung in "CalcExpression" eingefügt.
  437.  
  438. Bei einem Typenwechsel des Rechners, zum Beispiel auf komplexe Zahlen,
  439. COMPLEX (hierzu siehe auch PASCAL 3/87), wird der Typ "Calc_Operand"
  440. entsprechend geändert. Natürlich müssen sämtliche REAL-Funktionen dann
  441. auch durch ihre Äquivalente ersetzt werden. Wem das alles nicht aus-
  442. reicht und wer noch komplexere Berechnungen durchführen will, der kann
  443. dies durch Erweiterung des Backus-Naur-Diagramms und anschließende
  444. Übertragung ins Programm tun. So könnte man wegen des flexiblen
  445. Konzepts von CALC über diesen weitere "Programm-Strukturen" legen, die
  446. z.B. Schleifen, Anweisungsfolgen etc. handhaben.
  447.  
  448. Wir hoffen, das Mitvollziehen der Konstruktion dieses Programms hat
  449. Ihnen ein wenig Spaß gemacht und auch einiges an Erkenntnissen über die
  450. Programmentwicklung gebracht. Rechner einschalten und 'drauflostippen'
  451. ist eben nicht alles!
  452.