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