Funktionales Programmieren: Scheme Funktionales Programmieren: Scheme Gliederung Programmierparadigmen Einführung in Scheme 1 D. Rösner Institut für Wissens- und Sprachverarbeitung Fakultät für Informatik Otto-von-Guericke Universität Magdeburg Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz c Sommer 2011, 9. Juni 2011, 2011 D.Rösner D. Rösner PGP 2011 . . . Funktionales Programmieren: Scheme D. Rösner PGP 2011 . . . 1 Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Funktionales Programmieren: Scheme Historie 2 Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Historie unterschiedliche Ausrichtungen: Fortran: numerische Berechnungen Frage: Welches sind die zwei ältesten und (in modernisierter Form) immer noch benutzten Programmiersprachen? Lisp: Symbolverarbeitung Symbolverarbeitung? Beispiele: ... ... symbolisches Rechnen (z.B. Differentiation, Integration) logisches Schliessen Analyse und Synthese chemischer Formeln Verarbeitung natürlicher Sprache Repräsentation von Wissen (z.B. semantische Netze) ... Frage: Wann wurden diese Sprachen entwickelt? ... D. Rösner PGP 2011 . . . 4 D. Rösner PGP 2011 . . . 5 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Funktionales Programmieren: Scheme Symbolverarbeitung mit Lisp Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme als funktionale Sprache: Sprachmittel für Symbolverarbeitung in Lisp Diskussion von Scheme im Vergleich mit Haskell Atome als elementare Bausteine zusammengesetzte Strukturen auf der Basis verschachtelter Listen Beispiel: mögliche Darstellung eines Syntaxbaums (S (NP (DET Der) (N Mann)) (VP (V fragt) (PP (P nach) (NP (DET dem) (N Weg) (PP (P zum) (NP (N Bahnhof))))))) m.a.W. Gemeinsamkeiten und Unterschiede Haskell als ‘Blaupause’ für funktionale Sprachen Grundlegendes: Interpreter Definition von Assoziationen zwischen Namen und Werten mit define (define <name> <wert>) Scheme ist ein Dialekt von Lisp D. Rösner PGP 2011 . . . Funktionales Programmieren: Scheme D. Rösner PGP 2011 . . . 6 Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Funktionales Programmieren: Scheme Scheme als funktionale Sprache: 7 Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme: Anonyme Funktionen wichtig: Funktionen sind Objekte erster Ordnung eine Interaktion: Syntax: (lambda <parameterliste> <koerper>) überall dort verwendbar, wo auch mit Symbol auf Funktion verwiesen werden kann 1 ]=> (define pi 3.1415) ;Value: pi 1 ]=> pi ;Value: 3.1415 1 ]=> ((lambda (n) (* n n)) 3) 1 ]=> (define quadriere (lambda (zahl) (* zahl zahl))) ;Value: quadriere ;Value: 9 1 ]=> (quadriere pi) ;Value: 9.86902225 D. Rösner PGP 2011 . . . 8 D. Rösner PGP 2011 . . . 9 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Funktionales Programmieren: Scheme Scheme: Anonyme Funktionen Scheme: Übersicht vordefinierte Datentypen alternative Syntax für benannte Funktionen Zahlen: ganze, rationale, Fliesskomma, ... Strings: z.B. "ein String" Zeichen: z.B. #\a #\Z #\* #\space boolesche Werte: #t, #f ... (define (<name> <par-1> ... <par-n>) <koerper>) statt (define <name> (lambda (<par-1> ... <par-n>) <koerper>)) D. Rösner PGP 2011 . . . Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz D. Rösner PGP 2011 . . . 10 Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Funktionales Programmieren: Scheme Scheme: Übersicht 11 Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme vs. Haskell Syntax: wichtigster aggregierter Datentyp: Liste in Scheme: Klammerung innerhalb geschachtelter Ausdrücke in Haskell: Vorrangregeln, Layoutregel Konstruktor: cons Selektoren: car, cdr Prädikate: list?, null? Typisierung? wichtige Kontrollstrukturen: in Scheme: keine Typisierung; in Haskell: strenge Typisierung if cond Striktheit? Scheme: strikt Haskell: non-strikt Funktionen zur Konversion zwischen verschiedenen Typen, z.B. number->string string->list ... D. Rösner PGP 2011 . . . rein funktional (pur)? Scheme: nicht pur, Seiteneffekte möglich z.B. mit set! Haskell: pur 12 D. Rösner PGP 2011 . . . 14 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Funktionales Programmieren: Scheme Scheme vs. Haskell Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Striktheit vs. Non-Striktheit Eine (seiteneffekt-freie) Funktion heisst strikt, wenn gefordert wird, dass alle ihre Argumente definiert sind und so die Evaluationsordnung das Ergebnis nicht verändert. in Scheme: keine Typisierung Typüberprüfung durch vor- oder eigendefinierte Typprädikate Beispiele: Eine Funktion heisst non-strikt, wenn für sie die Forderung nach Striktheit nicht erhoben wird. Eine Sprache heisst strikt, wenn gefordert wird, dass alle ihre Funktionen strikt. number? list? string? ... Eine Sprache heisst non-strikt, wenn sie die Definition non-strikter Funktionen zulässt. s.a. [?], Ch. 11.2.2. D. Rösner PGP 2011 . . . Funktionales Programmieren: Scheme 15 Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz D. Rösner PGP 2011 . . . Funktionales Programmieren: Scheme Scheme vs. Haskell 16 Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme: weitere Aspekte Art der Auswertung? Funktionen mit beliebiger Anzahl von Argumenten möglich (sog. Restparameter, der an Liste gebunden) Beispiel: in Haskell: verzögerte Auswertung (lazy evaluation) für alle Argumente in Scheme: Auswertung für alle Argumente; aber: verzögerte Auswertung und call-by-need möglich mit delay und force s.a. [?], Ch. 11.2.2. (define (avg . nums) (average nums)) average erwartet Liste als Argument Definition: in Scheme nicht vordefiniert vorhanden sind u.a. (define (average nums) (/ (apply + nums) (length nums))) Listenkomprehensionen und Fallunterscheidung durch Pattern Matching D. Rösner PGP 2011 . . . 17 D. Rösner PGP 2011 . . . 18 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Funktionales Programmieren: Scheme Scheme: Funktionen höherer Ordnung Scheme: Funktionen höherer Ordnung map . . . Anwenden einer Funktion auf die Elemente einer Liste und Rückgabe der Liste der Ergebnisse z.B. Falten einer zweistelligen Funktion in eine Liste: > (define (map f l) (if (null? l) ’() (cons (f (car l)) (map f (cdr l))))) (define fold (lambda (f l i) (if (null? l) i ;; identity for f (f (car l) (fold f (cdr l) i))))) > (map (lambda (x) (* x x)) ’(2 3 4 5)) (4 9 16 25) D. Rösner PGP 2011 . . . Funktionales Programmieren: Scheme 20 Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz D. Rösner PGP 2011 . . . Funktionales Programmieren: Scheme Scheme: Funktionen höherer Ordnung 21 Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme vs. Haskell: partielle Anwendungen sind bei Funktionen im Curry-Format möglich Sichtbarkeit von Definitionen die Definitionen auf dem Toplevel von Scheme sind ‘global’ sichtbar wechselseitige Bezugnahme in rekursiven Definitionen ist möglich Beispiel: 1 ]=> (define (isOdd n) (if (<= n 0) () (isEven (- n 1)))) (define curry (lambda (f) (lambda (a) (lambda (b) (f a b))))) 1 ]=> (((curry +) 3) 4) ;Value: 7 ;Value: isodd ... (define curried-plus (curry +)) D. Rösner PGP 2011 . . . Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz 22 D. Rösner PGP 2011 . . . 24 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Funktionales Programmieren: Scheme Scheme vs. Haskell: Sichtbarkeit von Definitionen Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Lokale Definitionen Beispiel cont.: 1 ]=> (define (isEven n) (if (< n 0) () (if (= n 0) #t (isOdd (- n 1))))) Motivation: Vermeiden wiederholter Berechnungen klarer strukturierter Code Beispiel: eine Funktion addPairwise’, die korrespondierende Elemente zweier Zahlenlisten addiert und – falls eine Liste keine Elemente mehr hat – den aktuellen Rest der anderen an die Liste der Paarsummen anhängt ;Value: iseven 1 ]=> (isEven 5) ;Value: () 1 ]=> (isOdd 5) ;Value: #t D. Rösner PGP 2011 . . . Funktionales Programmieren: Scheme 25 Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz D. Rösner PGP 2011 . . . Funktionales Programmieren: Scheme Scheme: lokale Bindungen mit let und let* 26 Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme: lokale Bindungen mit let und let* Syntax: Beispiel: addPairwiseRest als Äquivalent zu addPairwise’ (let ((var1 val1) (var2 val2) ... (varN valN)) <body>) (define (addPairwiseRest list1 list2) (let ((front (addPairwise list1 list2)) (minLength (min (length list1)(length list2)))) (let ((rear (append (drop minLength list1) (drop minLength list2)))) (append front rear)))) Semantik: bei der Auswertung von <body> sind die in var1, var2, ..., varN gebundenen Werte val1, val2, ..., valN verfügbar D. Rösner PGP 2011 . . . 27 D. Rösner PGP 2011 . . . 28 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Funktionales Programmieren: Scheme Scheme: lokale Bindungen mit let und let* Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme: lokale Bindungen mit let und let* Beachte: bei let erfolgt die Auswertung und Bindung in den einzelnen (var1 val1) (var2 val2) ... (varN valN) parallel daher folgendes falsch: let* ist wie let, nur erfolgt Auswertung und Bindung in den einzelnen (var1 val1) (var2 val2) ... (varN valN) sequentiell (define (addPairwiseRest list1 list2) (let ((front (addPairwise list1 list2)) (minLength (min (length list1)(length list2))) (rear (append (drop minLength list1) (drop minLength list2)))) (append front rear))) m.a.W. für die Ausdrücke vali (für 2 <= i <= n) stehen die Bindungen var1, var2, ..., var(i-1) zur Verfügung 1 ]=> (addPairwiseRest ’(4 7 1 1 ) ’(8 15)) ;Unbound variable: minlength D. Rösner PGP 2011 . . . Funktionales Programmieren: Scheme 29 Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz D. Rösner PGP 2011 . . . Funktionales Programmieren: Scheme Scheme: lokale Bindungen mit let und let* 30 Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Beispiel cont. Hilfsfunktionen a la Haskell: (define (addPairwise list1 list2) (if (or (null? list1)(null? list2)) () (cons (+ (car list1)(car list2)) (addPairwise (cdr list1)(cdr list2))))) Beispiel für let*: (define (addPairwiseRest list1 list2) (let* ((front (addPairwise list1 list2)) (minLength (min (length list1)(length list2))) (rear (append (drop minLength list1) (drop minLength list2)))) (append front rear))) (define (take n list) (if (= n 0) () (cons (car list) (take (- n 1) (cdr list))))) 1 ]=> (addPairwiseRest ’(4 7 1 1 ) ’(8 15)) ;Value 12: (12 22 1 1) D. Rösner PGP 2011 . . . (define (drop n list) (if (= n 0) list (drop (- n 1) (cdr list)))) 31 D. Rösner PGP 2011 . . . 32 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Funktionales Programmieren: Scheme Scheme: Berechnung Quadratwurzel mit Newton-Verfahren Blockstruktur: (define (sqrt x) (define (good-enough? guess x) (< (abs (- (square guess) x)) .001)) (define (improve guess x) (average guess (/ x guess))) (define (sqrt-iter guess x) (if (good-enough? guess x) guess (sqrt-iter (improve guess x) x))) (define (sqrt x) (sqrt-iter 1 x)) (define (sqrt-iter guess x) (if (good-enough? guess x) guess (sqrt-iter (improve guess x) x))) (define (good-enough? guess x) (< (abs (- (square guess) x)) .001)) (sqrt-iter 1 x) ) (define (improve guess x) (average guess (/ x guess))) D. Rösner PGP 2011 . . . Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz 34 Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz D. Rösner PGP 2011 . . . Funktionales Programmieren: Scheme Interne Definitionen 35 Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz lexikalischer Skopus: Ausnutzen, daß x in sqrt gebunden (lexikalischer Skopus): (define (sqrt x) (define (good-enough? guess) (< (abs (- (square guess) x)) .001)) (define (improve guess) (average guess (/ x guess))) (define (sqrt-iter guess) (if (good-enough? guess) guess (sqrt-iter (improve guess)))) freie Variable in einer Prozedur verweisen auf Variable in umfassenden Prozeduren m.a.W.: Werte freier Variable werden in der Umgebung gesucht, in der die Prozedur definiert wurde (sqrt-iter 1)) D. Rösner PGP 2011 . . . 36 D. Rösner PGP 2011 . . . 37 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Funktionales Programmieren: Scheme Scheme: Programm-Daten-Äquivalenz Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme: Programm-Daten-Äquivalenz Programme haben die Form von Listen Beispiel cont.: Scheme (und Lisp) sind homoikonisch, d.h. selbstrepräsentierend (define compose2 (lambda (f g) (eval (list ’lambda ’(x) (list f (list g ’x))) (scheme-report-environment 5)))) Programme können mit allen Listenfunktionen bearbeitet werden Beispiel: 1 ]=> ((compose2 car cdr) ’(1 2 3)) (define compose (lambda (f g) (lambda (x) (f (g x))))) ;Value: 2 1 ]=> ((compose car cdr) ’(1 2 3)) ;Value: 2 D. Rösner PGP 2011 . . . Funktionales Programmieren: Scheme 39 Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Literatur: I Michael Lee Scott. Programming Language Pragmatics. Academic Press, San Diego, CA, USA, 2000. ISBN 1-55860-578-9. D. Rösner PGP 2011 . . . 41 D. Rösner PGP 2011 . . . 40