Scheme Tutorial 1.1 lambda,list & vector20-10-02 

Nachfolgendes Tutorial ist komplett Scheme gerecht geschrieben.
Ihr könnt also einfach den gesamten Text ins DrScheme Editierfenster kopieren und sofort laufen lassen.


;;;
;;; Scheme Tutorials by Moongate                   20-10-02
;;;
;;;  1.1 Lambda Functionen, Listen und Vectoren - erste Schritte
;;;

;;; Grundlegend sollte man folgendes wissen:
;;;  Ein Befehl in Scheme wird immer nach folgedem Prinzip
;;;  geschrieben:
;;;
;;; ( functionsname parameter1 parameter2 ... )
;;;
;;; Beispiel (+ 2 3) die Function heißt dabei "+"
;;;
;;; Nachdem das geklärt ist, können wir uns gleich dem
;;; nächsten zuwenden: Wie definiert man eine Variable
;;; in Scheme?

(define Zahl 0)

;;; Es wird also ähnlich wie in C++ (der Name gefolgt von einem
;;; Wert) angegeben. Statt der Typenbezeichnung aus C++ tritt
;;; nun das Wort "define" auf. -> "define" ist also eine
;;; Function die uns eine Variable definiert.

;;; Nun wollen wir mal mit unserer Variable rechnen:

(+ Zahl 3)

;;; Folglich wird da 3 herauskommen, da Zahl ja 0 ist.
;;; Um Anwendungen zu verschachteln, schreibt man einfach
;;; die Teilanweisungen wieder in Klammern. Also:

(+ Zahl (* 3 (+ Zahl 1)))

;;; Das Ergebnis ist identisch mit dem oberern da die "Zahl"
;;; immer noch 0 ist.
;;; Um dies zu ändern gibt es die function "set!"

(set! Zahl (+ Zahl 1))

;;; Dies würde die Variable Zahl um 1 erhöhen. Da erst der
;;; Ausdruck in der Klammer berechnet wird ( 0 + 1 ) und das
;;; Ergebnis dann in unsere Variable "Zahl" gespeichert wird.

;;; Da wir jetzt wissen wie wir Variabeln Werte zuweisen
;;; werden wir nun mal versuchen diese in einer Function zu
;;; verarbeiten:

(define laenge 8 )
(define breite 10)
(define flaeche (* laenge breite))

flaeche

;;; hmm das ganze funktioniert zwar ist aber schon ziemlich
;;; umständlich. Deshalb werden wir jetzt mal versuchen
;;; das ganze in eine Function zu packen.
;;; Wir wollen also folgendes schreiben können:
;;; (A_Rect 20 30) (A_Rect Abkürzung für Fläche von Rechteck)

;;; um eine function in Scheme zu schreiben geht man wie folgt
;;; vor:

( (lambda (a b) (* a b)) 20 30 )

;;; Sieht etwas umständlich aus. Was ist da nun was:
;;;
;;; (lambda (parameter1 parameter2 ...) ( Anweisungen ) )
;;;
;;; Und wozu nun diese 20 und 30 am Ende?
;;; Wir wissen das Scheme immer nach dem Prinzip
;;; (function para1 para2 ...) arbeitet.
;;; Wir haben aber diesmal keinen functionsnamen wie "+" oder
;;; "define" sondern die function selber.
;;;
;;; das heißt wir geben an der Stelle wo ein functionsname hin
;;; soll einfach eine function an (lambda (p1 p2 ..) (..) )
;;;
;;; Da das aber eigentlich recht unpraktisch ist, machen
;;; wir uns aus unser lambda function eine Variable.

(define A_rect (lambda (a b) (* a b)) )

;;; Jetzt können wir genau das machen was wir angestrebt hatten:

(A_rect 20 30)

;;; Jetzt wissen wir wie man eine function schreibt und sie an
;;; einen Namen bindet (define).

;;; Da Scheme sich aber nicht nur mit einzelnen Zahlen abgibt
;;; müssen wir auch noch unterscheiden was eine Liste ist und
;;; was Vectoren sind.

;;; Listen: Sind Aneinandereiungen von Werten die auch in sich
;;;         verschachtelte Listen besitzen können.

(list 6 7 3 4 5)

;;; Dies macht uns eine Liste in der 5 Elemente enhalten sind.

(list 6 7 (list 1 2) 4 5)

;;; Diese ist nun schon etwas komplizierter weil ein Element eine
;;; neue Liste (1 2) darstellt.

;;; Was mach ich nun damit ???

;;; gehen wir davon aus wir wollen das alle Flächenberechnungen
;;; die der User gemacht hat in einer Liste gemerkt werden.
;;; Dann müßten wir unsere function A_rect wie folgt umschreiben:

(define AListe (list ))

;;; Damit haben wir uns eine Leere Liste (list ) definiert.
;;; Nun werden wir erstmal herausfinden wie man ein Element
;;; an eine Liste anhängt.

(append (list 1 2 3) (list 2 3 4) )

;;; die function "append" hängt dabei zwei Listen (oder mehr)
;;; zusammen und gibt die neue Liste zurück.
;;; In unserer function sieht das dann irgendwie so aus:

(append AListe (list (* 1 2)) )

;;; Warum wird nun da diese list aus 1 * 2 gebildet?
;;; Das liegt daran das append immer zwei listen aneinander hängen
;;; will. Also machen wir aus unserem Rechenergebnis eine neue
;;; Liste mit einem Element.
;;;
;;; Das Ergebnis ist nun die Orginalliste + dem Rechenergebnis
;;; also eigentlich genau das was wir wollen. Nur hat die Sache
;;; einen Hacken: Die neue Liste wird ausgegeben nicht aber
;;; AListe zugewiesen - wir wollen ja das die Liste immer weiter
;;; wächst und nicht nur ausgegeben haben wie eine Liste aussehen
;;; würde wenn man AListe und ein Element dran hängt.
;;;
;;; Also benutzen wir wieder die set! function:

(set! AListe (append AListe (list (* 1 2)) ) )

;;; Nun ändern wir unsere A_Rect function

(set! A_rect
 (lambda (a b)(
    begin
    (set! AListe (append AListe (list (* a b)) ) )
    (* a b)
 ))
)

;;; Wir setzen also die function A_rect um. Dabei ist neu,
;;; daß wir jetzt mehr als eine Anweisung nach der lambda definition
;;; haben. Um Scheme zu verdeutlichen, daß mehrere Anweisungen
;;; folgen, schreiben wir ein "begin" vor die erste Anweisung.

;;; Wenn wir nun unsere function aufrufen:

(A_rect 20 45)

;;; Dann wird das Ergebnis an unsere Liste "AListe" angehängt.
;;; Um das auch zu testen lassen wir uns diese mal anzeigen:

AListe

;;; Und siehe da es funktioniert :o)

;;; Nun können wir noch etwas mit unserer Liste (AListe) arbeiten.
;;; Wir wollen Beispielsweise das erste Element der Liste angezeigt
;;; bekommen. Um Listen zu manipulieren gibt es im wesentlichen
;;; 3 Grundfunctionen "car" "cdr" und "cons".

;;; Für unsere erste Aufgabe benötigen wir nur die function
;;; "car":

(car AListe)

;;; car liefert uns also das erste Element einer Liste zurück.
;;; Kommen wir auch gleich zur nächsten "cdr". Diese liefert uns
;;; die Liste ohne das erste Element zurück. Das heißt car liefert
;;; uns immer ein Element - cdr immer eine Liste.
;;; In unserem Falle:

(cdr AListe)

;;; Dies liefert nun eine Liste mit allen Elementen (ohne dem ersten)
;;; zurück. Wenn du genauso vorgegangen bist wie im Tutorial hier
;;; dann solltest du eine (list 900) bekommen.

;;; Was machen wir nun wenn wir das zweite Element (was in obigen
;;; Fall ja 900 wäre) ausgegeben haben wollen?

;;; Genau wir kombinieren die beiden functionen:

(car (cdr AListe))

;;; Damit das ganze etwas schneller geht gibt es eine function
;;; die genau das tut:

(cadr AListe)

;;; Dies liefert uns das selbe Ergebnis.
;;; Nun haben wir nur noch das "cons" übrig - was macht das nun?
;;; Es dient dazu ein Element vor eine vorhandene Liste zu hängen.
;;; (im Unterschied zu append der etwas hinten dran hängt)

(cons 6 AListe)

;;; Wie wir sehen haben wir jetzt eine Liste (6 2 900) oder
;;; ähnlich je nach dem ob du bis jetzt alles genau so wie hier steht
;;; gemacht hast. Also könnten wir auch unsere A_rect function
;;; so umschreiben, daß das Ergebnis immer an den Anfang gehängt
;;; wird. Das sieht dann so aus:

(set! A_rect
 (lambda (a b)(
    begin
    (set! AListe (cons (* a b) AListe) )
    (* a b)
 ))
)

;;; Der einzige wichtige Unterschied ist, das wir diesmal keine
;;; Liste an die andere hängen sondern nur ein einzelnes Element
;;; ankleben.

;;; Dann könnten wir uns ja mal eine function schreiben die uns
;;; immer das letzte Ergebnis ausgegibt das berechnet wurde:

(define GetLastResult (lambda () ( car AListe ) ) )

;;; In diesem Fall haben wir eine function die keine Parameter
;;; besitzt "lambda ()". Gleich mal testen das ganze:

(A_rect 33 66)
(GetLastResult)

;;; Damit haben wir nun die Möglichkeit Ergebnise zu speichern
;;; und uns das letzte Ergebnis oder die ganze Liste auszugeben
;;; zu lassen.


;;; Zum Schluß noch etwas zu Scheme Vectoren - was ist das und
;;; wie funktioniert das?

;;; Vectoren sind im Grunde das selbe wie Listen nur kann man
;;; die functionen car cdr und cons nicht auf Vectoren anwenden.
;;; Dafür gibt es andere Grundbefehle:

(vector 3 4 1)

;;; Dies erstellt uns einen 3Dimensionalen Vector.
;;; Es gibt noch eine andere function um einen Vector zu erstellen:

(make-vector 5 0)

;;; Das Ergebnis ist ein 5Dimensionaler Vector der mit Nullen gefüllt
;;; ist. Also identisch zu (vector 0 0 0 0 0)

;;; Etwas leichter als bei Listen können wir ein Element aus einem
;;; Vector auslesen. Dazu dient die function "vector-ref" :

(define v1 (vector 3 4 1) )
(vector-ref v1 2)

;;; Dies liefert uns nun den 3. Wert aus unserem Vector V1
;;; Dabei ist zu beachten das immer bei 0 mit dem Zählen angefangen
;;; werden muß. Die function selbst arbeitet so:
;;; (vector-ref DerVector n)

;;; Als letztes will ich noch auf zwei Besonderheiten zur
;;; Definition von Listen und Vectoren hinweisen:

;;; Statt:  (list 1 2 3) kann man auch kurz '(1 2 3) schreiben.
;;; Statt:  (vector 1 2) kann man auch kurz #(1 2) schreiben.

;;; Das soll es für dieses Tutorial erstmal gewesen sein
;;; Ich hoffe ich habe damit ein paar Fragen beantwortet ;o)