VAL in PASCAL - Umwandlung von Strings in numerische Variablen

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:

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
Aus: ST-Computer 03 / 1988, Seite 68

Links

Copyright-Bestimmungen: siehe Über diese Seite