3.7 Lambda-Ausdruck zur Funktionsdefinition

Werbung
3.7 Lambda-Ausdruck zur Funktionsdefinition
Allgemeine Struktur der special form zur Funktionsdefinition:
(lambda
(<parameter-list>)
expression)
ƒ <parameter-list>:
eine Liste von Namen par1 … parn , auch formale Parameter
genannt; die Liste kann auch leer sein
ƒ expression:
Ausdruck, auch Rumpf (body) der Funktion genannt
Die Auswertung eines solchen lambda-Ausdrucks durch den
Scheme-Interpreter liefert eine Funktion mit entsprechender
Parameterzahl, die genau wie die vordefinierten Funktionen
benutzt werden kann
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
175
3.7 Lambda-Ausdruck zur Funktionsdefinition
Beispiel:
> (lambda (x) (* x 2))
(lambda (a1) ...)
> ((lambda (x) (* x 2)) 5)
10
Allgemeine Form der Funktionsanwendung:
((lambda (<parameter-list>) <exp>) <argument-list>)
ƒ <argument-list>:
Liste exp1 … expn von Ausdrücken, die zu n Argumenten arg1 …
argn ausgewertet werden müssen. Die Zuordnung zwischen diesen
Werten und den formalen Parametern erfolgt von links nach rechts,
d.h. durch Bindung von pari an argi.
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
176
3.7 Lambda-Ausdruck zur Funktionsdefinition
Namensbindung:
ƒ Formale Parameter werden im Rumpf der Funktion vor Bindungen
gleicher Namen in der Umgebung geschützt
ƒ Äußere Bindungen haben keinen Einfluß auf durch lambda als
Parameter festgelegte Namen!
> (define x 100)
> (define y 200)
Diese drei x haben miteinander
nichts zu tun!
> ((lambda (x) (+ x y)) 33)
233
> ((lambda (x) (+ x y)) 2)
202
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
177
3.7 Lambda-Ausdruck zur Funktionsdefinition
Benennung von Funktionen wie bei der Bindung von Werten an
Namen durch define
Beispiel:
> ((lambda (x) (∗ x x)) 5 )
25
Alternativ:
> (define square (lambda (x) (∗ x x)))
> (square 5)
25
Auswertung:
(square 5)
→ ((lambda (x) (∗ x x) ) 5)
→ (∗ 5 5)
→ 25
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
178
3.7 Lambda-Ausdruck zur Funktionsdefinition
Scheinbar unterschiedliches Verhalten bei Benutzung von Namen aus
der Umgebung:
> (define b (+ a 15))
reference to an identifier before its definition: a
> (define f (lambda (x) (+ a x))))
;; Keine Fehlermeldung
> (f 15)
reference to an identifier before its definition: a
> (define a 1)
> (f 15)
16
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
179
3.7 Lambda-Ausdruck zur Funktionsdefinition
Die bereits bekannte Funktionsdefinition:
(define (<function-name> <par-list>) <exp>)
ist die Kurzform von
(define <function-name> (lambda (<par-list>) <exp>))
Beispiel:
(define square (lambda (x) (∗ x x)))
ist äquivalent zu:
(define (square x) (∗ x x))
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
180
3.8 Datentyp Funktionen
Datentyp Funktion als Werkzeug zur Abstraktion:
Scheme = Lisp + „first class“–Funktionen
ƒ Funktionen werden in einer Programmiersprache als „Werte erster
Klasse“ behandelt, wenn man sie
• als Parameter an andere Funktionen übergeben,
• zur Laufzeit erzeugen,
• als Resultate von Funktionen zurückgeben kann
ƒ Funktionen, die andere Funktionen als Parameter und/oder Resultat
haben, heißen Funktionen höherer Ordnung. Scheme war eine der
ersten Sprachen, die dieses Konzept konsequent umgesetzt hat.
ƒ „First class“–Funktionen verbessern die Expressivität einer
Programmiersprache, also ihre Fähigkeit, komplexe Sachverhalte
möglichst klar und einfach auszudrücken
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
181
3.8.1 Funktionen als Parameter
Zusammenfassung von Listen: Aus zwei ähnlichen Funktionen
(define (sum l)
(cond [(empty? (rest l)) (first l)]
[else (+ (first l) (sum (rest l)))]))
(define (mult l)
(cond [(empty? (rest l)) (first l)]
[else (* (first l) (mult (rest l)))])
definieren wir eine abstraktere Funktion (vgl. 3.6.3 Listenfunktionen):
(define (list-op op l)
(cond [(empty? (rest l)) (first l)]
[else (op (first l) (list-op op (rest l)))]))
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
182
3.8.1 Funktionen als Parameter
Beispiel:
> (list-op + (list 1 2 3 4 5))
15
> (list-op * (list 1 2 3 4 5))
120
> (list-op min (list 1 2 3 4 5))
1
> (list-op max (list 1 2 3 4 5))
5
Quiz: Was berechnet der folgende Ausdruck?
(list-op + (map sqr (list 1 2 3 4 5)))
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
183
3.8.1 Funktionen als Parameter
Die Sortierfunktion sort (3.6.3 Listenfunktion) war auf die aufsteigende
Sortierung von Zahlen beschränkt. Wenn man eine Vergleichsfunktion als
Argument übergibt, wird sie erheblich vielseitiger einsetzbar.
(define (insert x l)
(cond [(empty? l) (list x)]
[(<= x (car l)) (cons x l)]
[else (cons (car l) (insert x (cdr l)))]))
(define (sort l)
(cond [(empty? l) empty]
[else (insert (first l) (sort (rest l)))]))
(define (insert x l rel)
(cond [(empty? l) (list x)]
[(rel x (car l)) (cons x l)]
[else (cons (car l) (insert x (cdr l) rel))]))
(define (sort l rel)
(cond [(empty? l) empty]
[else (insert (first l) (sort (rest l) rel) rel)]))
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
184
3.8.1 Funktionen als Parameter
Beispiel:
> (sort (list 17 3 -1 12 18 22 5 4 9 2) <)
(list -1 2 3 4 5 9 12 17 18 22)
> (sort (list 17 3 -1 12 18 22 5 4 9 2) >)
(list 22 18 17 12 9 5 4 3 2 -1)
;; string>? testet die lexikographische Relation
;; zweier Strings
> (sort (list "ab" "a" "abc" "baa") string<?)
(list "a" "ab" "abc" "baa")
> (sort (list "ab" "a" "abc" "baa") string>?)
(list "baa" "abc" "ab" "a")
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
185
3.8.1 Funktionen als Parameter
Beispiel: Sortieren von Zahlenpaaren
Ein Paar (a1, a2) ist lexikographisch kleiner als ein Paar (b1, b2) wenn
a1 < b1 oder (a1 = b1 und a2 < b2) gilt
(define (pair<? a b)
(or (< (car a) (car b))
(and (= (car a) (car b)) (< (cadr a) (cadr b)))))
> (pair<? (list 8 5) (list 7 9))
False
> (sort '((3 1) (5 2) (-1 -2) (-1 9)) pair<?)
(list (list -1 -2) (list -1 9) (list 3 1) (list 5 2))
(define (pair>=? a b) (not (pair<? a b)))
> (sort '((3 1) (5 2) (-1 -2) (-1 9)) pair>=?)
(list (list 5 2) (list 3 1) (list -1 9) (list -1 -2))
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
186
3.8.1 Funktionen als Parameter
Beispiel: Sortieren von Listen
(define (list<? a b)
(cond [(empty? a) true]
;; empty ist kleiner als alles
[(empty? b) false] ;; und größer als nichts
[else (or (< (first a) (first b))
(and (= (first a) (first b)) (list<? (rest a) (rest b))))]))
> (list<? '(2 4 3) '(1 5 7))
false
> (sort '((2 4 3) (1 5 7) (3 8 1)) list<?)
(list (list 1 5 7) (list 2 4 3) (list 3 8 1))
> (sort '((2 4 3) (1 5 7) (3) (-1 0)) list<?)
(list (list -1 0) (list 1 5 7) (list 2 4 3) (list 3))
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
187
3.8.1 Funktionen als Parameter
Funktionale Abstraktion:
ƒ Viele Daten- und Funktionsdefinitionen sehen ähnlich aus:
• Daten: Symbolliste, Zahlenliste, ……
• Funktionen: >, <, ……
ƒ Wiederholungen sind die Quelle vieler Programmfehler
Æ Ein wichtigster Schritt beim Schreiben eines Programms besteht
in der Elimination von Wiederholungen
ƒ Den Prozess, zwei ähnliche Funktionen zu einer Definition
zusammenzuführen, nennt man funktionale Abstraktion. Funktionen
als Parameter zu übergeben bietet eine Möglichkeit dazu.
ƒ Abstrakte Versionen von Funktionen zu definieren ist sehr nützlich:
• Eine einzige Funktion kann viele verschiedene Aufgaben erfüllen
• Eine einzige Funktion kann mehrere ähnliche Probleme auf
einmal lösen
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
188
3.8.2 Funktionen als Ergebnis
Beispiel: Die folgende Funktion quotient gibt zu einer einstelligen
Funktion f die Quotientenfunktion zurück, die zu einem
vorgegebenen Argument n den Wert 1 / f(n) berechnet:
(define (quotient f) (lambda (arg) (/ 1 (f arg))))
> (quotient sqr)
(lambda (a1) ...)
> ((quotient sqr) 2)
0.25
quotient wird folgendermaßen ausgewertet:
((quotient sqr) 2)
→ ((lambda (arg) (/ 1 (sqr arg))) 2)
→ (/ 1 (sqr 2))
→ (/ 1 4)
→ 0.25
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
189
3.8.2 Funktionen als Ergebnis
>
(define qsqr (quotient sqr))
> (qsqr 2)
0.25
Bedeutung von lambda-Ausdrücken:
Der lambda-Ausduck zur Funktionsdefinition gibt uns eine natürliche
Lösung für die Trennung zweier Ebenen:
ƒ Die Funktion selbst, die mittels lambda abhängig von anderen
Größen flexibel definiert und benannt werden kann
ƒ Die Argumente der definierten Funktion
Dadurch kann man beliebig komplexe Funktionen vom Programm aus
erzeugen (als Ergebnis von Funktionen) und weiter verwenden
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
190
3.8.2 Funktionen als Ergebnis
Beispiel: Für zwei einstellige Funktionen f: A → B und g: B → C wird
die Komposition g o f: A → C mit g o f(a) = g(f(a)) durch folgende
Funktion realisiert:
(define
(define
(define
(define
(define
(komp f g) (lambda (arg) (g (f arg))))
(mal2 x) (∗ 2 x))
(plus1 x) (+ x 1))
mal2plus1 (komp mal2 plus1))
plus1mal2 (komp plus1 mal2))
(mal2plus1 10) →
((lambda (arg) (plus1 (mal2 arg))) 10) →
(plus1 (mal2 10)) → (plus1 20) → 21
(plus1mal2 10) →
((lambda (arg) (mal2 (plus1 arg))) 10) →
(mal2 (plus1 10)) → (mal2 11) → 22
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
191
3.8.2 Funktionen als Ergebnis
Beispiel: Die folgende Funktion polynom konstruiert aus den
Koeffizienten a0, a1 und a2 die zugehörige Polynomfunktion
a0 + a1 ∗ x + a2 ∗ x2
die für ein vorgegebenes x den entsprechenden Wert berechnet
(define (polynom a0 a1 a2)
(lambda (x) (+ a0 (* a1 x) (* a2 (sqr x)))))
>(define p123 (polynom 1 2 3))
;; p123 = 1 + 2*x + 3*x2
> (p123 2)
17
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
192
3.8.2 Funktionen als Ergebnis
Beispiel: Konstruktion einer approximierenden Ableitungsfunktion
nach der Regel
f ′( x ) ≈
f ( x + dx ) − f ( x )
dx
(define (derive f dx)
(lambda (x) (/ (- (f (+ x dx)) (f x)) dx)))
> (derive (polynom 0 0 1) 0.00001) 4)
8.00001
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
193
3.8.2 Funktionen als Ergebnis
Ein Vorteil der funktionalen Ausdrucksweise liegt darin, dass man eine
Gruppe verwandter Funktionen relativ leicht zu einer gemeinsamen
Funktion „abstrahieren“ kann
Beispiel: Schnell wachsende Funktionen
(define (plus x y)
(if (= y 0) x (+ 1 (plus x (- y 1)))))
(define (times x y)
(if (= y 0) 0 (plus x (times x (- y 1)))))
(define (power x y)
(if (= y 0) 1 (times x (power x (- y 1)))))
(define (super x y)
(if (= y 0) 1 (power x (super x (- y 1)))))
(define (SUPER x y)
(if (= y 0) 1 (super x (SUPER x (- y 1)))))
……
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
194
3.8.2 Funktionen als Ergebnis
Dann gilt für die Funktion super:
(super x y) =
Bsp. (super 2 3) =
(y mal)
= 16. Die Funktion SUPER wächst noch sehr viel schneller.
Die Verallgemeinerung des Musters, nach dem diese Funktionen gestrickt sind,
sieht von n = 2 an in funktionaler Schreibweise so aus:
.....
(define (power x y)
(if (= y 0) 1 (times x (power x (- y 1)))))
(define (super x y)
(if (= y 0) 1 (power x (super x (- y 1)))))
(define (hyper n)
(if (= n 2) times
(lambda (x y) (if (= y 0) 1 ((hyper (- n 1)) x ((hyper n) x (- y 1)))))))
Es gilt (hyper 3) = power, (hyper 4) = super, ……
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
195
3.8.2 Funktionen als Ergebnis
> ((hyper 4) 2 3)
16
Die Funktionen (hyper n) wachsen schon für kleine n so schnell, dass
jeder Scheme-Interpreter Probleme bekommt. Der Nutzen liegt eher in
der Theoretischen Informatik.
Wenn man die Argumente x, y mit n zusammen wachsen lässt, erhält
man eine extrem schnell wachsende Funktion:
(define (ackermann n) ((hyper n) n n))
Es gilt:
(ackermann 1) = (plus 1 1) = 2
(ackermann 2) = (times 2 2) = 4
(ackermann 3) = (power 3 3) = 3^3 = 27
(ackermann 4) = (super 4 4) = 4^4^4^4
Die letzte Zahl hat bereits ca. 10153 Stellen (nicht: 153 Stellen!)
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
196
3.8.2 Funktionen als Ergebnis
Diese (genauer: eine sehr ähnliche)
Funktion wurde von Wilhelm Ackermann
(1896 – 1962) im Jahr 1926 konstruiert.
Ackermann war Assistent von David Hilbert
in Göttingen, später Lehrer in Burgsteinfurt
und Honorarprofessor an der WWU
Münster.
Anwendung in der Theoretischen Informatik: Die Ackermannsche
Funktion ist berechenbar, aber nicht primitiv rekursiv. Damit widerlegte
Ackermann eine Vermutung von Hilbert.
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
197
3.8.2 Funktionen als Ergebnis
Zusammenfassung:
ƒ „First class“–Funktionen erhöhen die Ausdruckskraft einer
Programmiersprache und ermöglichen die Beschreibung komplexer
Zusammenhänge in sehr kompakter Form
ƒ Der Preis dafür ist ein höheres Abstraktionsniveau im Denken:
Funktionen, die nur auf elementaren Daten wie Zahlen etc. arbeiten,
sind leichter zu verstehen, als Funktionen, die andere Funktionen
verarbeiten und/oder erzeugen
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
198
3.9 Blockstrukturen: Umgebungen und Scoping
Umgebung (Kontext): Der Scheme-Interpreter unterhält eine Art
Speicher, um Name-Wert Paare zu verwalten (vgl. 3.3.2. Bindungen
und Umgebungen)
ƒ Start in einer Umgebung, die alle vordefinierten Basisfunktionen /
Namen
ƒ Durch define werden weitere Namen an einfache Werte oder
Funktionen gebunden, Ausdrücke ausgewertet, Namen re-definiert
(erneutes define für schon gebundene Namen), ……
ƒ Zu jedem Zeitpunkt hat man in der Umgebung eine gerade aktuelle
Menge von Namen mit Bindungen für Werte und Funktionen, die sich
aufeinander abstützen
Diese veränderlichen Bindungen bilden die globale Umgebung
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
199
3.9 Blockstrukturen: Umgebungen und Scoping
Probleme der globalen Umgebung:
ƒ Wenn die Anzahl der Funktionen zu groß wird, verliert man schnell
den Überblick
ƒ Jede Prozedur „kostet“ einen Namen, d.h., der Namensraum für
Funktionen wird immer kleiner; Gefahr von Verwechselungen oder
Namenskonflikten wächst
ƒ Fehlen von lokal definierten Variablen macht den Code unübersichtlich
ƒ Da alle Funktionen überall in anderen Funktionen aufgerufen werden
können, müssen sie gegen mögliche falsche Verwendungen abgesichert
werden. Die globale Verfügbarkeit stellt generell eine permanente
Fehlerquelle dar.
Daher gibt es die Möglichkeit lokaler Umgebungen, um lokale Namen
zu definieren und an Werte oder Prozeduren zu binden
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
200
3.9.1 Lokale Variablen
Notwendikeit lokaler Variablen:
Scheme-Interpreter wertet zuerst die Argumente einer Funktion aus, mit
den so berechneten Bindungen für die formalen Parameter wird dann der
Funktionsrumpf ausgewertet Æ Häufig mehrfache Auswertung
erforderlich
f ( x, y ) = x ⋅ (1 + x ⋅ y ) 2 + y ⋅ (1 − y ) 2 + (1 + x ⋅ y ) ⋅ (1 − y )
(define (f x y)
(+ (* x (sqr (+ 1 (* x y))))
(* y (sqr (- 1 y)))
(* (+ 1 (* x y)) (- 1 y))))
Mehrfache Auswertung von a und b wird verhindert durch Substituition:
a = 1 + x ⋅ y ,b = 1 − y ⇒ f ( x, y ) = x ⋅ a 2 + y ⋅ b2 + a ⋅ b
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
201
3.9.1 Lokale Variablen
Realisierung durch lambda-Ausdruck:
Macht man einen mehrfach vorkommenden Ausdruck zum Argument
eines lambda-Ausdrucks, so wird seine Mehrfachauswertung vermieden
f ( x, y ) = x ⋅ (1 + x ⋅ y ) 2 + y ⋅ (1 − y ) 2 + (1 + x ⋅ y ) ⋅ (1 − y )
a = 1 + x ⋅ y ,b = 1 − y ⇒ f ( x, y ) = x ⋅ a 2 + y ⋅ b2 + a ⋅ b
(define (f x y)
( (lambda (a b) (+ (* x (sqr a)) (* y (sqr b)) (* a b)))
(+ 1 (* x y))
(- 1 y) ) )
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
202
3.9.1 Lokale Variablen
Kurzform durch special form let:
(define (f x y)
(let ( (a (+ 1 (∗ x y)))
(b (- 1 y)))
(+ (∗ x (sqr a)) (∗ y (sqr b)) (∗ a b))))
(a (+ 1 (∗ x y))) und (b (- 1 y)) sind sog. Bindungspaare
Allgemeine Form von let in Scheme:
(let ((<var1> <exp1>) … (<varn> <expn>)) <body>)
Liste von Bindungspaaren
ist äquivalent zu
((lambda (<var1> … <varn>) <body>) <exp1> … <expn>)
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
203
3.9.1 Lokale Variablen
Auswertung von let:
(let ((<var1> <exp1>) … (<varn> <expn>)) <body>)
1. Erst (!) alle <exp1> … <expn> auswerten zu <val1> … <valn>
2. Danach (!) alle <var1> … <varn> binden an <val1> … <valn> in der
Form ((<var1> <val1>) … (<varn> <valn>)) (∗)
3. <body> wird in der lokalen Umgebung mit den Bindungen (∗) ausgewertet, der Wert von <body> wird als Wert von let zurückgegeben
4. Danach sind die lokalen Bindungen nicht mehr zugreifbar
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
204
3.9.1 Lokale Variablen
Achtung: Eine Variable <vari> ist noch nicht an <vali> gebunden,
wenn <expj> mit j > i ausgewertet wird Æ <expj> darf nicht auf <vari>
zugreifen!
Beispiel:
(define a 100)
> (let ((a 10) (b (∗ 2 a))) (+ a b))
210
> (∗ 2 b)
reference to an identifier before its definition: b
© Xiaoyi Jiang
Informatik I – Grundlagen der Programmierung
205
3.9.1 Lokale Variablen
Alternative special form let* mit modifizierter Auswertung:
<vari> wird an <vali> gebunden, bevor <expi+1> ausgewertet wird
(let* ((<var1> <exp1>)
(<var2> <exp2>)
……
(<varn> <expn>))
<body>)
Beispiel:
> (define a 100)
> (let* ((a 10)
(b (∗ 2 a)))
(+ a b))
> 30
;;
;;
;;
;;
© Xiaoyi Jiang
Bei Auswertung (+ a b)
ist a bereits gebunden
mit a=10; Bindng a=100
nicht relevant
Informatik I – Grundlagen der Programmierung
206
Herunterladen