← ST-Computer 07 / 1990

Rekursion in OMIKRON.BASIC

Programmierpraxis

Was in anderen Sprachen möglich ist, muss doch auch in BASIC möglich sein. Und mit einem kleinen Trick ist es das auch. Im Rahmen eines LOGO-Seminars kamen die mannigfaltigen VorzĂŒge von LOGO ZUR Sprache, die es unter anderem erlauben, auf kĂŒrzeste Weise rekursive Programme zu schreiben. Dieses sei in einer “Primitivsprache” wie zum Beispiel BASIC nur schwer möglich. Um Ă€hnliche Resultate zu erzielen, mĂŒsste man sich in BASIC seitenlanger Programme bedienen.

Diese Auffassung zu widerlegen, war fĂŒr mich der Anstoß fĂŒr meinen Versuch in OMIKRON-BASIC. Mit nur zwei weiteren Befehlen lĂ€ĂŸt sich das Kapitel der Rekursionen auch in BASIC abhandeln. Allerdings verlangt die BASIC-Lösung auch etwas mehr Gedankenaufwand vom Programmierer.

Wie dem Listing zu entnehmen ist. habe ich auf zwei beliebte LOGO-Beispiele zurĂŒckgegriffen. Schnee zeichnet, wie der Name schon sagt, Schneekristalle auf den Bildschirm, wobei die Anzahl der Radien variabel ist. Zweige liefert die bekannte Baumstruktur mit variablem Winkel. Und Show liefert interessante Grafiken aus sich drehenden Quadraten. Es sind aber durchaus auch andere Beispiele möglich und relativ leicht selbst zu erstellen.

Die implementierten LOGO-Befehle dienen nur dem Zweck der Demonstration. So treten Rundungsfehler auf, die sich auch nur in einem Demoprogramm vertreten lassen. Aber vielleicht weiß ja jemand bessere Algorithmen?

Da Omikron-BASIC sich nicht um die Verwaltung der Variablen bei Selbstaufrufen von Prozeduren kĂŒmmert, mĂŒssen wir das eben selbst erledigen. Zu diesem Zweck erhĂ€lt jeder Aufruf der Prozedur einen eigenen Variablensatz durch den Index Eb. Bei diesem Verfahren ist die Rekursionstiefe nur durch den Speicherplatz begrenzt, da die Variablen als Array definiert sind. Doch nun zur Beschreibung des Listings: Nach der Initialisierung folgt das Hauptprogramm, welches eines der Beispiele aufruft. Der Prozedur Schnee werden folgende Parameter ĂŒbergeben: Eb - das ist die ZĂ€hlervariable fĂŒr die Rekursionsebenen, S(Eb) - bestimmt die LĂ€nge der Anfangsstrecke, N(Eb) - die Anzahl der Radien. In der Prozedur Zweige wird durch N(Eb) der Winkel zwischen zwei Ästen ĂŒbergeben. Die Übergabe des Wertes Null durch Eb beim Start der Prozeduren dient zum ZurĂŒcksetzen des RekursionszĂ€hlers. Zeile 63 enthĂ€lt die Abbruchbedingung bei Unterschreiten einer MindestlĂ€nge. In der nĂ€chsten Zeile befindet sich die ZĂ€hlschleife fĂŒr die Anzahl der zu zeichnenden Radien. Der Befehl Fd(S(Eb)) zeichnet nun eine Gerade mit der LĂ€nge S. Ihre Richtung ist vom letzten Aufruf der Prozedur Rt() abhĂ€ngig, die bei der Initialisierung auf 0 Grad (senkrecht nach oben) gesetzt wurde.

Vor dem Selbstaufruf der Prozedur mĂŒssen nun der RekursionsebenenzĂ€hler erhöht und der SchleifenzĂ€hler I(Eb) zurĂŒckgesetzt werden. Das erledigt die Prozedur Rec_up. Ebenso werden nach dem Selbstaufruf der EbenenzĂ€hler wieder erniedrigt und der SchleifenzĂ€hler wieder auf seinen vorigen Wert gebracht (Rec_down). Bis zur Zeile 68 existieren beim Programmablauf lediglich eine bestimmte Anzahl von Schneeprozeduren. die bisher nur eine Gerade gezeichnet und sich dann selbst wieder aufgerufen haben. In Zeile 69 zeichnet zuerst die zuletzt erzeugte Prozedur eine Gerade rĂŒckwĂ€rts [Bk(S(Eb))], und dann wird ein Winkel nach rechts vollzogen. Durch den Index Eb existiert praktisch fĂŒr jede Rekursionsebene ein eigener Variablensatz.

Beim Selbstaufruf der Prozedur muß aber auf die bereits wertzugewiesenen Variablen zurĂŒckgegriffen werden. Deshalb erfolgt der Aufruf mit S(Eb-1). Die Division durch 2 erniedrigt lediglich kontinuierlich die StreckenlĂ€nge. Das Beispiel Zweige schaut da durch den doppelten Selbstaufruf schon wesentlich wilder aus. Die Prozedur Rechtecke verdeutlicht die Wirkungsweise von Rekursionen noch einmal sehr anschaulich. Nur wird hier bei jedem Selbstaufruf die x-Koordinate verringert. Die LOGO-Befehle erklĂ€ren sich mit ein wenig Trigonometrie von selbst. Nur sei nochmals darauf hingewiesen, daß sie nur fĂŒr Demonstrationszwecke gedacht sind. Allerdings benötigt man mit diesen Befehlen. z.B. fĂŒr die beliebte Analoguhr auf dem ST. nur maximal 20 Zeilen. Versuchen lohnt sich! Zum Schluß sei noch darauf hingewiesen, daß es natĂŒrlich nicht meine Absicht ist, BASIC als die Hochsprache schlechthin zu verteidigen. Speziell Rekursionen lassen sich mit anders strukturierten Sprachen sicher besser bearbeiten. Aber möglich ist es in BASIC eben doch.

‚*********************************************** '* Rekursionen '* in '* OMIKRON-Basic '* '* Ulrich Hirschmann '* '*@ 02.1990, '*(c) MAXON Computer GmbH 1990 '*********************************************** Init ' REPEAT L_Input(0,0,"Parameter eingeben :",W%L) I%L(0)=0:L_Wa%L=0 PRINT @(2,0);"Ende: Winkel>90 oder [Help]" CLS Sety%L=250 Setx%L=250 ' Zweige(0,50,W)' alternativ ******** Schnee(0,50,W%L)' ******** ' Show' *** LOGO-Demo *** WHILE MOUSEBUT =0: WEND IF MOUSEBUT =2 THEN HCOPY UNTIL Immer%L ' END '************** Logoprogramm **************** DEF PROC Show FILL STYLE =1,0 Pe Sety%L=200 PRINT CHR$(27);"p" WHILE 1 Setx%L=600 Rt(0):Fd(0) L_Wa%L=0 INPUT @(0,0);"Winkel :";I%L PBOX 0,0 TO 640,400 Rechtecke(0,6,I%L) WEND Pd PRINT CHR$(27);"q" RETURN DEF PROC Quadrat(St%L) FOR I%L=1 TO 4 Fd(St%L):Rt(90) ' **mit rt(33) probieren NEXT I%L RETURN DEF PROC Rechtecke(Eb%L,S!(Eb%L),N%L(Eb%L)) IF S!(Eb%L)>200 THEN RETURN Lt(N%L(Eb%L)) Quadrat(S!(Eb%L)) Setx%L=Setx%L-4 Rec Up' *** nĂ€chste Ebene Rechtecke(Eb%L,S!(Eb%L-1)+3,N%L(Eb%L-l)) Rec_Down' *** und zurĂŒck Lt(N%L(Eb%L)) Quadrat(S!(Eb%L)) Setx%L=Setx%L-4 RETURN DEF PROC Schnee(Eb%L,S!(Eb%L),N%L(Eb%L)) '*** Eb setzt den RekursionszĂ€hler zurĂŒck IF S!(Eb%L)<4 THEN RETURN ' *** Abbruch WHILE I%L(Eb%L)<N%L(Eb%L)' *** Anzahl der gezeichneten Radien Fd(S!(Eb%L)) Rec Up' *** nĂ€chste Ebene Schnee(Eb%L,S!(Eb%L-1)/2,N%L(Eb%L-1)) Rec_Down' *** und zurĂŒck Bk(S!(Eb%L)) Rt(360/N%L(Eb%L)) WEND RETURN ' DEF PROC Zweige(Eb%L,S!(Eb%L),N%L(Eb%L)) IF S!(Eb%L)<10 THEN RETURN ' *** minimale StreckenlĂ€nge Lt(N%L(Eb%L)):Fd(S!(Eb%L)) Rec_Up Zweige(Eb%L,S!(Eb%L-1)/1.2,N%L(Eb%L-1)) Rec_Down Bk(S!(Eb%L)) Rt(N%L(Eb%L)*2):Fd(S!(Eb%L)) Rec_Up Zweige(Eb%L,S!(Eb%L-1)/1.2,N%L(Eb%L-1)) Rec_Down Bk(S!(Eb%L)):Lt(N%L(Eb%L)) RETURN ' '*********************************************** '*********** Logobefehlsdefinitionen *********** '*********************************************** ' DEF PROC L_Input(L_X%L,L_Y%L,L_Text$,R W%L) PRINT CHR$(27);"e" -J: INPUT @(L_X%L,L_Y%L);L_Text$;W%L IF W%L=0 THEN GOTO -J%L PRINT CHR$(27);"f" RETURN DEF PROC Warte: WHILE INKEY$ ="": WEND : RETURN DEF PROC Rec_Up:Eb%L=Eb%L+1:I%L(Eb%L)=0: RETURN DEF PROC Rec_Down:Eb%L=Eb%L-1:I%L(Eb%L)=I%L(Eb%L)+1: RETURN DEF PROC Pu: LINE COLOR =0: RETURN ' *** Pen up DEF PROC Pd: LINE COLOR =1: RETURN ' *** Pen dn DEF PROC Px: MODE =3: RETURN ' *** Pen XOR DEF PROC Pe: MODE =1: LINE COLOR =0: RETURN ' *** Pen erease ' DEF PROC Init ON HELP GOSUB L_Ende CLS : DEG : PRINT CHR$(27);"f": CLIP 0,0,640,400 Rt(0):Fd(0) DIM Eb%L(999),I%L(999),S!(999),N%L(999) MODE =1:Pd RETURN ' -L_Ende: END ' DEF PROC Rt(Winkel%L)' *** right turn L_W1%L=Winkel%L+L_Wa%L L_Wa%L=L_W1%L L_W%L=L_W1%L-90 RETURN ' DEF PROC Lt(Winkel%L)' *** left turn L_W1%L=-Winkel%L+L_Wa%L L_Wa%L=L_W1%L L_W%L=L_W1%L-90 RETURN ' DEF PROC Fd(Strecke%L)' *** forward X1%L=Setx%L:Y1%L=Sety%L X2%L=Strecke%L* COS(L_W%L)+Setx%L Y2%L=Strecke%L* SIN(L_W%L)+Sety%L DRAW X1%L,Y1%L TO X2%L,Y2%L Setx%L=X2%L:Sety%L=Y2%L RETURN ' DEF PROC Bk(Strecke%L)' *** backward Setxb%L=Setx%L:Setyb%L=Sety%L X1%L=Setxb%L:Y1%L=Setyb%L X2%L=Strecke%L* COS(L_W%L-180)+Setxb%L Y2%L=Strecke%L* SIN(L_W%L-180)+Setyb%L DRAW X1%L,Y1%L TO X2%L,Y2%L Setxb%L=X2%L:Setyb%L=Y2%L Setx%L=X2%L:Sety%L=Y2%L RETURN
Ulrich Hirschmann