← ST-Computer 03 / 1988

VAL in PASCAL - Umwandlung von Strings in numerische Variablen

Programmierpraxis

Als ich vor einiger Zeit anfing Mathematikprogramme zu schreiben, mußte ich feststellen, daß mir CCD Pascal+ nicht alle nötigen Hilfsmittel zur Verfügung stellte. Ich setzte mich also hin und schrieb zunächst eine VAL-Routine, wie sie mir von Basic aus bekannt war. Diese Routine ist als Include-Datei ausgelegt.

Die dazugehörige Datei hat den Namen VALUE.INC. Sie macht aus einem übergebenen Stringwert einen Realwert, falls dies möglich ist. Sollte eine Umwandlung nicht möglich sein, so enthält die global zu deklarierende Variable ‘Fehler’, die vom Typ Boolean sein muß, den Wert ‘FALSE’, ansonsten ist sie ‘TRUE’. Ich bin bei dieser Routine einige Kompromisse in puncto Codelänge und Rechengeschwindigkeit eingegangen, wodurch sich eine Zeit von ca. 10 ms für die Umwandlung eines 13-stelligen Strings(-123.4567E-16) ergibt. Das Prinzip der Umwandlung beruht auf der systematischen Zerpflückung des Strings und ist an und für sich leicht zu durchblicken. Das Programm CHR_REAL.PAS ist ein kleines Demoprogramm für VALUE.INC und wandelt die eingegebenen Strings in Realwerte um, bis man als String den Wert ‘ENDE’ eingibt. In das Programm CHR_REAL ist noch eine Procedure eingebaut, die alle Kleinbuchstaben in Großbuchstaben umwandelt, wodurch es egal wird, wie die Tastatur gerade eingestellt ist. Die Value-Routine wird folgendermaßen angewendet:

VALUE.INC

{Value-Routine von Klaus Wilczek für ST Pascal+} {wanden von Strings in Realwerte} FUNCTION VAL(X:STRING):REAL; VAR I1,12:STRING; TESTWORD:STRING; N,LE,El,R,EX,KO,VM,VE:INTEGER; M,I3,I4:REAL; PROCEDURE INT(R:INTEGER);{Erstellen der Zahlenwerte} BEGIN IF FEHLER<>FALSE THEN BEGIN I3:=0; IF KO=0 THEN FOR R:=1 TO LENGTH(I1) DO I3:=I3*10 + ORD(I1[R])-48 ELSE BEGIN FOR R:= 1 TO (KO-1) DO I3:=I3*10 + ORD (11[R])-48; 14:=0; FOR R:= LENGTH (II) DOWNTO (KO+1) DO I4:=(I4+ORD(I1[R])-48)/10; I3:=I3+I4; END; END; END; PROCEDURE SYNTAXCHECK(TESTWORD:STRING;KO:INTEGER); {Syntaxkontrolle} VAR I,W:INTEGER; BEGIN IF KO >0 THEN DELETE (TESTWORD, KO, 1) ; IF TESTWORD='' THEN FEHLER:=FALSE ELSE FOR I := 1 TO LENGTH(TESTWORD) DO BEGIN W:=ORD(TESTWORD[I])-48; IF (W<0) OR (W>9) THEN FEHLER:=FALSE; END; END; BEGIN FEHLER:=TRUE; REPEAT {löschen der Leerstellen} N:=POS(' ',X); IF (N=1) THEN DELETE(X, N, 1) ; UNTIL (N>1) OR (N=0) ; IF (POS (' ',X)>0) THEN DELETE (X, N, LENGTH (X)-N+l) ; IF POS('-',X)=1 THEN {suchen der Vorzeichen} BEGIN VM:=-1; DELETE(X,1,1) END ELSE VM:=1; KO := POS('.', X) ; EX := POS('E' ,X) ; IF EX = 0 THEN BEGIN {herstellen von Mantisse und Exponent} I1:=COPY(X,1,LENGTH(X)); I2:='0'; END ELSE BEGIN I1:=COPY(X,1, EX-1) ; I2:=COPY(X,EX+1,LENGTH(X)-EX}; VE:=1; IF (I2[1]='-') THEN VE:=1; IF (VE=-1) OR (I2[1]='+') THEN DELETE (I2, 1,1); END; TESTWORD:=CONCAT(I1,I2); SYNTAXCHECK(TESTWORD,KO); INT(R); M:=I3; I1:=I2; K0:=0; INT(R); E1:=TRUNC(I3); IF VE = 1 THEN VAL:= (M*VM) *PwrOfTen (E1) {herstellen der Realwerte} ELSE VAL: = (M*VM) * (1 /PwrOfTen (E1) ) ; IF FEHLER=FALSE THEN VAL:=0; END; dest := val(source); dest - Real source - String

Die Routine führt vor der Umwandlung eine grobe Syntaxkontrolle durch, wodurch die Gefahr eines eventuell auftretenden Fehlers sehr gering sein dürfte. Wird die Datei in irgendwelche Programme eingefügt, lassen sich z.B. Eingaben schnell auf ihre Richtigkeit prüfen, wodurch sich viele Fehler vermeiden lassen.

Weiterhin war ich seit einiger Zeit auf der Suche nach einem guten Algorithmus für einen ()Funktionsinterpreter. Als ich dann auf die Zeitschrift MC, Ausgabe 5/87 aufmerksam gemacht wurde, beschloß ich, den dort in Turbo-Pascal geschriebenen FI() in ST Pascal+ umzuschreiben. Hierbei zeigten sich die Differenzen zwischen den beiden Programmiersprachen doch recht deutlich, es mußte jedoch nicht allzuviel umgeändert werden. Das Resultat war ein recht gut und schnell funktionierender FI. Hier wird ebenfalls die VAL-Routine benutzt, die ja ansonsten bei Turbo-Pascal bereits zur Verfügung steht. Die Aufgabe des FI ist es, einen beliebigen Funktionstherm als String einzulesen, diesen zu analysieren, auf Syntaxfehler zu kontrollieren und zuzulassen, daß beliebige X-Werte eingesetzt werden können bzw. die dazugehörigen Funktionswerte errechnet werden.

Ein Fl muß die üblichen Rechenregeln beherrschen:

  • Punkt- vor Strichrechnung
  • Klammern zuerst
  • von links nach rechts

Hieraus ergibt sich eine Hierarchie der Rechenoperatoren:

  1. Klammern und Funktionen
  2. Potenz A
  3. Vorzeichen +,-
  4. Punktrechnung *, /
  5. Strichrechnung +,-

Aus der Funktion wird hier ein Baum gebaut, wobei die Knoten des Baumes durch Operatoren, Funktionen, Vorzeichen, Zahlen und Variablen belegt sind. Die Knoten sind als Variante Records deklariert. Der Baum wird von den höherwertigen Operationen hin zu den nieder-wertigeren durchgearbeitet. Das Programm FUNKTION.TOS erlaubt es, Funktionen einzugeben und Funktionswerte zu berechnen. Leider mußte ich feststellen, daß die mathematischen Funktionen von ST Pascal-»- sehr ungenau sind(z.B. 2A16=65535.99..), und ich will dies noch ändern. Alle bisher angebotenen Algorithmen sind aber entweder selber zu ungenau oder zu umfangreich programmiert, so daß ich es selber probieren will. Mit diesen beiden Funktionen kann bestimmt jeder, der irgendwann einmal mathematische Programme schreiben will, etwas anfangen. Die Programme sollten als TOS-Anwendungen compiliert werden, da ich auf Bildschirmaufbau keinen großen Wert gelegt habe.

CHR_REAL.PAS

{Programm zum testen von VALUE.INC geschrieben von Klaus Wilczek} PROGRAM TEST_VALUE; VAR X:STRING; FEHLER:BOOLEAN; {globale Deklaration der Variablen Fehler} {$1 VALUE.INC} PROCEDURE UPPER;{Umwandlung aller Kleinbuchstaben in Gro_buchstaben} {Die Variable (hier X) mu_ jeweils angepasst werden} VAR I:INTEGER; BEGIN FOR I:=1 TO LENGTH(X) DO IF (ORD(X{I])>96) AND (ORD(X[I])<123) THEN BEGIN INSERT(CHR(ORD(X[I])-32),X, I); DELETE(X,1+1,1); END; END; BEGIN WRITELN('Beenden des Programms mit "Ende" !'); REPEAT WRITELN;WRITE('Bitte einen Wert eingeben : '); READLN(X); UPPER;{ alle Kl.bst. werden umgewandelt in Gr.bst.} IF X<>'ENDE' THEN BEGIN WRITELN; WRITELN ( ' X als Stringwert : ', X) ; WRITELN ; WRITELN('- Umgewandelt zu einem Realwert: VAL(X)); WRITELN; WRITELN(' Fehlervariable : ',FEHLER); END; UNTIL X='ENDE';{aussteigen wenn X='ENDE'} END.

FUNKTION.PAS

PROGRAM FORMELINTERPRETER; TYPE ARTTYP = (VORZEICHEN,OPERATOR,FUNKTION,ZAHL, VARX); STRING128 » STRING[128]; OPTYP = SET OF CHAR; PTR = ^KNOTENTYP; KNOTENTYP - RECORD CASE ART:ARTTYP OF VORZEICHEN : (VON:PTR); OPERATOR : (OP:CHAR; LINKS,RECHTS:PTR); FUNKTION : (FN:STRING[6]; NEXT:PTR); ZAHL : (ZA:REAL); VARX : (X:REAL); END; VAR X,Y : REAL; WAHL : CHAR; FKT : PTR; TERM : STRING128; FEHLER : BOOLEAN; {$1 VALUE.INC} FUNCTION SUCHEOPERATOR (OPS:OPTYP; VAR K:INTEGER; TERM:STRING128):BOOLEAN; VAR KLAMMER:INTEGER;GEFUNDEN:BOOLEAN; BEGIN GEFUNDEN:=FALSE;KLAMMER:=0;K:=LENGTH (TERM)+1; REPEAT K:=K-1; IF TERM[K]=' (' THEN KLAMMER:=PRED(KLAMMER); IF TERM[K]=')' THEN KLAMMER:=SUCC(KLAMMER); IF (KLAMMER=0) AND (TERM[K]IN OPS)AND(K>1) AND(NOT(TERM[K-1]IN['E' , '^'])) THEN GEFUNDEN:=TRUE; UNTIL GEFUNDEN OR (K=1); SUCHEOPERATOR:=GEFUNDEN; END; FUNCTION SUCHEPLUSMINUS(VAR K:INTEGER;TERM:STRING128): BOOLEAN; BEGIN SUCHEPLUSMINUS:=SUCHEOPERATOR(['+','-'],K,TERM); END; FUNCTION SUCHEMALDURCH(VAR K:INTEGER;TERM:STRING128): BOOLEAN; BEGIN SUCHEMALDURCH:=SUCHEOPERATOR(['*', '/'],K,TERM); END; FUNCTION SUCHEVORZEICHEN(TERM:STRING128):BOOLEAN; BEGIN SUCHEVORZEICHEN:=(TERM[1] IN ['+', '-']) END; FUNCTION SUCHEPOTENZ(VAR K:INTEGER;TERM:STRING128): BOOLEAN; BEGIN SUCHEPOTENZ:=SUCHEOPERATOR(['^'],K,TERM); END; FUNCTION SUCHEFUNKTION(VAR K:INTEGER;TERM:STRING128): BOOLEAN; VAR F:STRING128; BEGIN SUCHEFUNKTION:=FALSE;K:=POS('(', TERM); IF K>0 THEN BEGIN F:=COPY(TERM,1,K-1); IF((F='ABS')OR(F='ARCTAN')OR(F='COS')OR(F='EXP')OR (F='FRAC')OR (F='INT')OR(F='LN')OR(F='SIN')OR(F='SQR')OR (F='SQRT')OR (F=' TAN')OR(F=''))AND(TERM[LENGTH(TERM)]=')') THEN SUCHEFUNKTION:=TRUE END; END; FUNCTION SUCHEZAHL(VAR WERT:REAL; TERM:STRING128): BOOLEAN; BEGIN WERT: VAL (TERM); SUCHEZAHL:=FEHLER; END; FUNCTION SUCHEX(TERM:STRING128):BOOLEAN; BEGIN SUCHEX: = (TERM=' X') ; END; FUNCTION FUNKTIONSANALYSE(TERM:STRING128):PTR; VAR TERMOK:BOOLEAN;FKT: PTR; PROCEDURE BAUEBAUM( VAR KNOTEN:PTR; TERM:STRING128); VAR WERT:REAL; K:INTEGER; VZ:CHAR; BEGIN IF TERMOK AND (LENGTH(TERM)>0) THEN IF SUCHEPLUSMINUS(K,TERM) THEN BEGIN NEW(KNOTEN); KNOTEN^.ART:=OPERATOR; KNOTEN^.OP:=TERM[K]; BAUEBAUM(KNOTEN^.LINKS ,COPY(TERM,1,K-1)); BAUEBAUM(KNOTENA.RECHTS, COPY(TERM,K+1,LENGTH(TERM)-K)); END ELSE IF SUCHEMALDURCH (K, TERM) THEN BEGIN NEW(KNOTEN); KNOTEN^.ART:=OPERATOR; KNOTEN^.OP:=TERM[K]; BAUEBAUM(KNOTEN^.LINKS ,COPY(TERM,1,K-1)); BAUEBAUM(KNOTEN^.RECHTS, COPY(TERM,K+1,LENGTH(TERM)-K)); END ELSE IF SUCHEVORZEICHEN (TERM) THEN BEGIN VZ:=TERM[1]; DELETE(TERM,1,1); CASE VZ OF '+': BAUEBAUM(KNOTEN, TERM); '-': BEGIN NEW(KNOTEN); KNOTEN^.ART:VORZEICHEN; BAUEBAUM(KNOTEN^.VON, TERM); END; END END ELSE IF SUCHEPOTENZ (K, TERM) THEN BEGIN NEW(KNOTEN); KNOTEN^.ART:=OPERATOR; KNOTENA.OP:=TERM[K]; BAUEBAUM(KNOTEN^.LINKS ,COPY(TERM,1,K-1)); BAUEBAUM(KNOTEN^.RECHTS, COPY(TERM,K+1,LENGTH(TERM)-K)); END ELSE IF SUCHEFUNKTION (K, TERM) THEN BEGIN NEW(KNOTEN); KNOTEN^.ART:^FUNKTION; KNOTEN^.FN:=COPY(TERM,1,K-1); BAUEBAUM(KNOTEN^.NEXT, COPY(TERM,K+1,LENGTH(TERM)-1-K)); END ELSE IF SUCHEZAHL (WERT, TERM) THEN BEGIN NEW(KNOTEN); KNOTEN^.ART:=ZAHL; KNOTEN^.ZA:=WERT; END ELSE IF SUCHEX (TERM) THEN BEGIN NEW(KNOTEN); KNOTEN^.ART:=VARX; END ELSE TERMOK:=FALSE ELSE TERMOK:=FALSE; END; PROCEDURE UPPER; VAR I:INTEGER; BEGIN FOR I:=1 TO LENGTH (TERM) DO IF (ORD(TERM(I])>96) AND (ORD(TERM[I])<123) THEN BEGIN INSERT(CHR(ORD(TERM[I])-32),TERM,I); DELETE(TERM,1+1,1); END; END; BEGIN TERMOK:=TRUE; UPPER; BAUEBAUM(FKT,TERM); IF NOT TERMOK THEN FUNKTIONSANALYSE:=NIL ELSE FUNKTIONSANALYSE:=FKT; END; FUNCTION FUNKTIONSBERECHNUNG(FKT:PTR;X:REAL) :REAL; CONST MAXREAL=1.0E30; MINREAL=1.0E-30; VAR WERTOK:BOOLEAN; FUNCTION WERT(ZEIGER:PTR):REAL; VAR TEST,FX:REAL; BEGIN FX:=X; IF WERTOK THEN WITH ZEIGER^ DO CASE ART OF VORZEICHEN: WERT:=-WERT(VON); OPERATOR:CASE OP OF '+':WERT:=WERT(LINKS)+WERT(RECHTS); '-':WERT:=WERT(LINKS)-WERT(RECHTS); '*':WERT:=WERT(LINKS)*WERT(RECHTS); '/': BEGIN TEST:=WERT(RECHTS); IF ABS(TEST)>MINREAL THEN WERT:=WERT(LINKS)/TEST ELSE WERTOK:=FALSE ; END; '^':BEGIN TEST:=WERT(LINKS); IF TEST>MINREAL THEN WERT:=EXP(WERT(RECHTS)* LN(TEST)) ELSE WERTOK:=FALSE; END; END; FUNKTION: BEGIN IF FN='' THEN WERT:=WERT(NEXT); IF FN='ABS' THEN WERT:=ABS(WERT(NEXT)); IF FN='ARCTAN'THEN WERT:=ARCTAN(WERT(NEXT)); IF FN='COS' THEN WERT: COS(WERT(NEXT)); IF FN='EXP' THEN WERT:=EXP(WERT(NEXT)); IF FN='LN' THEN BEGIN TEST:=WERT(NEXT); IF TEST>0 THEN WERT:=LN(TEST) ELSE WERT OK:=FALSE; END; IF FN='SIN' THEN WERT:=SIN(WERT(NEXT) ) ; IF FN='SQR' THEN WERT:=SQR(WERT(NEXT) ) ; IF FN='SQRT' THEN BEGIN TEST:=WERT(NEXT); IF TEST>0 THEN WERT:=SQRT(TEST) ELSE WERT OK:=FALSE; END; IF FN='TAN' THEN WERT:=SIN(WERT(NEXT))/COS(WERT(NEXT) ); END; ZAHL: WERT:=ZA; VARX: WERT:=FX; END; END; BEGIN IF FKT<>NIL THEN BEGIN WERTOK:=TRUE; FUNKTIONSBERECHNUNG:=WERT(FKT); IF NOT WERTOK THEN FUNKTIONSBERECHNUNG:=MAXREAL END ELSE FUNKTIONSBERECHNUNG:=MAXREAL; END; PROCEDURE clear_line; BEGIN write (chr (27), '1') END; PROCEDURE gotoxy (x, y: integer); BEGIN IF x < 0 THEN x := 0 ELSE IF x > 79 THEN x := 79; IF y < 0 THEN y := 0 ELSE IF y > 24 THEN y := 24; write (Chr (27), 'Y', chr (y + 32), chr (x + END; BEGIN REPEAT GOTOXY(3,2);WRITE('Interative Funktionseingabe'); GOTOXY(3,4);WRITE('<1> Term eingeben'); GOTOXY(3,5);WRITE('<2> Funktionswerte'); GOTOXY(3,6);WRITE('<3> Ende'); REPEAT GOTOXY (7,9) ;CLEAR_LINE;WRITE ('Ihre Wahl: '); READ(WAHL);CLEAR_LINE; UNTIL WAHL IN ['1'..'3'];GOTOXY(7,9); CASE WAHL OF '1' : BEGIN WRITE ('Funktionsterm: f(x)= '); READLN(TERM);CLEAR_LINE; FKT:=FUNKTIONSANALYSE(TERM); IF FKT=NIL THEN BEGIN GOTOXY(7,11);WRITE('FEHLER IN TERM'); READLN;CLEAR_LINE;END; END; '2': BEGIN IF FKT<>NIL THEN BEGIN WRITE('X = '); GOTOXY(11,9);READLN(X);CLEAR_LINE; Y:=FUNKTIONSBERECHNUNG(FKT,X); GOTOXY(0,11);CLEAR_LINE;GOTOXY(11,11); WRITELN('f(X)= ',Y); END ELSE BEGIN GOTOXY (7,11); CLEAR_LINE; WRITE('KEINE FUNKTION DA.'); END; READLN; END; END; UNTIL WAHL='3'; END.
Klaus Wilczek