BASIC-Kurs: "Von Adam und Eva..." (Teil 4) ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
Hiermit möchte ich Sie zum vierten Teil unseres Ba- sickurses herzlichst begrüßen. Diesen Monat werden wir uns noch ein wenig mit der formatierten Program- mierung befassen und werden uns anschließend endlich den lang versprochenen Schleifenbefehlen zuwenden. Letzten Monat hatten Sie ja gelernt, wie man, mit Hilfe von REM und sogenannten "Doppelpunktzeilen" ein wenig Ordnung in einen Programmtext bringt. Eine richtige "Programmierung" war das ja noch nicht, zu- mal die von uns eingefügten Zeilen und Kommentare ja nichts mit dem eigentlichen Programmablauf zu tun hatten, und wir sie genausogut auch hätten weglassen können, wie ich zum Schluß andeutete. Deshalb kommen wir heute zum GOSUB-Befehl, der in zusammenhang mit dem RETURN-Befehl uns ungeheuerliche Weiten zur platzsparenden, einfachen und formatierten Programmierung öffnet. GOSUB steht für GO to SUBroutine, was soviel heißt, wie GEHE zu UNTERprogramm, und ist in etwa vergleich- bar mit GOTO. Hinter GOSUB wird ebenfalls eine Zei- lennummer angegeben, zu der der Computer springen soll. Bis hierhin tut GOSUB genau dasselbe, was auch GOTO tut, nämlich ganz einfach zu einer bestimmten Zeile im Programm springen. Die Programmzeilen, die dort stehen und folgen werden ganz normal abgearbei- tet. Doch nun kommt der Clou: nachdem diese Zeilen abgearbeitet wurden, können Sie mit dem RETURN-Befehl wieder an die Stelle im Programm zurückspringen, von wo der GOSUB-Befehl in das Untersprogramm (so nennt sich das nämlich) eingesprungen ist. Dies hat den Vorteil, daß Sie beliebige Programmsequenzen, die Sie innerhalb eines Programms benötigen, beliebig oft benutzen können, aber nur EINMAL schreiben müssen. Der Computer merkt sich jedesmal die Stelle, von wel- cher Sie in das Unterprogramm eingesprungen sind, und wird jedesmal auch wieder dorthin zurückkehren. In der Praxis sieht das so aus:
5 REM ***************** 6 REM * hauptprogramm * 7 REM ***************** 8 : 10 GOSUB 200 20 PRINT"Hallo Leute..." 30 GOSUB 200 40 PRINT"Unn weils so schön war, grad nochmal..."; 50 GOSUB 200 60 END 90 : 195 REM ************ 196 REM * demotext * 197 REM ************ 198 : 200 PRINT 205 PRINT"Das ist ein Text, der hier zur Demon-" 210 PRINT"stration insgesamt 3 Mal ausgedruckt" 220 PRINT"wird." 230 PRINT 240 RETURN
Genau so sollte ein gut formatiertes Programm übri- gens aussehen. Vor jedem Modul (=Programmabschnitt, das hatten wir letzten Monat ja schon einmal...) steht ein unübersehbarer Kommentar. Zuerst haben wir das "HAUPTPROGAMM". Der Programmteil also, der am Anfang auch mit RUN gestartet wird, und der alle benötigten Unterprogramme, so wie zum Beispiel das Programm "DEMOTEXT", zu dem Zeitpunkt aufruft, zu dem sie benötigt werden. Schauen wir uns doch einmal unser Programm ein wenig näher an. Gleich am Anfang, in Zeile 10, wird unser kleines Unterprogramm schon beansprucht und aufgeru- fen. Dann wird in Zeile 20 der Text "Hallo Leute..." ausgegeben. Anschließend kommt wieder das Unterpro- gramm dran. Dann kommt etwas aus dem Hauptprogramm, nämlich der Text "Unn weils so schoen war, grad noch- mal...". Schließlich und endlich wird DEMOTEXT noch ein letztes Mal aufgerufen und das Hauptprogramm mit END beendet. Der Ausdruck, den Sie somit erhalten, sieht im Endeffekt dann so aus:
Das ist ein Text, der hier zur Demon- stration insgesamt 3 Mal ausgedruckt wird.
Hallo Leute...
Das ist ein Text, der hier zur Demon- stration insgesamt 3 Mal ausgedruckt wird.
Unn weils so schoen war, grad nochmal...
Das ist ein Text, der hier zur Demon- stration insgesamt 3 Mal ausgedruckt wird.
Sie sehen, obwohl Sie den Text, der hier, wie so oft angezeigt, "insgesamt 3 Mal ausgedruckt wird", nur einmal eingegeben haben, erscheint er tatsächlich drei Mal auf dem Bildschirm. Sie haben sich also er- steinmal ein wenig Tipparbeit gespart, und dann auch noch Speicherplatz, da drei mal mehr Platz verbraucht worden wäre, hätten Sie jedesmal unseren Text nochmal eingegeben. Außerdem sieht unser Programm so jetzt schön sauber und ordentlich aus - Unterprogramm schön vom Hauptprogramm getrennt... Und es gibt da noch einen kleinen Vorteil: angenom- men, Sie sind gerade dabei ein Programm zu schreiben, zu dem Sie ein bestimmtes Unterprogramm benötigen, das eine ganz bestimmte Aufgabe erfüllen soll. Welche Aufgabe im Einzelnen, sei jetzt einmal dahingestellt. Dieses Unterprogramm haben Sie vor längerer Zeit al- lerdings schon einmal in einem anderen Programm be- nutzt, und haben es dort noch voll funktionsfähig drin stehen. Das einzigste, was Sie jetzt zu tun ha- ben, ist das Unterprogramm aus Ihrem alten Programm herauszuisolieren, und es dann in Ihr neues einzubin- den. Also ein weiterer Vorteil von Unterprogrammen, da Sie sie jederzeit wiederverwenden können, können Sie sich Ihre Programme sozusagen aus vielen kleinen Modulen (also Unterprogrammen) "zusammenbauen", wie als wenn Sie mit Bauklötzen einen Turm konstruieren würden. Ich muß Ihnen allerdings gestehen, daß dies gar nicht so einfach ist, wie es sich anhört, da man beim BA- SIC V2.0 des C64 nicht so einfach riesige Programmab- schnitte löschen kann, geschweige denn, daß man ein- zelne Module so einfach aneinander hängen könnte. Doch nicht verzagen, in einer der nächsten Ausgaben, wird wahrscheinlich ein kleines Hilfsprogramm hierzu erscheinen, daß diese Aufgaben bewältigen wird. Wie oben schon erwähnt, kann man GOSUB auch zum "he- rumspringen" genauso wie GOTO verwenden, doch möchte ich Ihnen DRINGENDST davon abraten. Denn wie eben- falls schon gesagt, merkt sich der GOSUB-Befehl (im Gegensatz zu GOTO) eben jene Stelle, von wo aus er in ein Unterprogramm eingesprungen ist. Da der Computer allerdings nur einen begrenzten Speicherplatz für solche Rücksprünge bereithält, kann es Ihnen somit passieren, daß Ihr Programm irgendwann aus Speicher- mangel einen Fehler ausspuckt. Benutzen Sie also im- mer GOTO, wenn Sie "nur" springen möchten. Dies ist auch ein kleiner Hinweis für Sie, falls Sie mal ein Programm haben sollten, das nicht so laufen sollte, wie Sie es wünschen. Eine häufige Fehlerquelle ist nämlich auch die Tatsache, daß vergessen wurde, das Unterprogramm mit RETURN abzuschließen. Sollte näm- lich jetzt zufällig wieder der Programmteil folgen, von dem aus das Unterprogramm aufgerufen wurde, dann wiederholt sich dieser Aufruf solange, bis der Compu- ter abbrechen muß, da sein Merkspeicher "überläuft" (so nennt man das, wenn bei einer internen Operation der Computer über die Speichergrenze hinaus einen Zugriff machen muß). Das soll jetzt natürlich NICHT heißen, daß Sie ein Unterprogramm nicht wiederum von einem weiteren Unterprogramm aus aufrufen könnten. Natürlich geht das, Sie müssen nur jedes Unterpro- gramm immer wieder ornungsgemäß mit dem RETURN-Befehl abschließen, um keinen Ärger zu bekommen. Theoretisch könnten Sie sogar 24 Unterprogramme untereinander verschachteln, doch werden Sie diese Grenze wohl nie erreichen... Wollen wir uns nun endlich den Scheifenbefehlen zu- wenden. Zuerst einmal, was ist eine Schleife? Wie Sie sich vielleicht erinnern, hatten wir, beim Abhan- deln des GOTO-Befehls eine sogenannte "Endlosschlei- fe" geschrieben. Deshalb Endloschleife, weil ein ganz bestimmter Programmteil immer und immer wieder ab- gearbeitet wurde, so, daß das Programm endlos lange weitergelaufen wäre, hätten wir es nicht mit RUN/STOP angehalten. Hier nochmals ein solches Programm: 10 PRINT"Das ist eine Endlosschleife..."; 20 GOTO 10 Hier wird der Text "Das ist eine Endlosschleife..." also immer wieder ausgegeben, da in Zeile 20 ja immer wieder zu Zeile 10 verzweigt wird, womit sich unser Programm also immer wieder selbst aufruft. Klarer wird es, wenn Sie Zeile 20 wie folgt abändern: 10 PRINT"Das ist eine Endlosschleife..."; 20 RUN Jetzt startet sich das Programm also immer wieder selbst, ohne daß es jemals zu einem Ende kommen wür- de. Sie müssen allerdings zugeben, daß solche Endlos- schleifen, so wie sie hier dargestellt sind zumin- dest, wenig Sinn ergeben. Es wäre doch viel besser, wenn man eine solche Schleife unter Kontrolle hätte, und sie endlich machen könnte, so also, daß sie nach eine bestimmten Anzahl von Durchläufen beendet wird. Zum Einen könnte man das, wenn wir uns auf unser bis- heriges Wissen stützen, sehr gut mit der IF-THEN- Klammer lösen. Nämlich so:
5 i=0 10 i=i+1 20 PRINT"DAS IST EIN TEXT" 30 IF i<>5 THEN 10 40 END
Zuerst wird in Zeile 5 die Variable I auf 0 gesetzt. Anschließend wird diese Laufvariable (so wollen wir es einmal nennen), ihres Zeichens eine Floatvariable, um eins erhöht. Dann drucken wir den Text "DAS IST EIN TEXT" aus. In Zeile 30 vergleichen wir nun, ob I den Wert 5 enthält. Wenn nicht, dann wird wieder zu Zeile 10 gesprungen und der Vorgang des Um-Eins- Erhöhens und des Ausdruckens wiederholt sich. Andern- falls wird das Programm beENDet. Sie sehen, daß diese Schleife nur 5 Mal durchlaufen werden kann, da sich nämlich bei jedem Durchlauf die Variable I um eins erhöht, hat sie nach 5 Durchgängen den Wert 5 tatsächlich erreicht. Also wird die Schleife abgebro- chen, und es geht in Zeile 40 weiter im Programm. Hiermit hätten wir also eine Endlichschleife geschaf- fen, doch dies geht auch einfacher und übersichtli- cher, denn BASIC stellt uns extra zur Schleifenpro- grammierung zwei Befehle zur Verfügung, FOR und NEXT, die die FOR-NEXT-Schleife bilden. FOR-NEXT arbeitet ähnlich, wie unsere IF-THEN- Schleife. Auch hier benötigen wir eine Laufvariable, nur, daß wir hier gleich in einem Zug angeben können, von welchem Wert zu welchem Wert gezählt werden soll. Also haben wir hier die Zeilen 5 und 30 unseres er- sten Schleifenprogramms in EINEM zusammengefaßt. Se- hen Sie sich doch einfach einmal dieses kleine Pro- gramm an, das die selbe Arbeit verrichtet, wie unser letztes Programm, allerdings mit Hilfe von FOR-NEXT:
10 FOR I=1 to 5 20 PRINT"DAS IST EIN TEXT" 30 NEXT
Das war alles! Auf den ersten Blick sieht das zwar genauso lang aus, wie unser erstes Schleifenprogramm, doch der FOR-Befehl hat noch einige Eigenarten, die ihn zum einen komfortabler machen, und zum anderen ÜBERSICHTLICHER, was ja außerordentlich wichtig ist, wie wir festgestellt hatten. Nicht umsonst nämlich hatte ich ein ganzes Kapitel der formatiertien Pro- grammierung gewidmet! Das Übersichtliche an FOR-NEXT ist, daß der gesamte Schleifenrumpf, der Teil also, der immer wieder ab- gearbeitet werden soll, durch FOR und NEXT einge- grenzt wird. Sehen wir uns nun einmal an, was im In- nern unsers Computers abläuft, wenn er eine solche Schleife abarbeitet: In Zeile 10 erkennt der 64er erst einmal (an FOR), daß hier gleich eine Schleife folgt. Direkt hinter dem FOR-Befehl wird dann die Laufvariable mit einem Grundwert "initialisiert", das heißt, daß ein be- stimmter Wert als Ausgangswert für diese Variable festgelegt wird. Diese Initialisierung entspricht Zeile 5 unseres ersten Programms, hier wurde die Va- riable 'I' auf 0 gesetzt. Daß wir sie nicht mit 1 initialisierten, wie bei FOR, liegt einfach daran, daß sie anschließend, in Zeile 10, schon auf 1 gezählt wurde. Womit sie den selben Anfangswert hat, wie in unserem zweiten Programmbeispiel. Wie gesagt haben wir nun die untere und die obere Grenze festgelegt, innerhalb der der Computer zählen soll. In unserem Beispiel ist das von 1 bis 5. In Zeile 20 geht's jetzt weiter mit dem Schleifen- rumpf, der hier nur diese eine Zeile, mit Nummer 20, lang ist. Natürlich könnten Sie hier auch mehrere Zeilen stehen haben, Hauptsache ist, daß Sie diese dann auch zwischen FOR und NEXT sozusagen "einklam- mern". Unser Schleifenrumpf besteht also aus der An- weisung, den Text "DAS IST EIN TEXT" auszudrucken. In Zeile 30 nun trifft der Computer auf die NEXT- Anweisung. Jetzt zählt er die Schleifen- oder Laufva- riable erst einmal um 1 hoch. Entsprechend also der Zeile 10 unseres ersten Programms: 10 I=I+1 Anschließend prüft er, ob der Inhalt dieser Variable mittlerweile größer ist, als der obere Grenzwert, ob dieser also schon überschritten wurde. Ist dies der Fall, so wird die Schleife beendet und der Computer fährt in Programm fort. Ist die Laufvariable aller- dings noch kleiner oder gleich dem Grenzwert, so wird wieder an den Befehl im Programm verzweigt, der nach der FOR-Anweisung kommt. Dies wiederholt sich dann halt solange, bis der Grenzwert dann endlich überschritten wurde. Ich möchte hier auch noch darauf aufmerksam machen, daß die Laufvariable VOR dem Ver- gleich hochgezählt wird. Deshalb hat Sie nach dem Aussprung aus der Schleife immer den Inhalt des obe- ren Grenzwertes plus 1!!! Dies zu wissen kann äu- ßerst wichtig sein, da hier manchmal Fehler gemacht werden:
FOR i=0 TO 3:PRINT"TEXT":NEXT
Diese Schleife zum Beispiel wird insgesamt 4 (!) mal durchlaufen, da von 0 nach 3 gezählt wird (also 3 Ziffern plus die Null...). Nicht also, wie man fälschlicherweise annehmen könnte, nur 3 mal! Hier sehen Sie übrigens auch, daß man FOR und NEXT nicht unbedingt in eigene Programmzeilen schreiben muß, es ist nämlich auch durchaus möglich sie in EI- NER Zeile zusammenzufassen, vorrausgesetzt, der Schleifenrumpf ist nicht zu lang. Grundsätzlich gilt: alle Befehle, Anweisungen und Funktionen, die zwischen FOR und NEXT stehen bilden alle gemeinsam den Schleifenrumpf, den Teil also, der immer wieder von der Schleife durchlaufen wird, unabhängig davon, wie die einzelnen Befehle innerhalb von Programmzeilen verteilt sind. Doch kann man mit FOR noch viel mehr anfangen! Ange- nommen Sie wollten nicht immer um 1 erhöhen, sondern immer um 2, weil Sie, aus welchen Gründen auch immer, alle 2er Zahlen, oder alle geraden Zahlen, benutzen möchten. Hierzu kann man die STEP-Anweisung benutzen, die an den FOR-Befehl angehängt wird, ohne diesen sie nicht existieren kann, sprich nicht verwendbar ist. Geben Sie doch einfach einmal folgendes ein:
FOR I=0 to 10 STEP 2:PRINT I:NEXT
Die Ausgabe dieser Schleife sieht so aus:
0 2 4 6 8 10
Eigentlich nichts neues mehr für uns. Hier wurde ganz einfach von 0 bis 10 gezählt, allerdings in 2er Schritten, wie man an der Ausgabe erkennen kann - der Inhalt von 'i' war immer um 2 erhöht. Auch hier galt: erst als NACH einem Hochzählen um den Zählwert (oder die sogenannte Schrittweite) der obere Grenz- wert überschritten wurde, wurde die Schleife verlas- sen. Es mag sein, daß dieses Ihnen noch nicht ganz ein- leuchtet, deshalb also ein weiteres Beispiel. Wir hatten bis jetzt immer von einem kleineren Wert bis zu einem größeren gezählt. Manchmal könnte es aber auch ganz nützlich für uns sein, rückwärts zu zählen, beispielsweise von 5 bis 0 in 1er-Schritten, oder gar in halben Zahlen, also in 0.5er-Schritten. Das ist absolut kein Problem mit STEP. Der Wert hinter diesem Anweisungswort muß ganz einfach negativ sein, also mit einem Minuszeichen (-) davor. Etwa so:
FOR I=5 to 0 STEP -1:PRINT I:NEXT
Hier haben wir dann eine "Rückwärtsschleife". Sie zählt rückwärts von 5 bis 0. Auch hier gilt: erst wenn der zweite Grenzwert, diesmal ist er allerdings kleiner als der erste, UNTERSCHRITTEN wurde, wird die Schleife verlassen. Diesmal also erst, wenn der In- halt der Zählvariable kleiner ist, als der Grenzwert, zu dem gezählt werden soll. Dies ist ein kleiner Un- terschied, der bei Schleifen mit negativen Zählwerten auftritt, an den man sich jedoch leicht gewöhnt, da er im Grunde ja ganz logisch ist, oder? Das zum Thema Schleifen. Ich hoffe, Ihnen dieses Pro- blem verständlich dargelegt zu haben. Nun möchte ich mich, quasi zur Entspannung, einem etwas leichteren Themen zuwenden, sozusagen ein paar kleine Häppchen als "Hors d'oevre" vorschicken, auf das was nächsten Monat auf sie zu kommt... Beginnen wir mit einem kleinen Rechenoperator, den ich Ihnen bei unserer Rechnerei am Anfang dieses Kur- ses verschwiegen habe. Es handelt sich hierbei um den Exponentialoperator, der durch den 'Pfeil nach oben' ('↑') ausgedrückt wird. Mit ihm können Sie die n-te Potenz einer Zahl berechnen. Etwa 2 hoch 8, oder 5 zum Quadrat. Geschrieben wird das so:
PRINT 2↑8 PRINT 5↑2
Sie können diesem Operator natürlich genauso benut- zen, wie die anderen Operatoren auch, also ebenfalls auch bei Variablenzuweisungen benutzen. Sollten Sie übrigens darauf bedacht sein, Ihre Pro- gramme so zu programmieren, daß sie mit einem Minimum an Zeit ablaufen, dann gebe ich Ihnen den Tip, Ihre Quadrat-Potenzen (also die Potenzen, die die 2 als Exponenten, oder als 'Hochzahl' haben) mit dem Multi- plikationsoperator '*' auszudrücken. Dies rechnet der 64er nämlich bedeutend schneller aus, als wenn Sie den Potenz-Operator verwenden würden. Also anstatt: PRINT X↑2 schreiben Sie besser: PRINT X*X Dieses führt zum selben Ergebnis, verbraucht aller- dings bedeutend weniger Zeit. Sie selbst dürften das vielleicht nicht direkt merken, da es der Computer für uns normal Sterbliche immer noch sehr schnell be- rechnet, aber schreiben Sie doch vielleicht einfach einmal eine Schleife, die eine solche Potenzoperation vielleicht 1000 oder 2000 Mal durchführt, und stoppen Sie dann doch mal die Sekunden, ich denke, daß Sie einen Unterschied feststellen müßten... Das nächste, was ich Ihnen nicht vorenthalten möchte, ist die "DEF-FN"-Anweisung. Mit dieser Anweisung kön- nen Sie ganze Funktionen in einem Ausdruck definie- ren, so daß sie diesen Ausdruck nur noch durch ein kleines Namenskürzel aufrufen müssen. Angenommen, Sie haben eine Formel, mit der Sie durch Eingabe einer Unbekannten eine gewisse Größe berechnen können. Als praktisches Beispiel will ich mich mal ein wenig an der Kreisberechnung auslassen. Nehmen wir an, Sie wollten ein Programm schreiben, das Ihnen den Kreis- umfang sowie den Kreisinhalt berechnet. Die dazugehö- rigen Formeln lauten, wie wir wissen, wenn wir alle schön in der Schule aufgepaßt haben:
Kreisumfang: 2rπ (= 2*r*π) Kreisinhalt: r↑2π (= r↑2*π)
Da wir hier in unserem Kurs leider keine Sonderzei- chen haben, habe ich, um das Quadrat auszudrücken, wie unser C64 auch, den Exponentenpfeil (↑) benutzt. Sie kennen Ihn ja mittlerweile. In Klammern sehen Sie dann die endgültige Computerschreibweise (da ja noch die Multiplikations-Zeichen '*' eingefügt werden muß- ten. Die Formel für den Kreisinhalt werden wir allerdings gleich nochmal ein wenig abändern. Da wir ja die Qua- drate etwas schneller berechnen wollen, heißt es jetzt also: r*r*π (was ja dasselbe ist wie r↑2*π). Nun müssen diese beiden Funktionen nur noch mit DEF FN in einem Namen definiert werden. Dies ge- schieht folgendermaßen:
10 def fnu(r)=2*r*π 20 def fni(r)=r*r*π
Mit DEF zeigen Sie dem Computer also an, daß Sie hier eine Funktion definieren wollen. Dann geben Sie den Namen der Funktion an, der sich immer aus dem FN- Prefix, einem oder zwei Buchstaben und dem ' (R)'- Suffix zusammensetzt. Das Prefix ist bei jeder Defi- nition gleich, der eigentliche Name der Funktion wird durch die beiden Buchstaben in der Mitte bestimmt, bei denen Sie nach den selben Regeln arbeiten müssen wie bei den Variablennamen. Diese Regeln hatte ich Ihnen, als wir die Variablennamensgebung hatten, ja schon einmal aufgezählt. Zum Suffix sei nur noch zu sagen, daß hier in Klammern immer die Unbekannte ste- hen muß, die in der zu definierenden Funktionsglei- chung (oder Formel) eingesetzt werden soll. Warum, werden Sie beim Aufruf erkennen. Ich möchte hier jedoch erst einmal wieder ein weiteres Stück unseres Programmes einfügen:
30 print chr$(14)chr$(8) 40 print"[CLS]Programm zur Berechnung von Kreisum- fang" 50 print"und -inhalt." 60 input"[2 DOWN]Radius ";r 70 u=fnu(r) 80 i=fni(r)
Die Zeilen 30-50 drucken die Titelzeile des Programms aus. In Zeile 30 wird übrigens wieder mit CHR$(14) die Kleinschrift eingeschaltet und mit CHR$(8) die Umschaltmöglichkeit durch Drücken der SHIFT- und der COMMODORE-Tasten blockiert. Wir hatten dies ja schon einmal bei unserem Zylinderberechnungsprogramm... In Zeile 60 fragt das Programm jetzt nach dem Radius, der nach Eingabe eines beliebigen Wertes in der Va- riablen 'r' gespeichert wird. Jetzt folgt endlich unser langerwarteter Funktions- aufruf. Sie sehen, daß in Zeile 70 das Ergebnis der Funktion erst einmal der Variablen ' u' zugeornet wird. Der Aufruf ansich gestaltet sich höchst einfach und schmerzlos. Sie schreiben ganz einfach wieder den Prefix 'FN' gefolgt vom Funktionsnamen und dem Suf- fix, mit der unbekannten Variablen in runden Klammern eingeschlossen - das wars schon. Jetzt müssen wir das Ergebnis nur noch ausdrucken, und das geschieht fol- gendermaßen: 90 print"Der Kreisumfang ist: "u 100 print"Der Kreisinhalt ist: "i Was ja hoffentlich keinerlei Erklärung bedarf... Der Clou an den vordefinierten Funktionen ist also, daß man eine Unbekannte gleich in den Klammern am Ende angeben kann. Sie hätten die Funktion ebenso auch so aufrufen können: print fnu(1.5) Hier haben wir den Wert der Funktion also gleich nu- merisch angegeben, nicht also in einer Variablen. Der Vorteil von solchen Funktionsdefinitionen liegt auf der Hand. Angenommen, Sie haben eine bestimmte For- mel, die Sie in einem Programm wahnsinnig oft benut- zen müssen. Mit DEF-FN können Sie diese also immer abkürzen, und haben Ihre Funktion immer sofort parat. Mit FN haben Sie also eine Art Variable für Formeln (oder Funktionen), weshalb FN als ' normaler' Varia- blenname nicht zulässig ist. Sie können gerne einmal versuchen der Variable 'fn' einen Wert zuzuweisen, doch wird Sie der C64 dann nur mit einem lapidaren SYNTAX ERROR belohnen... Außerdem sei noch hinzuzufügen, daß Sie eine Funk- tionsdefinition ausschließlich NUR innerhalb von Pro- grammen durchführen können. Versuchen Sie dies im Direktmodus, so quittiert Ihnen das der Computer mit einem "ILLEGAL DIRECT ERROR", einem "UNERLAUBTEN DI- REKTANWEISUNGS FEHLER" also. Ebenso wie INPUT bei- spielsweise ist DEF außerhalb von Programmen unsin- nig, weshalb dieser Fehler auftritt. Einmal definiert allerdings, können Sie jede Funktion aus dem Direkt- modus aus aufrufen - andersherum geht es also... Beschäftigen wir uns nun noch ein wenig mit der be- nutzerfreundlichen Programmierung. Das heißt, daß wir unsere Programme so schreiben wollen, daß sie auch jeder Computeranfänger problemlos bedienen kann. Ge- nauergesagt möchte ich Ihnen die komfortable Program- mierung von sogenannten "Menüs" näherbringen. BASIC stellt uns hierzu 2 äußerst nützliche und komfortable Befehle zur Verfügung, die auch sehr gut noch für weitere Aufgaben zu verwenden sind. Als erstes will ich den Begiff "MENÜ" einmal klären. Bei einem Menü handelt es sich um eine Zusammenfas- sung von Unterpunkten innerhalb eines Programms, die der Benutzer einzeln aufrufen kann. Sie kennen dies bestimmt schon von meinem kleinen ASCII-Druck- Programm namens "SHOWASC", das dem zweiten Teil die- ses Kurses beigefügt war. Hier wurden Sie nämlich gefragt, welche Funktion des Programms Sie wünschen. Ich zeige Ihnen hier am besten einmal das Menü von SHOWASC, damit Sie sich ein besseres Bild machen kön- nen:
----------------------------------------------------- ASCII-Tabellen-Druck. Written in 1988 by Uli Basters.
Bitte druecken Sie:
==================== F1 - Ausgabe der Liste auf Drucker F7 - Ausgabe der Liste auf Bildschirm E - fuer Programmende -----------------------------------------------------
Sie können hier also zwischen 3 sogenannten "Menü- punkten" wählen, die Sie durch das Drücken einer be- stimmten Taste erreichen können. Kommen wir hier nun zum ersten der beiden Befehle, die ich Ihnen vorstel- len wollte: dem GET-Befehl. Mit ihm können Sie einen Buchstaben von der Tastatur einlesen, der dann - in einer Stringvariable gespei- chert - jederzeit abgerufen werden kann. Hier ein Auszug aus dem Programm des Menüs von oben (ich habe es aus Gründen der besseren Verständlichkeit ein we- nig abgeändert...):
... 25 print"Bitte druecken Sie: " 26 print"====================" 30 print" F1 - Ausgabe der Liste auf Drucker" 40 print" F7 - Ausgabe der Liste auf Bildschirm" 50 print" E - für Programmende" 60 get a$:if a$=""then 60
In den Zeilen 25-50 wird erst einmal der Text zu un- serem Menü auf dem Bildschirm ausgegeben. Wie das dann aussieht haben wir oben ja schon einmal gesehen. In Zeile 60 haben wir jetzt unseren GET-Befehl. Wie Sie sehen, steht ihm die String-Variable 'a$' nach. Die Taste, die also auf der Tastatur gedrückt wird, wird im ASCII-Code in dieser Stringvariablen gespei- chert. Hier könnte natürlich auch jede andere String- variable stehen, nicht aber eine Numerische, oder eine Intgervariable. Direkt anschließend, aber immer noch in Zeile 60, kommt ein IF-THEN-Vergleich. Es wird geprüft, ob der Inhalt der Variablen a$="" ist. Sie werden sich viel- leicht fragen, was diese 2 Hochkommas ausdrücken. Nun, ganz einfach, schreiben Sie irgendwo in BASIC für einen String, bei einer Zuweisung, oder wie hier zum Vergleich, ein "" dann steht das für NICHTS, oder auch KEIN STRING. Es wird also hier bei uns geprüft, ob 'a$' leer ist, oder ob ein String in ihr gespei- chert ist. Dieser Vergleich ist notwendig, da der Computer ja eine Taste in 'a$' gespeichert haben könnte, obwohl Sie gar keine gedrückt haben. Das hängt ganz einfach mit internen Vorgängen zusammen. Mit GET fragen Sie nämlich grundsätzlich immer die Tastatur ab, egal, ob jetzt eine Taste gedrückt ist, oder nicht - GET holt sich immer den aktuellen Tasta- turstand in die ihm nachgestellte Stringvariable. Sollte also beim Aufruf von GET keine Taste gedrückt sein, so brauchen wir gar nicht erst weiterzumachen, sondern schicken den Computer gleich wieder auf GET. Solange bis endlich eine Eingabe gebacht wurde. Wir haben hier also praktisch eine Endlosschleife produ- ziert, die erst dann beendet wird, wenn eine Taste gedrückt wird. Jetzt können wir prüfen, welche Taste dies war. Dies funktioniert ganz einfach über ein paar IF-THEN- Abfragen, siehe auch die folgenden Zeilen:
70 if a$="[F1]" then 300 80 if a$="[F7]" then 200 90 if a$="e" then end 100 goto 60
Wie die eckigen Klammern ja anzeigen, haben wir hier- wieder 2 neue Sonderzeichen, die sehr leicht zu erklären sind. Sie entstehen, wenn Sie innerhalb der Gänsefüßchen die F1- (in Zeile 70) oder die F7-Taste (in Zeile 80) drücken. Diese beiden Zeichen sind übrigens gute Beispiele für die Sonderzeichen, denn obwohl sie nicht sichtbar sind, wenn man sie aus- druckt, sind sie doch relativ wichtig, da man ohne sie die Abfrage der F-Tasten nur schwer realisieren könnte. Sie könnten hier übrigens ebenso schreiben:
70 if a$=chr$(133) then 300 80 if a$=chr$(136) then 200
Dies würde dasselbe bewirken, da die ASCII-Codes 133 und 136 ja den Tasten F1, und F7 entsprechen, die sie nebenbei auch der ASCII-Tabelle im Bedienungshandbuch des 64ers, Anhang F, entnehmen können, oder Sie be- nutzen ganz einfach SHOWASC. Es wird hier also nach Zeile 300 verzweigt, wenn der Drucker die Tabelle drucken soll, oder zu Zeile 200, wenn sie auf dem Bildschirm dargestellt werden soll. Anschließend kommt in Zeile 90 unseres Programms noch der Vergleich, ob 'a$' nicht den Buchstaben 'e' bein- haltet. Ist dies der Fall, so wird das Programm mit END beendet. Interessant ist noch Zeile 100. Hier wird wieder in Zeile 60 zum GET-Befehl gesprungen, also zurück in unsere Abfrageschleife. Dies ist notwendig, da ja auch noch andere Tasten, als F1, F7 oder e, gedrückt worden sein könnten - sei es aus Versehen, oder nur weil der User das Programm einmal ärgern wollte. War dies der Fall, so hat der Computer bis in Zeile 90 verglichen und festgestellt, daß keiner der 3 ASCII- Codes in ' a$' gespeichert ist. Jetzt würde er ja im Programm ganz normal weitermachen. Da ich in (der nun folgenden) Zeile 200 allerdings das Modul abgelegt habe, das die Ausgabe auf den Bildschirm übernimmt, müssen wir ihn hier dann abgefangen, damit er nicht unkontrolliert in eine Unterroutine reinläuft, was eben durch jenen besagten Sprung in die Zeile 60 rea- lisiert wird. Der zweite Befehl, den ich Ihnen noch vorstellen möchte, heißt ON, und wird in Verbindung mit GOTO und GOSUB benutzt. Er ist sozusagen eine spezielle Ver- sion des IF-Befehls. Hinter ON folgt eine Formel, die vom Computer errechnet wird. Dies ist im einfachsten Fall eine numerische Variable. Dann kommt entweder der GOTO- oder der GOSUB-Befehl, gefolgt von einer Liste mit Zeilennummern, die durch Kommata getrennt werden. Praktisch sieht das etwa so aus:
20 on x goto 100,200,300,400,500
Jetzt wird geprüft, welcher Wert in 'x' enthalten ist. Ist x=1, dann wird die erste Zeilennummer, die in der Liste steht, angesprungen (wie wenn sie ein GOTO 100 ausführen würden). Ist x=2 dann wird die 2. Zeilennummer angesprungen, bei x=3 die 3. und so fort... Diese eine Zeile entspricht also den 5 IF-THEN- Zeilen:
10 if x=1 then 100 20 if x=2 then 200 30 if x=3 then 300 40 if x=4 then 400 50 if x=5 then 500
Sollte x gleich 0, beziehungsweise negativ, oder gar größer als die angegebenen Glieder der Liste sein, so wird ganz normal im Programm fortgefahren. Wollten wir unser Programm von vorhin also umschreiben, so müßte das folgendermaßen aussehen.
60 input"Menüpunkt ";x 70 on x goto 200,300,400 80 goto 60
Das sieht doch schon gleich viel kürzer aus! Aller- dings darf man hier nicht vergessen, daß in Zeile 400 dann auch wirklich noch ein END-Befehl steht, da die- ser ja in unserem alten Programm direkt in der IF- THEN-Abfrage kam. Ich habe hier übrigens ganz bewußt den INPUT-Befehl benutzt, da wir ja gelernt haben, daß man bei GET nur Stringvariablen einlesen kann. Hierbei haben wir dann im Endeffek keine Möglichkeit mit ON zu arbei- ten, es sein denn man greift zu einem kleinen Trick: die Ziffern der Zahlen sind nämlich im ASCII-Code alle hintereinander angeordnet. Die 0 hat den Code 48, die 1 den Code 49 und so weiter, bis zur 9, die den Code 57 belegt. Wird bei einer GET-Abfrage also die 1 gedrückt, so enthält a$ den CHR$-Code 49. Wenn wir jetzt von dieser Zahl einfach den Wert 48 subtra- hieren, so erhalten wir das Ergebnis 1, was ja genau die Taste ist, die gedrückt wurde, nur haben wir jetzt einen Zahlwert, mit dem ON etwas anfangen kann. Das Programm hierzu müßte dann so aussehen:
60 get a$:if a$="" then 60 70 on asc(a$)-48 goto 200,300,400 80 goto 60
Hier haben Sie dann auch ein Beispiel, in dem wir eine ganze Formel benutzen, die zuerst berechnet und dann ausgewertet wird. Wir haben dabei die, uns mitt- lerweile schon altbekannte, ASC-Anweisung angewandt, um den CHR$-Code der in 'a$' gespeicherten Taste zu erfahren. Wie eingangs schon erwähnt, könnten Sie hier auch den GOSUB-Befehl verwenden, nur müssen Sie dann Ihre Mo- dule auch mit RETURN wieder abschließen. Denkbar wäre auch, daß man mit Buchstaben die Tasten abfrägt, wo- bei man alphabetisch vorgeht. Da der ASCII-Code des Zeichens 'a' den Wert 65 hat, müßten Sie hier nur den Wert 64 subtrahieren, um brauchbare Zahlwerte für ON herauszubekommen. Hiermit möchte ich nun für diesen Monat wieder einmal schließen und mich bis Mai verabschieden. Wir werden uns dann, wie ich diesmal schon angedeutet hatte, um etwas höchst wichtiges kümmern, was unerläßlich ist, wenn man sich ein wenig mit der Grafik- und Soundpro- grammierung des C64 beschäftigen möchte. Ich rede von dem sogenannten Binärsystem und dem Speicheraufbau unseres kleinen Freundes. Mehr dann nächsten Monat. Bis dann also, Ihr Uli Basters.