Funktionales Programmieren: Scheme Programmierparadigmen Einführung in Scheme D. Rösner Institut für Wissens- und Sprachverarbeitung Fakultät für Informatik Otto-von-Guericke Universität Magdeburg c Sommer 2011, 9. Juni 2011, 2011 D.Rösner D. Rösner PGP 2011 . . . 1 Funktionales Programmieren: Scheme Gliederung 1 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz D. Rösner PGP 2011 . . . 2 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Historie Frage: Welches sind die zwei ältesten und (in modernisierter Form) immer noch benutzten Programmiersprachen? ... ... Frage: Wann wurden diese Sprachen entwickelt? ... D. Rösner PGP 2011 . . . 4 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Historie unterschiedliche Ausrichtungen: Fortran: numerische Berechnungen 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) ... 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 Symbolverarbeitung mit Lisp Sprachmittel für Symbolverarbeitung in Lisp 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))))))) Scheme ist ein Dialekt von Lisp D. Rösner PGP 2011 . . . 6 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme als funktionale Sprache: Diskussion von Scheme im Vergleich mit Haskell 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>) D. Rösner PGP 2011 . . . 7 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme als funktionale Sprache: wichtig: Funktionen sind Objekte erster Ordnung eine Interaktion: 1 ]=> (define pi 3.1415) ;Value: pi 1 ]=> pi ;Value: 3.1415 1 ]=> (define quadriere (lambda (zahl) (* zahl zahl))) ;Value: quadriere 1 ]=> (quadriere pi) ;Value: 9.86902225 D. Rösner PGP 2011 . . . 8 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme: Anonyme Funktionen Syntax: (lambda <parameterliste> <koerper>) überall dort verwendbar, wo auch mit Symbol auf Funktion verwiesen werden kann 1 ]=> ((lambda (n) (* n n)) 3) ;Value: 9 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 Scheme: Anonyme Funktionen alternative Syntax für benannte Funktionen (define (<name> <par-1> ... <par-n>) <koerper>) statt (define <name> (lambda (<par-1> ... <par-n>) <koerper>)) D. Rösner PGP 2011 . . . 10 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme: Übersicht vordefinierte Datentypen Zahlen: ganze, rationale, Fliesskomma, ... Strings: z.B. "ein String" Zeichen: z.B. #\a #\Z #\* #\space boolesche Werte: #t, #f ... D. Rösner PGP 2011 . . . 11 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme: Übersicht wichtigster aggregierter Datentyp: Liste Konstruktor: cons Selektoren: car, cdr Prädikate: list?, null? wichtige Kontrollstrukturen: if cond Funktionen zur Konversion zwischen verschiedenen Typen, z.B. number->string string->list ... D. Rösner PGP 2011 . . . 12 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme vs. Haskell Syntax: in Scheme: Klammerung innerhalb geschachtelter Ausdrücke in Haskell: Vorrangregeln, Layoutregel Typisierung? in Scheme: keine Typisierung; in Haskell: strenge Typisierung Striktheit? Scheme: strikt Haskell: non-strikt rein funktional (pur)? Scheme: nicht pur, Seiteneffekte möglich z.B. mit set! Haskell: pur 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 Scheme vs. Haskell in Scheme: keine Typisierung Typüberprüfung durch vor- oder eigendefinierte Typprädikate Beispiele: number? list? string? ... D. Rösner PGP 2011 . . . 15 Funktionales Programmieren: Scheme 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. 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. 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 . . . 16 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme vs. Haskell Art der Auswertung? 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. in Scheme nicht vordefiniert vorhanden sind u.a. Listenkomprehensionen und Fallunterscheidung durch Pattern Matching D. Rösner PGP 2011 . . . 17 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme: weitere Aspekte Funktionen mit beliebiger Anzahl von Argumenten möglich (sog. Restparameter, der an Liste gebunden) Beispiel: (define (avg . nums) (average nums)) average erwartet Liste als Argument Definition: (define (average nums) (/ (apply + nums) (length nums))) 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 Scheme: Funktionen höherer Ordnung map . . . Anwenden einer Funktion auf die Elemente einer Liste und Rückgabe der Liste der Ergebnisse > (define (map f l) (if (null? l) ’() (cons (f (car l)) (map f (cdr l))))) > (map (lambda (x) (* x x)) ’(2 3 4 5)) (4 9 16 25) D. Rösner PGP 2011 . . . 20 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme: Funktionen höherer Ordnung z.B. Falten einer zweistelligen Funktion in eine Liste: (define fold (lambda (f l i) (if (null? l) i ;; identity for f (f (car l) (fold f (cdr l) i))))) D. Rösner PGP 2011 . . . 21 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme: Funktionen höherer Ordnung partielle Anwendungen sind bei Funktionen im Curry-Format möglich (define curry (lambda (f) (lambda (a) (lambda (b) (f a b))))) 1 ]=> (((curry +) 3) 4) ;Value: 7 (define curried-plus (curry +)) D. Rösner PGP 2011 . . . 22 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme vs. Haskell: 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)))) ;Value: isodd ... 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 Scheme vs. Haskell: Sichtbarkeit von Definitionen Beispiel cont.: 1 ]=> (define (isEven n) (if (< n 0) () (if (= n 0) #t (isOdd (- n 1))))) ;Value: iseven 1 ]=> (isEven 5) ;Value: () 1 ]=> (isOdd 5) ;Value: #t D. Rösner PGP 2011 . . . 25 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Lokale Definitionen 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 D. Rösner PGP 2011 . . . 26 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme: lokale Bindungen mit let und let* Syntax: (let ((var1 val1) (var2 val2) ... (varN valN)) <body>) 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 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme: lokale Bindungen mit let und let* Beispiel: addPairwiseRest als Äquivalent zu addPairwise’ (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)))) 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 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: (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))) 1 ]=> (addPairwiseRest ’(4 7 1 1 ) ’(8 15)) ;Unbound variable: minlength D. Rösner PGP 2011 . . . 29 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme: lokale Bindungen mit let und let* let* ist wie let, nur erfolgt Auswertung und Bindung in den einzelnen (var1 val1) (var2 val2) ... (varN valN) sequentiell 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 D. Rösner PGP 2011 . . . 30 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme: lokale Bindungen mit let und let* 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))) 1 ]=> (addPairwiseRest ’(4 7 1 1 ) ’(8 15)) ;Value 12: (12 22 1 1) D. Rösner PGP 2011 . . . 31 Funktionales Programmieren: Scheme 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))))) (define (take n list) (if (= n 0) () (cons (car list) (take (- n 1) (cdr list))))) (define (drop n list) (if (= n 0) list (drop (- n 1) (cdr list)))) 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 Scheme: Berechnung Quadratwurzel mit Newton-Verfahren (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)) (define (improve guess x) (average guess (/ x guess))) D. Rösner PGP 2011 . . . 34 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz 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))) (sqrt-iter 1 x) ) D. Rösner PGP 2011 . . . 35 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Interne Definitionen 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)))) (sqrt-iter 1)) D. Rösner PGP 2011 . . . 36 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz lexikalischer Skopus: 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 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 Scheme: Programm-Daten-Äquivalenz Programme haben die Form von Listen Scheme (und Lisp) sind homoikonisch, d.h. selbstrepräsentierend Programme können mit allen Listenfunktionen bearbeitet werden Beispiel: (define compose (lambda (f g) (lambda (x) (f (g x))))) 1 ]=> ((compose car cdr) ’(1 2 3)) ;Value: 2 D. Rösner PGP 2011 . . . 39 Funktionales Programmieren: Scheme Einführung Scheme vs. Haskell Funktionen höherer Ordnung Skopus Beispiel: Newton-Verfahren Programm-Daten-Äquivalenz Scheme: Programm-Daten-Äquivalenz Beispiel cont.: (define compose2 (lambda (f g) (eval (list ’lambda ’(x) (list f (list g ’x))) (scheme-report-environment 5)))) 1 ]=> ((compose2 car cdr) ’(1 2 3)) ;Value: 2 D. Rösner PGP 2011 . . . 40 Funktionales Programmieren: Scheme 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