Proseminar Funktionales Programmieren Die Programmiersprache LISP Stephan Kreutzer Teil I: Funktionales Programmieren Imperative Sprachen Imperative Sprachen: Befehlsorientiert “Imperative Sprachen orientieren sich an der Arbeitsweise von Computern.” Idee: Durch Befehle wird der Zustand des Rechners verändert. Typische Elemente: Zuweisungen: x = 3; Schleifen: for(int i=17; i!=0; i--) { ... } Prozeduren: int whatever(int x) { ... } Zeiger: int *y = &x; *y = 19; Explizite Speicherverwaltung: int ar[] = malloc(17*sizeof(int)); free(ar); Arrays: ar[ 13] = ar[12]-3; Typische Vertreter: C, Pascal, Fortran, Algol, ... Stephan Kreutzer Proseminar Funktionales Programmieren 3 Funktionale Sprachen Funktionale Sprachen: Ausdrucksorientiert “Funktionale Programme berechnen Ausdrücke.” Idee: Programme beschreiben Abbildungen (der Eingabe auf die Ausgabe) Typische Elemente: Gleichheit: let x = 3;; Seiteneffektfrei, daher keine Zeiger, usw. Funktionen: let rec factorial = function n -> if n = 0 then 1 else n * factorial (n-1);; Implizite Speicherverwaltung (garbage collection) Datenstrukturen: Listen und Bäume (Rekursion) Pattern Matching, Typinferenz, Funktionen höherer Ordnung... Typische Vertreter: Stephan Kreutzer Haskell, ML und Nachfolger, (Scheme), (Lisp) Proseminar Funktionales Programmieren 4 Seiteneffekte Seiteneffekte: Betrachte C­Programm: int global-var = 9; int f(int x) { global-var = global-var + 1; return x + global-var; } void main() { int x1 = f(5); int x2 = f(5); } ● ● // x1 := 15 // x2 := 16; Solche Zustandsveränderungen heißen Seiteneffekte. Funktionale Programme sind “seiteneffektfrei” ! ➢ Variablen sind keine Namen für Speicherstellen! ➢ Keine Zuweisungen Stephan Kreutzer Proseminar Funktionales Programmieren 5 Seiteneffekte Betrachte OCaml­Programm: let y = 3;; (* Weist x den Wert 3 zu *) let f = function n -> (y+n);; (* Definiert Funktion f(n) = n+x*) let x1 = f 3;; (* Liefert 6 als Wert *) let y = 10;; (* Weist x den Wert 10 zu *) let x2 = f 3;; (* Liefert 6 als Wert *) ● Variablen bezeichnen Werte, keine Speicherstellen! ● Zweites let x = 10;; verändert die Definition von f nicht. ● x ist also keine globale Variable, wie im C­Programm vorhin. Vorteil: Seiteneffekte sind einer der häufigsten Fehlerquellen Seiteneffekfreiheit eliminiert diese Fehlerquelle Stephan Kreutzer Proseminar Funktionales Programmieren 6 Schleifen und Rekursion Schleifen in imperativen Programmen: int ergebnis=0; int ergebnis=0; while (some-test(ergebnis)) { ergebnis = some-function(); } for (int i = 0; i<17; i++) { ergebnis = some-thing(); } return ergebnis; return ergebnis; Schleifenabbruch erfolgt also über Seiteneffekte. Dumm, wenn Seiteneffekte nicht erlaubt sind. In funktionalen Programmen: Rekursion let rec f = function ergebnis -> if some-test(ergebnis) then f(some-function(ergebnis)) else ergebnis;; Stephan Kreutzer Proseminar Funktionales Programmieren 7 Listen Für rekursive Programme ohne Speicherveränderung eignen sich besonders „ rekursive” Datenstrukturen, z.B. Listen oder Bäume. let list = [1; 2; 3; 4];; (* Definiert Liste 1,2,3,4 *) let rec add = function (* Addiert die Werte einer Liste *) [] -> 0 | n::r -> n + add r;; Pattern Matching let result = add list;; (* [] steht für leere Liste *) (* n::r steht für Liste mit erstem Element n und Rest r. Beispiel: [ 1; 2; 3; 4] = 1::[2; 3; 4] *) (* Liefert result = 10 als Ergebnis *) Listen und Bäume gut geeignet, da typischerweise sequentieller Zugriff. Weniger gut geeignet für funktionale Programme: Arrays Hier erfolgt der Zugriff an beliebigen Positionen. Typischerweise Manipulation der Werte in einem Array. Stephan Kreutzer Proseminar Funktionales Programmieren 8 Teil II: Common LISP „ Common Lisp is a new dialect of Lisp, a successor to MacLisp [33, 37], in­ fluenced stronlgy by ZetaLisp [55, 34] and to some extend by Scheme [46] and InterLisp [50].” (Common Lisp the language, 2nd edition, Guy L. Steele) LISP – List Processing Lisp: Akronym für “List Processing Language” 1956: John McCarthy entwickelt Idee zu einer Sprache für algebraische Listenverarbeitung in der künstlichen Intelligenz. Wie der Name schon andeutet, ist Lisp auf Listenverarbeitung spezialisiert. Anders gesagt: Lisp verarbeitet eigentlich nur Listen. Beispiele fü r Listen in Lisp: ( 1 2 3 4 ) ( Name 2 ) ( Name 2 (/ 2 3) ) ; Liste mit Elementen 1, 2, 3, 4 ; Liste Student, Name, 2 ; Liste Name, 2, Unterliste /, 2, 3 Name, 2 ... Symbole Beachte, daß Name nicht in Anführungszeichen steht, ist also kein String! In Listen können Elemente verschiedenen Typs vorkommen: Lisp ist eine ungetypte Sprache! (In Lisp haben Objekte einen Typ, Variablen aber nicht. Also kein ' int x;' ) Stephan Kreutzer Proseminar Funktionales Programmieren 10 Formen in Lisp Lisp­Programme bestehen aus Formen, die ausgewertet werden. Form: Entweder ein Atom oder eine Liste. Atom: Zahl: 123 Variable: x, 4h//34+, ... Konstante: (defconst konstante 4) Liste: (list 'Listenelement­2 'Elem­3 'x) (+ 4 5) Auswertung von Listen: Das erste Element wird als Funktion gewertet und auf die anderen Ele­ mente angewendet. > (+ 4 5) 9 >(list 'x 3 'Name ) (x 3 Name) Stephan Kreutzer ; Numerische Operationen in Präfix­Notation ; Bildet Liste mit Elementen x, 3, Name Proseminar Funktionales Programmieren 11 Listen in Lisp Listen: (element1 element2 element3 ...) Listenelemente werden durch ' ' getrennt. Listenkonstruktoren: (cons h r): Bildet eine Liste mit Kopf h und Rest r (cons 1 nil) -> (1) ; nil steht für leere Liste (cons 1 (cons 2 nil)) -> (1 2) (cons 1 '(2)) -> (1 2) ' : quote Verhindert, daß Lisp versucht, '2' als Funktion auszuwerten. (cons 1 (quote (2))) Listen zerlegen: (first '(1 2 3)) (rest '(1 2 3)) äquivalent zu (cons 1 '(2)) -> 1 ; liefert das erste Element einer Liste -> (2 3) ; liefert den 'Rest' einer Liste (Man beachte wieder die quotes) (rest (list 1 2 3)) Stephan Kreutzer -> (2 3) ; liefert den 'Rest' einer Liste Proseminar Funktionales Programmieren 12 Einfache Sprachelemente (Spezielle Formen) Variablenzuweisung: (setq x wert) Weist x den Wert 'wert' zu Dabei wird das erste Argument 'x' nicht ausgewertet (setq x 123) (setq x '(1 2 3)) ; Weist x die Zahl 123 zu ; Weist x die Liste ( 1 2 3 ) zu Bedingte Verzweigung: (if test w1 w2) Wertet zu w1 aus, wenn 'test' erfüllt ist, sonst zu w2. (if (= 1 1) (+ 3 4) "String" ) Conditional: (cond (t1 v1) (t2 v2) ...) Wertet zu vi aus, wenn Test ti erfüllt ist. (Erster erfüllter Test) (cond ((= 1 1) "String") ((= 2 3) Symbol) (t 123)) Stephan Kreutzer Proseminar Funktionales Programmieren 13 Funktionen Funktionsdefinition: (defun funktion (p1 p2 ...) (Funktionsrumpf)) Definiert Funktion 'funktion' mit Parametern p1 p2 .... “ Hello World” : > (write “Hello World!”) Hello World! Fakultä tsfunktion: > (defun factorial (n) (cond ((= n 0) (t 1) (* n (factorial (- n 1)))))) FACTORIAL Addieren der Elemente einer Liste: (defun add (l) (cond Stephan Kreutzer ( (eq l () ) 0) ( t (+ (first l) (add (rest l)))))) Proseminar Funktionales Programmieren 14 Ein­ und Ausgabe Eingabe: Liest Eingabe von der Standardeingabe (read) (setq x (read)) Liest eine Eingabe und weist sie der Variabel x zu Ausgabe: (write “Hello World!”) Besonderheiten: 'read' liefert einen Lisp­Ausdruck zurück, keine Zeichenkette. > (read) Dies ist eine Zeichenkette! *** - EVAL: variable IST has no value Nü tzlich in Verbindung mit 'eval': (eval w) wertet den Ausdruck 'w' aus. (eval '(setq x 10)) weist x den Wert 10 zu Betrachte: > (eval (read)) (setq x 100) Liest Eingabe und wertet sie aus, d.h. weist x den Wert 100 zu. Man spart sich also den Parser! Stephan Kreutzer Proseminar Funktionales Programmieren 15 Ein Beispiel Datenbank: (setq datenbank '((father Homer Bart) (father Homer Lisa) (son Bart Homer))) Programm: > (eval (read)) (cons '(daughter Lisa Homer) datenbank) Fügt den Eintrag '(daugther Lisa Homer)' der Datenbank hinzu. Werden jetzt noch Funktionen wie 'remove' und 'lookup' definiert, entsteht in wenigen Zeilen ein Programm zur Verwaltung einer einfachen Abstam­ mungsdatenbank! Man vergleiche dies mit einem äquivalenten Program in C oder Java! Bisher liest das Programm allerdings nur eine einzelne Eingabe. Es fehlt ein Wiederholungsmechanismus! Stephan Kreutzer Proseminar Funktionales Programmieren 16 Schleifen "There' s no such thing as an infinite loop. Eventually, the computer will break." ­­ John D. Sullivan Loop­Schleife: (loop (body)) Wiederholt unendlich oft den Schleifenrumpf ' (body)' Abbruch der Schleife: bricht die Schleife mit Wert ' wert' ab. (return wert) Weitere Schleifen: (dotimes (n Zahl) (body1) (body2) ...) > (dotimes (n 11) 0 0 1 1 2 4 3 9 4 16 ... (print n) Äquivalent einer ' for' ­Schleife (print (* n n))) Es gibt noch andere Schleifenkonstrukte. Stephan Kreutzer Proseminar Funktionales Programmieren 17 Ein Beispiel Datenbank: (setq datenbank '((father Homer Bart) (father Homer Lisa) (son Bart Homer))) Programm: > (loop (eval (read))) (cons '(daughter Lisa Homer) datenbank) Fügt den Eintrag '(daugther Lisa Homer)' der Datenbank hinzu. Mit der Schleife ist sind nun beliebige viele Datenbankoperationen möglich. Die Lisp­TopLoop: (loop (write (eval (read)))) Liest endlos einen Lisp­Ausdruck von der Standardeingabe, wertet in aus und gibt das Ergebnis auf dem Bildschirm aus. Stephan Kreutzer Proseminar Funktionales Programmieren 18 Basistypen in Lisp Zahlen: BIGNUM: RATIO: FIXNUM: FLOAT: Ganze Zahlen beliebiger Stelligkeit (beschr. durch Speicher) Rationale Zahlen (* 3 (/ 1 2)) -> 3/2 Integers (Größe maschinenabhängig. Entspr. int) Gleitkommezahlen (maschinenabhängig) Lisp benutzt Präfixnotation (da in Listen erstes Symbol Funktion sein muß) Zeichenketten: STRING: Zeichenketten CHAR: Einzelne Zeichen ( #\a steht für Buchstaben 'a') Lisp ist ungetypt, d.h. Variablen haben keinen Typ (also kein BIGNUM x). Natürlich kann auch Lisp keine Zahlen zu Wörtern addieren. > (defun f (n) (+ n "Hallo")) ; Def. Funktion die n zu „Hallo” add. F ; Lisp akzeptiert Definition. > (f 3) *** - +: "Hallo" is not a NUMBER Stephan Kreutzer Proseminar Funktionales Programmieren 19