Scheme als funktionale Sprache: Scheme als funktionale Sprache: • Funktionen Objekte erster Ordnung • Diskussion von Scheme im Vergleich mit Haskell 1 ]=> (define pi 3.1415) ;Value: pi • m.a.W. Gemeinsamkeiten und Unterschiede • Haskell als ‘Blaupause’ für funktionale Sprachen 1 ]=> pi ;Value: 3.1415 Grundlegendes: 1 ]=> (define quadriere (lambda (zahl) (* zahl zahl))) ;Value: quadriere • Interpreter • Definition von Werten mit define 1 ]=> (quadriere pi) ;Value: 9.86902225 (define <name> <wert>) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 209 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), Scheme: Anonyme Funktionen 210 Scheme: Anonyme Funktionen alternative Syntax für benannte Funktionen • Syntax: • (define (<name> <par-1> ... <par-n>) <koerper>) (lambda <parameterliste> <koerper>) • überall dort verwendbar, wo auch mit Symbol auf Funktion verwiesen werden kann 1 ]=> ((lambda (n) (* n n)) 3) • statt (define <name> (lambda (<par-1> ... <par-n>) <koerper>)) ;Value: 9 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 211 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 212 Scheme vs. Haskell Scheme: Übersicht • in Scheme: Klammerung innerhalb geschachtelter Ausdrücke; in Haskell: Vorrangregeln • vordefinierte Datentypen – Zahlen: ganze, rationale, Fliesskomma, ... – Strings – Zeichen – boolesche Werte: #t, #f • in Scheme: keine Typisierung; in Haskell: strenge Typisierung • Scheme: strikt; Haskell: non-strikt • wichtigster aggregierter Datentyp: Liste – Konstruktor: cons – Selektoren: car, cdr – Prädikate: null? • in Scheme nicht vordefiniert vorhanden sind u.a. Listenkomprehension und Fallunterscheidung durch Pattern Matching • Kontrollstrukturen: – if – cond • 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 • Funktionen zur Konversion zwischen verschiedenen Typen c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 213 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), Striktheit vs. Non-Striktheit Scheme: Programm-Daten-Äquivalenz • 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. • Programme haben die Form von Listen • Scheme (und Lisp) sind homoikonisch, d.h. selbstrepräsentierend • Programme können mit allen Listenfunktionen bearbeitet werden • Beispiel: • Eine Sprache heisst strikt, wenn gefordert wird, dass alle ihre Funktionen strikt. (define compose (lambda (f g) (lambda (x) (f (g x))))) • Eine Sprache heisst non-strikt, wenn sie die Definition non-strikter Funktionen zulässt. 1 ]=> ((compose car cdr) ’(1 2 3)) ;Value: 2 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 214 215 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 216 Scheme: weitere Aspekte Scheme: Programm-Daten-Äquivalenz • Funktionen mit beliebiger Anzahl von Argumenten möglich (sog. Restparameter, der an Liste gebunden) • Beispiel cont.: (define compose2 (lambda (f g) (eval (list ’lambda ’(x) (list f (list g ’x))) (scheme-report-environment 5)))) • Beispiel: (define (avg . nums) (average nums)) 1 ]=> ((compose2 car cdr) ’(1 2 3)) • average erwartet Liste als Argument ;Value: 2 • Definition: (define (average nums)(/ (apply + nums) (length nums))) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 217 Scheme: Funktionen höherer Ordnung c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 218 Scheme: Funktionen höherer Ordnung • z.B. Falten einer zweistelligen Funktion in eine Liste: • partielle Anwendungen sind bei Funktionen im Curry-Format möglich (define fold (lambda (f l i) (if (null? l) i ;; identity for f (f (car l) (fold f (cdr l) i))))) (define curry (lambda (f) (lambda (a) (lambda (b) (f a b))))) 1 ]=> (((curry +) 3) 4) ;Value: 7 (define curried-plus (curry +)) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 219 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 220 Prozeduren und Prozesse: Prozeduren und Prozesse: Beispiel: Berechnung Fakultät Beispiel: Berechnung Fakultät als linear-rekursiver Prozess Definition: n! = n * (n - 1) * . . . * 2 *1 (factorial 6) (* 6 (factorial 5)) (* 6 (*5 (factorial 4))) (* 6 (*5 (* 4 (factorial 3)))) (* 6 (*5 (* 4 (* 3 (factorial 2))))) (* 6 (*5 (* 4 (* 3 (* 2 (factorial 1)))))) (* 6 (*5 (* 4 (* 3 (* 2 1))))) (* 6 (*5 (* 4 (* 3 2)))) (* 6 (*5 (* 4 6))) (* 6 (*5 24)) (* 6 120) 720 Zusammenhang: n! = n * (n - 1)! (define (factorial n) (if (= n 1) 1 (* n (factorial (- n 1))))) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 221 Beispiel: Berechnung Fakultät als linear-iterativer Prozess c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 222 Beispiel: Berechnung Fakultät als linear-iterativer Prozess in jedem Schritt: (factorial 6) (fact-iter 1 1 6) (fact-iter 1 2 6) (fact-iter 2 3 6) (fact-iter 6 4 6) (fact-iter 24 5 6) (fact-iter 120 6 6) (fact-iter 720 7 6) 720 product <-- counter * product counter <-- counter + 1 (define (factorial n) (fac-iter 1 1 n)) (define (fact-iter product counter max-count) (if (> counter max-count) product (fact-iter (* counter product) (+ counter 1) max-count))) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 223 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 224 Scheme vs. Haskell: Scheme vs. Haskell: Sichtbarkeit von Definitionen Sichtbarkeit von Definitionen Beispiel cont.: • die Definitionen auf dem Toplevel von Scheme sind ‘global’ sichtbar 1 ]=> (define (isEven n) (if (< n 0) () (if (= n 0) #t (isOdd (- n 1))))) • wechselseitige Bezugnahme in rekursiven Definitionen ist möglich ;Value: iseven • Beispiel: 1 ]=> (isEven 5) 1 ]=> (define (isOdd n) (if (<= n 0) () (isEven (- n 1)))) ;Value: () ;Value: isodd ... 1 ]=> (isOdd 5) ;Value: #t c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 225 Lokale Definitionen c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 226 Scheme: lokale Bindungen mit let und let* Motivation: • Syntax: (let ((var1 val1) (var2 val2) ... (varN valN)) <body>) • Vermeiden wiederholter Berechnungen • Semantik: bei der Auswertung von <body> sind die in var1, var2, ..., varN gebundenen Werte val1, val2, ..., valN verfügbar • klarer strukturierter Code • Beispiel: addPairwiseRest als Äquivalent zu addPairwise’ • 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 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 227 (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)))) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 228 Scheme: lokale Bindungen mit let und let* 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 • let* ist wie let, nur erfolgt Auswertung und Bindung in den einzelnen (var1 val1) (var2 val2) ... (varN valN) sequentiell • daher folgendes falsch: • 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 (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 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 229 Scheme: lokale Bindungen mit let und let* 230 Beispiel cont. • Hilfsfunktionen a la Haskell: • 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)) (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)))) ;Value 12: (12 22 1 1) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 231 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 232 Blockstruktur: Scheme: Berechnung Quadratwurzel mit Newton-Verfahren (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))) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 233 Interne Definitionen c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 234 lexikalischer Skopus: Ausnutzen, daß x in sqrt gebunden (lexikalischer Skopus): • freie Variable in einer Prozedur verweisen auf Variable in umfassenden Prozeduren; (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)))) • m.a.W.: Werte freier Variable werden in der Umgebung gesucht, in der die Prozedur definiert wurde (sqrt-iter 1)) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 235 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 236 Ströme: Motivation Ströme: Sprachmittel in SCHEME • Berechnungen auf Kollektionen von Daten • Konstruktor: cons-stream • abstrakt gesehen: Sequenz von Datenobjekten • Selektoren: head, tail • mögl. Implementation: durch Listen • Beziehung: für bel. a,b und x: Wenn x ist (cons-stream a b), dann (head x) ist a und (tail x) ist b. • bessere Implementation: mit verzögerter Auswertung Ströme repräsentieren den zeitlichen Verlauf der durch sie modellierten Systeme c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 237 Ströme: Implementation • Konstante: the-empty-stream • Prädikat: empty-stream? c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 238 Implementation cont. • Implementation von Strömen durch verzögerte Evaluation (delayed evaluation, lazy evaluation) • in normalen Listen: car und cdr werden zur Konstruktionszeit evaluiert • in Strömen: tail erst zur Zugriffszeit evaluieren (call-by-need) • (cons-stream <a><b>) . . . Spezialform, die gleichwertig mit (cons <a> (delay <b>)) – d.h. Strom wird als Paar dargestellt – im cdr steht promise zur Berechnung des tail • (define (head stream) (car stream)) • Spezialform delay: (delay <exp>) . . . liefert ein verzögertes Objekt, sog. promise, dass erst mit force zur Ausführung veranlasst wird • (define (tail stream) (force (cdr stream))) • (force <promise>) . . . veranlasst Ausführung von <promise> c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 239 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 240 Unendlich lange Ströme Unendlich lange Ströme cont. Sieb des Eratosthenes • Strom positiver ganzer Zahlen (define (sieve stream) (cons-stream (head stream) (sieve (filter (lambda (x) (not (divisible? x (head stream)))) (tail stream))))) (define (integers-starting-from n) (cons-stream n (integers-starting-from (1+ n)))) (define integers (integers-starting-from 1)) • Strom von Fibonacci-Zahlen (define (fibgen a b) (cons-stream a (fibgen b (+ a b)))) (define primes (sieve (integers-starting-from 2))) (define fibs (fibgen 0 1)) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 241 Implizit definierte Ströme c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 242 Implizit definierte Ströme cont. Hilfsfunktion: Elementweises Addieren zweier Ströme • Strom positiver ganzer Zahlen (define (add-streams s1 s2) (cond ((empty-stream? s1) s2) ((empty-stream? s2) s1) (else (cons-stream (+ (head s1)(head s2)) (add-streams (tail s1) (tail s2)))))) (define ones (cons-stream 1 ones)) (define integers (cons-stream 1 (add-streams ones integers))) • Strom der Fibonacci-Zahlen (define fibs (cons-stream 0 (cons-stream 1 (add-streams (tail fibs) fibs)))) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 243 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 244 Funktionale Programmierung: Haskell Funktionale Programmierung: Scheme • rein funktional • funktional und imperative Elemente • non-strikt • strikt • strenge Typisierung • keine Typisierung • lazy evaluation • eager evaluation (aber: delay) • Listenkomprehension • – • – • Programm-Daten-Äquivalenz • Pattern matching in Definitionen • – c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 245 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), Rein funktionale Sprachen Funktionskomposition als Sprachmittel • in Scheme: • Programmieren ohne Seiteneffekte (define (compose f1 f2) (lambda (x) (f1 (f2 x)))) • Seiteneffekte können Programme schwer zu lesen und schwer zu kompilieren machen • Abwesenheit von Seiteneffekten: – Ausdrücke sind referentiell transparent – unabhängig von Ausführungsordnung – Denken in Gleichungen möglich – Gleichheit zweier Ausdrücke ist nicht zeitabhängig c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 246 247 • Beispiel für Anwendung: (define (define (define (define (flatten tree) ...) (remove-duplicates list) ...) list-of-atoms (compose remove-duplicates flatten)) nr-of-atoms (compose length list-of-atoms)) ; 1 ]=> tree ;Value 6: ((a (b (c d) e) (b f g) h (c d))) ; 1 ]=> (list-of-atoms tree) ;Value 7: (a e b f g h c d) ; 1 ]=> (nr-of-atoms tree) ;Value: 8 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 248