home *** CD-ROM | disk | FTP | other *** search
-
- TOOLBOX Spezial IV: CALC Plus
-
-
- Integration von "Kurvendiskussion" PASCAL 11'86 und Sonderdruck
- mit "CALC" Formelinterpreter PASCAL 8'87
-
-
- Überarbeitung von Peter Engels, am 4.9.1988
-
-
-
-
- Das Programm "DISCUSS" von Karsten Gieselmann, das eine umfangrei-
- che Kurvendiskussion erlaubt, wurde dahingehend erweitert, daß nun
- auch die Eingabe von Funktionstermen zur Laufzeit möglich ist. Als
- Kernstück dient hier der mathematische Compiler "CALC", der von um
- die Routinen zur symbolischen Differentiation ergänzt worden ist.
-
- Bei diesen Änderungen stand neben der komfortableren Handhabung
- das Ziel im Vordergrund, das fertige Programm möglichst auf allen
- Rechnertypen und unter allen PASCAL - Varianten lauffähig zu
- halten. Darüberhinaus sollten die einzubindenden Prozeduren wenn
- möglich unverändert übernommen werden, damit sie weiterhin als
- Include-Files in anderen Programmen leicht verwendet werden
- können. Leider konnte ich diese Forderungen nicht vollständig
- erfüllen, da das fertige Programm bei dem Anwender sonst den
- Eindruck einer unausgereiften Rohfassung hinterlassen hätte.
-
- Entwickelt wurde das Programm auf einem Apple II+ mit IBS AP22
- Karte unter CP/M80 und Turbo 3.0 und anschließend für die normale
- SoftCard umgeschrieben und getestet. Anschließend um ein DRAW.INC
- für PC-CGA Karte erweitert und ebenfalls getestet.
-
- Das Programm weist in der vorliegenden Fassung gegenüber der
- Originalversion die folgenden Erweiterungen auf:
-
- - Eingabe eines bis zu 128 Zeichen langen Funktionsterms zur Lauf-
- zeit, der jederzeit geändert werden kann. Die maximale Zeichenzahl
- kann in CALCTYPE durch Ändern der Stringlänge von Calc_String auf
- bis zu 255 Zeichen erhöht werden. Dadurch gehen allerdings etwa
- 1.8 kB Speicherplatz für den Heap verloren (bei CP/M rechnet man
- noch mit jedem kB!).
-
- - symbolische Berechnung der ersten beiden Ableitungen, die auf
- dem Bildschirm ausgegeben und bei allen Berechnungen benutzt
- werden (der maximale Grad ist in maxgrad im File DISCUSS
- festgelegt und kann erhöht werden).
-
- - sämtliche Eingaben erfolgen als String über die ebenfalls von
- Karsten Gieselmann geschriebene Prozedur "READSTR", so daß ein
- optimaler Eingabekomfort gewährleistet ist.
-
- - bei der Eingabe von numerischen Werten, wie z.B. den Intervall-
- grenzen, wird ebenfalls mit "CALC" gearbeitet, so daß man jetzt
- auch 1/7 statt 0.1428571 oder einfach PI anstelle von 3.141592654
- eingeben kann. Es steht der gesamte Befehlsvorrat von CALC zur
- Verfügung. Die maximal mögliche Zeichenzahl beträgt 30, wozu ich
- aber später noch etwas sagen muß.
-
- - alle Eingaben werden im Rahmen des Möglichen auf Zulässigkeit
- geprüft
-
- - die Integrationsroutine erlaubt nun auch die Berechnung von
- Rotationsvolumen, Mantelfläche und Bogenlänge.
-
- - das Modul DRAW.INC wurde so erweitert, daß auch die ersten
- beiden Ableitungen grafisch dargestellt werden können, und
- jederzeit durch Drücken einer beliebigen Taste ein Abbruch möglich
- ist.
-
- - die Tabellenausgabe kann mit Ctrl-S angehalten werden. Dabei
- wird aber nicht die Compiler-Option $C+ benutzt, um einen voll-
- ständigen Programmabruch zu vermeiden.
-
- - alle Berechnungen, mit Ausnahme der Integration, können durch
- Drücken einer beliebigen Taste abgebrochen werden.
-
- - bei der Aufforderung, eine beliebige Taste zu drücken, wird bei
- gültigen Eingaben das Menu übersprungen, und gleich in den
- entsprechenden Programmteil verzweigt.
-
- Die neu hinzugekommenen Turbo-spezifischen Prozeduren und Varia-
- blen habe ich auf ein Minimum beschränkt. Es werden benutzt:
-
- 1) Delay(n) - im Hello - File
-
- 2) ClrEol - um den Bildschirm bei Fehlermeldungen zu säubern
-
- 3)ConOutPtr - in den Grafik-Routinen "DRAW3.INC" und "DRAW4.INC",
- die speziell für Apple II+/e Rechner angepaßt sind.
-
-
- Ich möchte nun für jedes eingebundene Modul die erforderlichen
- Änderungen angeben, die aber allesamt die Routinen in keiner Weise
- spezialisieren, sondern lediglich Verbesserungen für jeden Anwen-
- dungsfall bringen, so daß die einzelnen Teile auch problemlos und
- ohne Änderung in anderen Programmen verwendet werden können.
- Darüberhinaus werde ich auch auf eventuelle Bugs eingehen, die
- berichtigt wurden.
-
-
-
- 1) READSTR.INC Heft 3/87 S.82,83 - Karsten Gieselmann
-
- Sollte die Eingabe vom aufrufenden Programm abgewiesen werden, so
- daß an der gleichen Bildschirmposition eine erneute Eingabe erfol-
- gen muß, so wird zwar der String aber nicht der Bildschirm
- gelöscht.
-
- Abhilfe : zwischen Zeile 45 und 46 einfügen
-
- 45' : Write(' ' : MaxLen); Gotoxy(succ(x),y);
-
-
-
-
- 2) CALC und Hilfsfiles - Karsten Gieselmann, Michael Ceol
-
- Zunächst die Bugs :
-
- I) Schaltet man die RANGE - CHECK - Option $R+ ein, so steigt Calc
- in der Prozedur SearchSymTab regelmäßig mit einem Out Of Range
- Error aus. Ich habe daher die gesamte Prozedur umgeschrieben,
- wodurch sich leider auch Änderungen in den Files CALCCONS,
- CALCTYPE und CALCVAR ergeben :
-
- i) Die Prozedur SearchSymTab wird ersetzt durch :
-
- PROCEDURE SearchSymTab;
-
- VAR symok: BOOLEAN;
-
- BEGIN
- symok := FALSE;
- symbol := calc_err;
- WHILE (symbol < calc_end) AND NOT symok DO
- BEGIN
- symbol := Succ(symbol);
- symok := ident = calc_ids(.symbol.);
- END;
- IF NOT symok
- THEN
- symbol := calc_err
- END;
-
-
- ii) In CALCCONS entfällt Zeile 6, und damit das lästige Abzählen
- der implementierten Funktionen
-
-
- iii) In CALCTYPE ergänzt man Zeile 29 zu :
- ..., Calc_Rez, Calc_Fak, Calc_End);
-
-
- iv) In CALCVAR ergänzt man Zeile 47 zu :
- ..., 'REZ' , 'FAK' , '@ende@');
-
-
- II) Reicht der Speicherplatz nicht aus, um eine neue Variablen-
- tabelle anzulegen, so wird der Zeigervariablen, die auf diese
- Tabelle zeigt, der Wert Nil zugewiesen. Alle Hilfsroutinen, die
- mit Variablentabellen arbeiten, nehmen darauf keine Rücksicht, so
- daß Systemabstürze in diesen Fällen unvermeidbar sind. Man ändere
- daher in CALCUTIL folgende Zeilen :
-
- 85: BEGIN if vartab <> nil THEN dispose(vartab);
- 86: vartab := nil END;
-
- Zwischen Zeile 96 und 97 :
- 96': if vartab <> nil THEN BEGIN
-
- Zwischen Zeile 99 und 100
- 99': END ELSE SearchVarTab := 0
-
- Zwischen Zeile 111 und 112 :
- 111': if vartab <> nil THEN BEGIN
-
- Zwischen Zeile 119 und 120 :
- 119': END ELSE AddToVarTab := -1
-
- Zwischen Zeile 128 und 129 :
- 128': if vartab <> nil THEN BEGIN
-
- Zwischen Zeile 132 und 133 :
- 132' : END
- 132'': ELSE CalcError(0, 'Wertzuweisung an unbekannte Variable')
-
-
- III) In CALC muß die Zeile 354 wie folgt geändert werden :
-
- 354: Calc_Dvd : If Trunc(x) <> 0 THEN
-
- IV) In CALC muß die Zeile 357 wie folgt geändert werden :
-
- 357: Calc_Mod : If Trunc(x) <> 0 THEN
-
- V) CALC erlaubt auch die Eingabe von numerischen Werten, die
- unvollständig sind oder außerhalb des zulässigen Bereichs liegen,
- ohne auf den Fehler aufmerksam zu machen. Man ändere daher in CALC
- die Prozedur GetNumber wie folgt ab :
-
-
- i) Zeile 108 wird ergänzt zu :
-
-
- 108: NumberEnd, posi : INTEGER;
-
-
- ii) zwischen die Zeilen 111 und 112 füge man die folgenden ein :
-
- NumberStr := NumberStr + ' ';
- posi := 1;
- while (not (numberstr(.posi.) in (.'e','E'.)))
- and (posi < length(numberstr))
- do posi := succ(posi);
- if numberstr(.posi.) in (.'e','E'.) THEN
- BEGIN
- if numberstr(.posi+1.) in (.'+','-'.)
- THEN posi := succ(posi);
- if not (numberstr(.posi+1.) in (.'0'..'9'.))
- THEN error(StrPos+posi,'unvollstaendiger Ausdruck')
- else
- if
- ((numberstr(.posi+1.) = '3') and
- (numberstr(.posi+2.) in (.'7'..'9'.)))
- or
- ((numberstr(.posi+1.) > '3') and
- (numberstr(.posi+2.) in (.'0'..'9'.)))
- THEN
- error(StrPos+posi,'ausserhalb des Real-Bereichs');
- END;
-
-
-
- Außerdem lassen sich an CALC drei kleine Verbesserungen vornehmen,
- die die Arbeit mit diesem Programm etwas erleichtern :
-
-
- a) Bei den Calc-Variablen wird zwischen Groß- und Kleinschreibung
- unterschieden. Abhilfe schaffen folgende Ergänzungen :
-
- Man fügt in CALCUTIL jeweils zwischen die Zeilen 96 und 97 bzw.
- 111 und 112 die folgende ein :
-
- For i := 1 to Length(id) do id(.i.) := upcase(id(.i.));
-
- b) Eine Eingabe einer Real-Zahl, die betragsmäßig kleiner als 1
- ist, erfordert immer eine führende 0. Man ergänze daher in CALC
- Zeile 149 zu :
-
- 149 : '0'..'9','.' : BEGIN Getnumber; ...
-
- c) PI und E sind als Basis einer Exponentialfunktion nicht
- erlaubt. Man ergänze in CALC Zeile 245 zu :
-
- 245 : IF LastSymbol in (.Calc-Pi,Calc_e,Calc_Const ...
-
-
-
- Nun zu den Änderungen, die sich für die Anpassung an DISCUSS als
- günstig erwiesen haben :
-
- a) Um bei Fehleingaben den Bildschirm zu säubern, wurden die
- Prozeduren "Error" in CALC und "CalcError" in CALCUTIL wie folgt
- ergänzt :
-
-
- PROCEDURE Error (ErrPos: INTEGER; ErrMsg: Calc_String);
-
- BEGIN
- IF NOT ParsError THEN
- BEGIN
- WriteLn; Write('*** Fehler in CompileExpression:');
- ClrEol; WriteLn;
- Write (' ',Expr);ClrEol; Writeln;
- Write (' ':ErrPos, '^'); ClrEol; Writeln;
- Write (ErrMsg, '!'); Clreol; Writeln; ClrEol;
- WriteLn; ClrEol
- END;
- ParsError := TRUE; Symbol := Calc_Err
- END;
-
-
- PROCEDURE CalcError (ErrNum: INTEGER; Message: ErrString);
-
- BEGIN
- WriteLn; Write('*** Laufzeitfehler:'); Clreol; WriteLn;
- Write(' ');
- CASE ErrNum OF
- 1: Write('Gleitkommaueberlauf');
- 2: Write('Division durch Null');
- 3: Write('Argumentfehler in ');
- ELSE ;
- END;
- Write(' ', Message, ' !'); ClrEol;
- WriteLn; ClrEol;
- WriteLn; ClrEol;
- CalcResult := FALSE; (* Auswertung abbrechen ! *)
- END;
-
-
-
- 3) CALCDERI - Peter Engels
-
- Die Routinen zur symbolischen Differentiation wurden von mir
- eingefügt. Hier noch einmal eine kurze Funktionsbeschreibung der
- neu hinzugekommenen Routinen :
-
- Die von "Calc" erzeugten Programme werden zunächst in die UPN-
- Notation übersetzt und anschließend als linear verkettete Liste im
- Speicher abgelegt. Dabei ist das erste Element der Liste ein
- Listenanker, der nur den Verweis auf den ersten gültigen Eintrag
- in der Liste enthält.
-
- Ein Beispiel : f(x) = (7+x)*x^3 wird in folgender Reihenfolge
- abgespeichert : @ 7 x + x 3 ^ * . Das @-Zeichen soll dabei den
- Listenanker darstellen.
-
- Da man eine solche linear verkettete Liste nur von Anfang bis Ende
- durchlaufen kann, ist folglich der für die Differentiation als
- erstes bedeutsame Operator '*' erst bekannt, wenn man über dessen
- Operanden bereits hinweggelesen hat. Daher schien es mir für die
- gestellte Aufgabe am einfachsten, zunächst die Liste vollständig
- zu invertieren, so daß der letzte Operator als erstes Element der
- Liste erscheint. Diese Aufgabe übernimmt die Funktion "invert",
- die das vorliegende Calc-Programm invertiert, das für das gewählte
- Beispiel danach so aussieht : @ * ^ 3 x + x 7 . Damit ist der
- erste Operator eine Multiplikation, und die Ableitungsregel
- bekannt. Man beachte, daß bei dieser Invertierung natürlich auch
- die Reihenfolge der Operanden vertauscht worden ist, was
- insbesondere bei nicht kommutativen Operationen zu berücksichtigen
- ist.
-
- Die Aufgabe des Ableitens wird von der Prozedur "calcderivation"
- übernommen, deren Kernstück die rekursive Prozedur "derive" ist,
- und die in dem Include_File "CALCDERI.PAS" enthalten ist.
-
- Nachdem also nun der erste Operator bekannt ist, müssen als
- nächstes die beiden zugehörigen Operanden gefunden werden. Der
- erste Operand ist sofort klar : er beginnt mit dem nächsten
- Listenelement und wird im Programm als Zeiger pptra gespeichert.
- Der Anfang des zweiten Operanden ist nicht unmittelbar anzugeben,
- da man nicht weiß, wie lang der erste Operand ist. Er wird mit
- Hilfe der Funktion "endof" gesucht, die ebenso wie "invert" global
- im Programm definiert ist, da diese Funktionen mehrfach von
- verschiedenen Programmteilen benutzt werden. endof liefert einen
- Zeiger zurück, der auf den letzten Eintrag der zu pptra gehörenden
- Anweisung zeigt. pptrb ist dann der Zeiger auf den nächsten
- Eintrag. Damit müßte die Ableitungsregel für unser Beispiel dann
- so aussehen :
-
- newop(calc_add);
- newop(calc_mul);
- derive(pptrb); <-- rekursiver Aufruf
- push(pptra);
- newop(calc_mul);
- derive(pptra); <-- rekursiver Aufruf
- push(pptrb);
-
- Das ist genau die Umsetzung der bekannten Produktregel.
-
- Die Prozedur "newop" fügt ein neues Listenelement mit der angege-
- benen Anweisung an die Liste für die Ableitung an, die Prozedur
- "push" fügt den gesamten zum Zeiger gehörenden Ausdruck an die
- Liste an. Als nächstes wird also nun pptrb differenziert. Das ist
- in unserem Beispiel die Anweisung : + x 7 . Die Operation ist eine
- Addition, also :
-
- newop(calc_add);
- derive(pptra);
- derive(pptrb);
-
- Zu pptra gehört der Eintrag x, der die Variable darstellt :
-
- newconst(1.0);
-
- und zu pptrb gehört der Eintrag 7, also :
-
- newconst(0.0);
-
- Damit ist die Ableitung des Zeigers pptrb der Multiplikation
- erledigt, so daß jetzt pptra in die Liste eingetragen wird. Nun
- muß pptra differenziert werden, wozu der Eintrag ^ 3 x gehört. Es
- handelt sich in diesem Beispiel also um eine Potenz. Da es zur
- Ableitung von Exponential- und Potenzfunktionen in Heft 7/87 in
- Zusammenhang mit dem in (2) veröffentlichten Programm bereits zu
- einem kritischen Leserbrief gekommen ist, will ich hierzu etwas
- Mathematik bringen und die Ableitungsregel für jeden nur denkbaren
- Fall angeben :
-
- (f^g)' = (exp(g*ln(f))' = (g'*ln(f) + g*f'/f)*(f^g)
-
- Mit dieser Regel läßt sich von 2^3 bis x^x alles differenzieren,
- da alle anderen Ableitungsregeln nur Spezialfälle dieser einen
- Regel sind, die einzig von der Kettenregel lebt. Also :
-
- newop(calc_mul);
- newop(calc_pow);
- push(pptra);
- push(pptrb);
- newop(calc_add);
- newop(calc_dvd);
- push(pptrb);
- newop(calc_mul);
- push(pptra);
- derive(pptrb);
- newop(calc_mul);
- derive(pptra);
- newop(calc_ln);
- push(pptra);
-
- Da beide Operanden keine neuen Operationen enthalten, ist die
- Ableitung beendet. Die Prozeduren "newop", "newconst" und "push"
- sind so angelegt, daß sie gleich eine Liste erzeugen, die
- unmittelbar von Calc ausgewertet werden kann. Die Liste für unser
- Beispiel sieht nun so aus :
-
- @ 7 x + 3 ln 0 * 1 3 * x / + x 3 ^ * * x 3 ^ 0 1 + * +
-
- Nach ähnlichen Regeln lassen sich alle differenzierbaren Funktio-
- nen mit calcderivation ableiten. Bleibt die Frage, was differen-
- zierbare Funktionen sind. Mathematisch gesehen, ist diese Frage
- leicht zu beantworten, im vorliegenden Fall muß man sich die Ant-
- wort aber etwas schwerer machen. Calc verfügt über eine ganze Rei-
- he von Funktionen, die urspünglich nur auf N oder Z leben und
- damit natürlich nicht differenzierbar sind. Durch die Art der
- Implementation in Calc sind diese Funktionen aber auf ganz R fort-
- gesetzt worden. Auf R-Z sind diese Funktionen lokal konstant und
- somit differenzierbar, die Ableitung ist Null. Ich habe das in
- meinem Programm in dieser Form berücksichtigt, wer hier strenger
- sein will, bricht mit einer Fehlermeldung ab. Eine weitere Sonder-
- stellung nimmt die Funktion ABS(X) ein. Sie ist bis auf Null über-
- all differenzierbar, ihre Ableitung ist SIGN(X). Die Funktion
- SIGN(X) muß dazu in "MATHFUNC.PAS" und Calc aufgenommen werden.
-
- Zurück zum Beispiel. Man sieht sehr deutlich, daß bei sturer
- Anwendung der Ableitungsregeln zwar richtige Ableitungsterme ent-
- stehen, die allerdings reichlich viel Müll in Form von toten Zwei-
- gen und überflüssigen Operationen enthalten.
-
- Diesem Müll wird vom Programm an zwei Stellen begegnet. Zunächst
- werden schon in calcderivation Konstanten gesondert behandelt, so
- daß aus x^3 bei der Ableitung auch 3*x^2 wird. Danach wird die
- Liste von der Funktion "calcsimplify" weiterbearbeitet. Hier wer-
- den Terme wie 2*3 berechnet und durch das Ergebnis ersetzt. Außer-
- dem werden Ausdrücke, die die Konstanten 0.0 oder 1.0 enthalten
- entsprechend vereinfacht. Extreme Schwierigkeiten bereitet bei der
- Vereinfachung das Kommutativgesetz, von dem ich bislang nur einige
- Spezialfälle berücksichtigen konnte.
-
- Insgesamt arbeitet calcsimplify recht zufriedenstellend, wenn auch
- einige Sonderfälle gar nicht oder nur unzureichend berücksichtigt
- worden sind. Hier ist noch sehr viel Platz für eigene Ideen,
- Ergänzungen und auch für Verbesserungen.
-
- Mit diesen Routinen lassen sich bereits numerische Berechnungen
- durchführen, die eigentliche Auswertung wird dabei wieder von Calc
- übernommen. Da ich davon ausgegangen bin, daß alle Listen entweder
- von calc oder von calcderivation erstellt worden sind, habe ich an
- keiner Stelle der Routinen einen Syntax-Check vorgesehen.
-
- Wer aber auch den Ableitungsterm auf dem Schirm sehen oder auf
- Diskette speichern will, muß das Calc_Programm wieder in AOS-
- Schreibweise zurückübersetzen. Diese Aufgabe übernimmt die eben-
- falls rekursive Prozedur "calcaos". Diese Routine ist für die
- Bildschirmausgabe konzipiert, sie kann durch kleine Änderungen
- aber auch für die Ausgabe in einen String benutzt werden, der dann
- zur weiteren Bearbeitung zur Verfügung steht. Hierbei besteht
- allerdings die Gefahr, daß insbesondere bei höheren Ableitungen
- leicht das Limit von 255 Zeichen überschritten wird.
-
-
- Zur Handhabung der Routinen :
-
- Alle drei Routinen sind so angelegt, daß sie unmittelbar mit Calc-
- Programmen arbeiten können und auch wieder für Calc lesbare
- Programme erzeugen.
-
- 1) Der Prozedur calcsimplify wird als Parameter der Zeiger auf das
- zu vereinfachende Calc-Programm übergeben. Geht etwas schief, weil
- z.B. ln(-10) berechnet werden muß, so ist CalcResult = False und
- das Calc-Programm verloren!
-
-
- 2) Die Funktion calcderivation benutzt drei Parameter : zunächst
- das Calc-Programm, dann die Variablenliste und schließlich die
- Variable, nach der differenziert werden soll als String. Sie
- liefert als Wert den Zeiger auf die Ableitung zurück. Geht etwas
- schief, so ist das Ergebnis Nil und CalcResult = False.
-
- 3) Der Prozedur calcaos werden das Calc-Programm und die
- Variablenliste übergeben. Reicht der Speicher auf dem Heap nicht
- aus, erfolgt keine Ausgabe.
-
- Abschließend noch ein Wort zum Laufzeitverhalten. Vergleicht man
- die Laufzeit der von calcderivation erzeugten Programme mit der
- Laufzeit von numerisch berechneten Ableitungen, so zeigt sich kein
- einheitliches Bild. Es gibt Situationen, in den calcderivation bis
- zu 70 mal schneller als die numerische Variante ist, andererseits
- aber auch solche, bei denen die Auswertung der symbolischen
- Ableitung länger braucht. Das hängt stark von dem vorliegenden
- Funktionsterm und der Ordnung der Ableitung ab. In jedem Falle
- aber liefert die symbolische Ableitung ein genaueres Ergebnis als
- die numerische Variante.
-
-
- Im Zusammenhang mit CALCDERI wurde auch die Funktionensammlung
- MATHFUNC erweitert bzw. geändert. Da die Ableitung von ABS(X) im
- wesentlichen SIGN(X) ist, wurde diese Funktion ergänzt :
-
- FUNCTION sign(x : real) : real;
- BEGIN
- if x > 0.0 then sign := 1.0
- else if x < 0.0 then sign := -1.0
- else sign := 0.0
- END;
-
- Außerdem erwies sich die Funktion x_hoch_y als sehr fehleranfäl-
- lig. Leider kann ich hier nicht auf Zeilennummern verweisen, da
- die bereits geänderte Fassung nicht abgedruckt wurde. Daher
-
- FUNCTION x_hoch_y (x, y: REAL): REAL;
-
- VAR ganz_y: INTEGER;
-
- BEGIN
- IF (x <> 0.0) OR (y <> 0.0) THEN
- IF x > 0.0 THEN
- if abs(y*ln(abs(x))) > 86.0 THEN
- CalcError(3,'x_hoch_y(x,y): ABS(y*ln(X)) > 86.0')
- else x_hoch_y := Exp(y * Ln(x))
- ELSE
- BEGIN
- ganz_y := Trunc(y);
- IF ABS(y) > ABS(ganz_y) THEN
- CalcError(3, 'x_hoch_y(x,y):'+
- ' nur ganzzahlige Exponenten zulaessig')
- ELSE
- IF x <> 0.0 THEN
- IF (ganz_y MOD 2) = 0 THEN
- if abs(y*ln(abs(x))) > 86.0 THEN
- CalcError(3,'x_hoch_y(x,y): ABS(y*ln(X)) > 86.0')
- else x_hoch_y := Exp(Ln(ABS(x)) * y)
- ELSE
- if abs(y*ln(abs(x))) > 86.0 THEN
- CalcError(3,'x_hoch_y(x,y): ABS(y*ln(X)) > 86.0')
- else x_hoch_y := -Exp(Ln(ABS(x)) * y)
- (* ungerader Exponent *)
- ELSE
- x_hoch_y := 0
- END
- ELSE
- x_hoch_y := 1.0 (* 0^0 := 1 *)
- END;
-
-
- Die neu eingefügte Funktion 'Sign' muß natürlich CALC mitgeteilt
- werden. Da ich darüberhinaus die INT-Funktion vermißt habe, wurde
- diese gleich mit implementiert :
-
- a) in CALCTYPE zwischen Zeile 28 und 29 einfügen :
-
- 28' : Calc_Sig, Calc_Int,
-
- b) in CALCVAR zwischen Zeile 46 und 47 einfügen :
-
- 46': 'SIGN' , 'INT' ,
-
- c) in CALC zwischen Zeile 391 und 392 einfügen :
-
- 391 ': Calc_sig : x := sign(x);
- 391'': Calc_int : x := int(x);
-
-
- Wer weitere Funktionen in CALC implementieren will, muß beachten,
- daß die neuen Funktionen in den Listen nicht einfach hinten ange-
- hängt werden dürfen, sondern in der Mitte der Liste eingebunden
- werden müssen. Dazu bieten sich die oben angegebenen Stellen an.
-
-
-
- 4) DISCUSS - Karsten Gieselmann
-
- Hier waren selbstverständlich die meisten Änderungen notwendig,
- die ich aufgrund ihrer Fülle nicht alle im Detail ansprechen kann.
-
- Die Zeilenangaben beziehen sich auf den Nachtrag in Heft 8/87.
-
- Auch hier zunächst die Bugs :
-
- 1) In FLEX.INC muß Zeile 30 lauten :
-
- 30: if abs(fn(x,1)) > eps then
-
- 2) In SOLVE.INC sollte Zeile 36 lauten :
-
- 36: until (abs(x-lastx) < eps * abs(x)) or (y = lasty);
-
- -----------------------------------------------------------------
-
- Nun zu den sonstigen Änderungen :
-
- a) In DISCUSS müssen die einzelnen IncludeFiles hinzugefügt
- werden, sowie weitere erforderliche globale Variable deklariert
- werden (s. Listing)
-
- b) In DEF entfallen die Zeilen 11 bis 13 und 21 bis 24. Außerdem
- habe ich wegen der zu großen Häufigkeit von Laufzeitfehlern die
- Rechengenauigkeit wieder auf den ursprünglichen Wert reduziert.
-
- c) Das File FUNCTION.INC enthält nun zusätzlich die Prozedur zum
- Einlesen und Ausgeben des Funktionsterms, sowie die geänderte
- Routine zur Funktionswertberechnung mittels CALC. Bei der Eingabe
- von f(x) ist zu beachten, daß das ';' am Ende vom Programm automa-
- tisch ergänzt wird, und folglich nicht mit eingegeben werden muß.
- (s. Listing)
-
- d) Das File SOLVE.INC hat bereits in PASCAL Heft 3 einige Verbes-
- serungen erfahren, die allerdings ebensoviel geschadet, wie
- genutzt haben. Die ursprünglich veröffentlichte Version war nicht
- in der Lage, Nullstellen geraden Grades von f zu finden, so daß
- die Prozedur Solve dahingehend erweitert wurde. Dann ermittelt
- diese Prozedur aber auch die Nullstellen gerader Ordnung der
- Ableitungen, um Extrem- und Wendestellen zu finden, was natürlich
- Unfug ist, denn solche ausgezeichneten Punkte liegen nur bei Null-
- stellen ungerader Ordnung vor (Vorzeichenwechsel der entsprechen-
- den Ableitung ist hinreichendes Kriterium!!!). Warum also diese
- Nullstellen gerader Ordnung mitberechnen - das kostet nur unnötig
- Zeit! Ändert man das ab, so kann sogar auf die dritte Ableitung
- vollständig verzichtet werden, was in dem Fall der symbolischen
- Differentiation wertvollen Speicherplatz spart. Insgesamt werden
- durch diese Änderung alle ausgezeichneten Punkte wieder wesentlich
- schneller gefunden. Sattelpunkte findet der Rechner dann natürlich
- nur noch im Zusammenhang mit der Wendestellenbestimmung.
-
- e) Das File FORMULA.INC ist neu hinzugekommen und enthält die Pro-
- zeduren zur Eingabe von numerischen Werten, die ebenfalls von CALC
- ausgewertet werden. Zur Eingabe des Strings wird auch hier die
- Prozedur "ReadStr" verwendet. Diese Prozedur verlangt allerdings
- eine genaue Angabe, an welcher Bildschirmposition die Eingabe
- erfolgen soll. Das kann jedoch bei den hier vorkommenden Fällen
- nicht sicher angeben werden, da man mit Eingabefehlern rechnen
- muß, die zu Fehlermeldungen führen und jede Eingabemaske zerstö-
- ren. Man ist daher gezwungen, alle Eingaben zeilenweise vorzuneh-
- men, wobei sich die Zeile aus der jeweiligen Bildschirmposition
- des Cursors ergibt. Kritisch ist daher die y- Position, die x-
- Position kann immer mit 1 angenommmen werden. Das Problem ist
- sofort gelöst, wenn die PASCAL-Version auf dem verwendeteten
- Rechner über eine WhereY-Prozedur verfügt, wie sie in der IBM
- Version vorgesehen ist. Für Apple II und Schneider Rechner wurde
- eine entsprechende Prozedur in PASCAL veröffentlicht. Da aber
- nicht für jeden Rechner eine derartige Prozedur implementiert ist,
- mußte ich in die Trickkiste greifen : Als y-Position wird einfach
- 0 angegeben. Das führt dazu, daß zumindest in Turbo 3.0 ein Gotoxy
- einfach auf die gegenwärtige Zeile positioniert. Überschreitet man
- dann allerdings das Zeilenende, spielt ReadStr "verrückt", so daß
- die Eingabezeile auf 30 Zeichen beschränkt wurde. (s. Listing)
-
- f) Die Funktion INTEGRAL wurde so geändert, daß nun zusätzlich,
- neben der Flächenberechnung, auch die Berechnung von Rotations-
- volumen, Mantelfläche und Bogenlänge erfolgen kann.
-
- g) in allen Files wurden die Sonderzeichen zur Darstellung der
- deutschen Umlaute entsprechend ersetzt, da sie zu Schwierigkeiten
- führen können. So ist unter CP/M ein File, daß ein großes UE
- enthält nicht mehr lesbar, da diesem Zeichen der Code H9A
- entspricht, was im Turbo-Pascal-Editor der File-Ende Markierung
- gleichkommt!!! Ich konnte mir nur mit einem Disk-Editor helfen,
- indem ich diese Bytes auf einen lesbaren Wert (H30) gesetzt habe.
- (Vielleicht sollte man in Zukunft grundsätzlich auf derartige
- Spezialitäten verzichten !)
-
- h) das File INTERVAL wurde so geändert, daß es mit der Prozedur
- "FormulaLn" zusammenarbeitet.
-
- i) Die Files DRAW und DRAW2 wurden ebenfalls geändert, um trotz
- auftretender Laufzeitfehler einen ordentlichen Funktionsgraphen zu
- garantieren. Außerdem wurde die Möglichkeit geschaffen, neben dem
- Graphen von f auch die Graphen der ersten beiden Ableitungen zu
- zeichnen. Darüberhinaus wurden die Files DRAW3 und DRAW4 erstellt,
- die an die Hardware der Apple II+/e Rechner angpaßt sind. Diese
- Prozeduren benutzen die in (7) abgedruckten Hilfsroutinen zur
- Steuerung der 6502 CPU und erfordern daher ohnehin Turbo-Pascal
- als Pascalversion, so daß die hier ebenfalls Turbo-spezifische
- Variable "ConOutPtr" keine zusätzliche Einschränkung mit sich
- bringt. Diese Variable muß deshalb benutzt werden, weil nach
- meinen (spärlichen) Erfahrungen mit Apple IIe Rechnern eine Bild-
- schirmausgabe im Grafikmodus zu merkwürdigen Resultaten führt.
- Daher wurde vorsorglich jegliche Bildschirmausgabe unterbunden,
- die im Falle eines Laufzeitfehlers auftreten würde.
-
- j) Das File MENU3 wurde vollständig überarbeitet. Insbesondere
- wurden die nichtssagenden Ziffern zur Programmauswahl durch leicht
- zu merkende Buchstaben ersetzt. MENU3 ruft auch die neue Prozedur
- HELLO auf, die den Programmnamen und einen Copyright- Vermerk
- ausgibt. Gibt man auf die Aufforderung "Weiter mit beliebiger
- Taste..." ein gültiges Zeichen ein, so wird das Menu übersprungen,
- und gleich der entsprechende Programmpunkt angewählt.
-
-
-
- Abschließend noch ein Tip speziell für Turbo 3.0:
-
-
- Einen Programmabsturz habe ich zwar nur bei ganz unüberlegten
- Eingaben beobachtet, aber leider häufen sich bei bestimmten
- Funktionstermen die Fehlermeldungen, insbesondere in der Funktion
- x_hoch_y. Es empfiehlt sich daher alle Anweisungen, die eventuell
- auftretende RunTime-Fehler abfangen, aus dem Programm zu entfer-
- nen, und stattdessen den entsprechenden Error_Handler (4) oder (5)
- einzubinden. Dadurch wird das Programm natürlich auch weiter
- beschleunigt.
-
-
- Nun noch ein Paar Worte zur Implementation auf Apple Rechnern. Da
- mein Apple Rechner mit einer 8MHz IBS AP22 Karte und einer 304kB
- großen Ram-Floppy ausgestattet ist, war es für mich überhaupt kein
- Problem das vorliegende Programm zu entwickeln und zu compilie-
- rern. Was aber tun, wenn der gute alte Apple nicht "getunt" ist?
-
- Nun, Mindestvoraussetzung sind in jedem Fall zwei Laufwerke sowie
- eine normale 2MHz SoftCard und die CP/M Version 2.23, die immerhin
- nochmals 4kB mehr Speicherplatz bereit stellt, als die Version
- 2.20. Da das Programm die zweite Grafikseite benutzt, die bei der
- SoftCard in der TPA liegt, muß das Programm mit einer Startadresse
- von H5000 auf Diskette compiliert werden. Dadurch verringert sich
- natürlich der freie Speicherplatz im RAM erheblich, so daß man
- zusätzlich auf die Overlay-Technik zurückgreifen muß. Wer mit der
- Version 2.20(B) arbeitet, muß entweder noch mehr Overlays benut-
- zen, wozu dann aber die Prozedurreihenfolge gut überlegt sein muß,
- oder er muß das Programm abspecken, indem z.B. auf die symbolische
- Differentiation verzichtet wird. Leider ist es unmöglich sowohl
- Turbo-Pascal, alle Quellfiles und das Compilat auf einer Diskette
- zu halten. Man fertige daher zwei Disketten an :
-
- Disk A : enthält TURBO, DISCSOFT.PAS und CALC.PAS; auf dieser
- Diskette entsteht dann auch das Compilat
-
- Disk B : enthält die restlichen Include Files
-
- Die Bezeichnungen A und B beziehen sich auf das verwendete Lauf-
- werk. Trotz dieser Maßnahmen bleibt der freie Speicherbereich für
- Calc-Programme reichlich klein, so daß bei einigen Funktionen die
- dritte Ableitung nicht mehr berechnet werden kann. Das File
- DISCSOFT ist eine Variante des Files DISCUSS und speziell für
- diesen Fall vorbereitet, d.h. hierin sind alle OVERLAY- Anweisun-
- gen enthalten und die Include-Files werden in Laufwerk B: erwar-
- tet.
-
-
- Literatur :
-
- (1) Gieselmann, Karsten
- Kurvendiskussion
- PASCAL Heft 11/86 S.29ff
-
- (2) Schlöter, Martin
- Symbolverarbeitung in Prolog
- PASCAL Heft 4/87 S.58ff
-
- (3) Gieselmann, Karsten ; Ceol, Michael
- CALC - ein mathematischer Compiler
- PASCAL Heft 8/87 S.52ff
-
- (4) Gieselmann, Karsten
- Eigene Fehlerbehandlung in Turbo-Pascal-Programmen
- PASCAL Heft 9/87 S.94
-
-
- (5) Engels, Peter
- Eigene Fehlerbehandlung in Turbo-Pascal-Programmen unter
- CP/M 80
- PASCAL Heft 12/87 S.88
-
-
- (6) Gieselmann, Karsten
- Komfortable Stringeingabe
- PASCAL Heft 3/87 S.82,83
-
-
- (7) Engels, Peter
- Turbo-Pascal und die 6502 des Apple II
- PASCAL Heft 12/87 S.82,83
-
-
-
- Sicherlich sind noch viele Ergänzungen möglich, von denen ich
- abschließend noch einige vorstellen möchte, die dem Anwender als
- Anregung dienen mögen :
-
-
- - Hardcopy der erzeugten Grafik und der Berechnungen
- - Beschriften der Grafik
- - Abspeichern und Einlesen von Funktion und Graph auf/von Disk
-
- Wie man sieht, sind diese nützlichen Erweiterungen leider allesamt
- sowohl von der Hardware, als auch von der verwendeten Pascal-
- Version abhängig, so daß es wenig sinnvoll ist, diese Erweiterun-
- gen generell einzubauen.
-
-
- Peter Engels