Von Thomas Grasel Wie schon angekündigt geht es dieses Mal um die Benutzung des varianten Verbundes. Den varianten Verbund möchte ich anhand folgender Aufgabenstellung einführen: Die Distanz von zwei Punkten soll berechnet werden. Die Punkte können in Polar- oder in kartesischen Koordinaten angeben werden. Wer sich schon mit Polar- und kartesischen Koordinaten auskennt kann den folgenden Absatz überspringen. Bei kartesischen Koordinaten wird ein Punkt durch einen X-Wert sowie einen Y-Wert festgelegt. Dies ist die übliche Form. Man kann den Punkt aber auch durch seinen Abstand (Radius) vom Ursprung und den Winkel (+/- 180 Grad) zwischen ihm und der X-Achse angeben (Polarkoordinaten). Dabei wird der Winkel positiv gegen den Uhrzeigersinn gerechnet. Mit Hilfe des Satzes von Pythagoras sind beide Darstellungarten leicht ineinander umrechenbar. Hat man z.B. die kartesischen Koordinaten gegeben so erhält man die Polar- koordinaten durch radius=sqrt(sqr(x)+sqr(y)) winkel=arctan(y/x) Hat man jedoch die Polardarstellung gegeben so verwendet man x=radius*cos(winkel) y=radius*sin(winkel) Um die Eingaben abspeichern zu können müßte z.B. folgende Typenvereinbarung getroffen werden. type punkt = record x,y : real r,phi: real polar: boolean end In die Speicherstellen x und y würde die Position des Punktes eingetragen werden wenn er in kartesischen Koordinaten angegeben werden würde. Wird jedoch Polar eingegeben, so würde die Position in r und phi abgespeichert werden. In der Variablen 'polar' wird noch vermerkt ob der Punkt in Polar- koordinaten eingegeben wurde. Wie man sieht werden immer 2 Variablen nicht benutzt. Das ist in diesem Beispiel nicht weiter schlimm weil nur 16 Bytes für die beiden nicht ver- wendeten real-Zahlen verschwendet werden. Man kann sich jedoch leicht vorstellen das dies bei größeren Programmen schnell zu Problemen führen kann. Eine Möglichkeit der Abhilfe wäre folgende Deklaration: type punkt = record a,b : real polar: boolean end In 'a' könnte man dann entweder die X-Position oder den Radius, in 'b' die Y-Position oder den Winkel abspeichern. In Basic wäre dies auch die einzige Möglichkeit Speicherplatz zu sparen. Pascal jedoch hilft da dem Program- mierer mit dem varianten Verbund. type punkt = record case polar: boolean of true : (r,phi: real) false: (x,y : real) end Wie man sieht wird ein case-Ausdruck eingebaut. Dieser ist jedoch etwas anders strukturiert als der schon aus früheren Pascal-Kursen bekannte case- Befehl. Sinngemäß leistet er jedoch ähnliches. So bewirkt er das nur noch 16 statt 32 Bytes für die Position eines Punktes benötigt werden. Je nach dem ob die Variable polar 'true' oder 'false' ist werden die 2 Speicherplätze mit den Namen r/phi oder x/y ange- sprochen. Es werden also nicht wie oben 4 Speicherplätze reserviert sondern nur noch 2!!! Im Kopf der case-Anweisung wird eine Variable zur Fallunterscheidung definiert. Zum Vergleich: Bei einer case-Anweisung im Programm- teil würde man 'case polar of' schreiben, hier jedoch 'case polar: boolean of'. Die Anweisung z.B. hinter 'true' werden in Klammern geschrieben. Die case-Anweisung wird auch nicht wie üblich mit einem 'end' beendet. Das im Beispiel stehende 'end' gehört zur record-Anweisung!!! Dadurch bedingt muß der variante Teil eines Verbundes immer am Ende stehen. Man darf also die 'normale' case- Anweisung nicht mit dem case-Befehl des varianten Verbundes verwechseln! Zu Beachten ist ebenfalls das die Speicherplätze nur als x,y oder!!! r,phi angesprochen werden können, da x und r bzw. y und phi die gleichen Speicherplätze belegen. Das Problem tritt z.B. hier auf: punkt.x:=45 punkt.r:=4 Da die X-Position und der Radius die gleichen Speicherplätze belegen wird die 45 durch die 4 überschrieben. D.h. beim Lesen von 'x' und 'r' erscheint nur noch die 4!!! Natürlich kann nicht nur eine boolean- Variable zur Auswahl benutzt werden. Vielmehr sind alle 'Einfachen Daten- typen' (siehe Kurs #8) erlaubt. Werden für die einzelnen Fälle (z.B. true/false) unterschiedlich viel Bytes benötigt so wird soviel Speicherplatz reserviert wie der 'längste' Fall benötigen würde. Nun aber genug der Theorie, das folgende Programm arbeitet mit diesem Verfahren und löst obige Aufgabenstellung. program distanz type pkttyp = record case polar: boolean of true : (r,phi: real) false: (x,y : real) end var punkt: array(+1..2)+ of pkttyp (*-----------------------------------*) function distanz: real var dx,dy: real procedure wandeln var b: real i: integer begin (* wandeln *) for i:=1 to 2 do with punkt(+i)+ do begin if polar then begin b :=phi*arctan(1)/45 y :=r*sin(b) x :=r*cos(b) polar:=false end (* if *) end (* for/with *) end (* wandeln *) begin (* distanz *) wandeln dx:=punkt(+2)+.x-punkt(+1)+.x dy:=punkt(+2)+.y-punkt(+1)+.y distanz:=sqrt(sqr(dx)+sqr(dy)) end (* distanz *) (*-----------------------------------*) procedure eingabe var inp: char i : integer begin for i:=1 to 2 do with punkt(+i)+ do begin writeln ('Soll der ',i,'. Punkt polar ') write ('eingegeben werden (J/N) ? ') readln (inp) writeln if (inp='J') or (inp='j') then begin polar:=true write ('Radius : ') readln (r) write ('Winkel : ') readln (phi) end else begin polar:=false write ('X = ') readln (x) write ('Y = ') readln (y) end writeln end end (*-----------------------------------*) procedure titel begin writeln(' ':10,'Distanzberechnung') writeln writeln('Fuer den Pascal-Kurs des User-Mags') writeln(' ':8,'(c) 1992 Th.Grasel') writeln end (*************************************) begin writeln(chr(125)) titel eingabe writeln ('Die Distanz betraegt : ', distanz:6:3) end. Zuerst müssen die zwei Punkte einge- geben werden. Sie werden nun gegebenen- falls in kartesische Darstellung umge- rechnet. Danach wird die Distanz berechnet und ausgegeben. Im Programm findet ein neuer Befehl Verwendung. 'With' ermöglicht es 'lange' Verbundnamen die bei der Verwendung von records schnell entstehen kürzer zu schreiben. Die beiden folgenden Programmteile sind äquivalent und aus obigen Programm entnommen: begin (* wandeln *) for i:=1 to 2 do with punkt(+i)+ do begin if polar then begin b :=phi*arctan(1)/45 y :=r*sin(b) x :=r*cos(b) polar:=false end (* if *) end (* for/with *) end (* wandeln *) begin (* wandeln *) for i:=1 to 2 do begin if punkt(+i)+.polar then begin b:=punkt(+i)+.phi*arctan(1)/45 punkt(+i)+.y:=punkt(+i)+.r*sin(b) punkt(+i)+.x:=punkt(+i)+.r*cos(b) punkt(+i)+.polar:=false end (* if *) end (* for *) end (* wandeln *) Man sieht ist der obere Teil wesentlich kürzer. Verwendung findet die with- Anweisung überall da wo kurz hinter- einander viele Komponenten ein und desselben records verwendet werden. Der formale Aufbau sieht folgendermaßen aus: with variable do begin ... end Zu Beachten ist noch das innerhalb einer with-Anweisung keine Unter- programme aufgerufen werden dürfen (Kyan-Pascal spezifisch). Aber nun zurück zum Programm. Bei der evtl. Umwandlung von Polar- in kartesische Koordinaten muß man darauf achten man die alten Werte erst dann überschreiben darf wenn sie nicht mehr benötigt werden. So muß z.B. der y-Wert vor dem x-Wert beschrieben werden, da 'x' und 'r' ein und denselben Speicher- platz belegen, und zur Berechnung von x noch der Radius benötigt wird! Da der Computer im Bogenmaß arbeitet wird vor der eigentlichen Umwandlung der Winkel phi in das Bogenmaß b umgerechnet. Soviel für dieses Mal, zu sagen bleibt noch das ich in der nächsten Ausgabe wieder ein nützliches Utility ver- öffentlichen werde!