Deklarative Programmierung

Werbung
Was bisher geschah
I
Deklarative vs. imperative Programmierung
I
Deklarative Programmierung: FP, LP, FLP, CLP
Funktionale Programmierung in Haskell:
I
Algebraische Datentypen
I
Pattern Matching
I
Polymorphie
I
Rekursive Datentypen
(Peano-Zahlen, Listen, binäre Bäume)
I
Rekursive Funktionen
I
strukturelle Induktion
47
Wiederholung λ-Ausdrücke
Beispiele:
I
double :: Int -> Int
double x = x + x
double = \ x -> x + x
I
succ :: Int -> Int
succ x = x + 1, succ = \ x -> x + 1
I
f :: Int -> Int -> Int
f x y = 2 * x + y,
Anwendung f 5 2
f x = \ y -> 2 * x + y ,
Anwendung (f 5) 2
f = \ x -> ( \ y -> 2 * x + y )
oder kürzer f = \ x y -> 2 * x + y
I
g :: a -> b -> a
g x _ = x, g x = \ _ -> x, g = \ x y -> x
48
Currying
Idee:
Jede Funktion mit mehreren Argumenten lässt sich durch
Funktionen mit einem Argument darstellen
Beispiel:
g :: [ a ] -> Int
g l n = (len l) <
g l = \ n -> (len
g = \ l n -> (len
-> Bool
n
l) < n
l) < n
in mathematischer Notation:
(A × B) → C ist isomorph zu A → (B → C)
49
Funktionen höherer Ordnung
Funktionen als Argument von Funktionen
Beispiel:
twice :: (a -> a) -> a -> a
twice f x = f (f x)
Anwendung:
I
double hat den Typ Int -> Int
I
twice double hat den Typ Int -> Int
I
twice double 3
hat den Typ Int und den Wert ?
\x -> 2 * x + 1 hat den Typ Int -> Int
I twice (\x -> 2 * x + 1)
hat den Typ Int -> Int
I
I
twice (\x -> 2 * x + 1) 3
hat den Typ Int und den Wert ?
I
succ 0, twice succ 0, twice twice succ 0
I
twice (^2) 3, twice twice (^2) 3
50
Beispiele
I
punktweise Summe zweier Funktionen:
fsum :: (a -> Int) -> (a -> Int) -> (a -> Int)
fsum f g x = (f x) + (g x)
fsum f g = \x -> (f x) + (g x)
Beispiele:
I
I
I
fsum (*2) (+1) 4,
fsum len head [ 2 .. 5 ]
Komposition von Funktionen
(.) :: (a -> b) -> (b -> c) -> (a -> c)
(f . g) x = f (g x)
(f . g) = \ x -> f (g x)
Beispiel: ( ( \ x -> x * 2 ) . len ) "foo"
51
Wiederholung: rekursive Datentypen
I
Peano-Zahlen
data Nat = Z
| S Nat
I
Listen
data List a = Nil {}
| Cons { head :: a, tail :: List a}
oder kürzer
data [a] = [] | a : [a]
I
Binärbäume
data Tree a = Leaf {}
| Branch { left :: Tree a,
key :: a,
right :: Tree a}
52
Wiederholung: Funktionen auf rekursiven Datentypen
Entwurf rekursiver Funktionen auf rekursiven Datentypen:
1. Typdefinition
2. Angabe aller Basis- und rekursiven Fälle
3. Definition der Ergebnisse der Basisfälle
4. Definition der Ergebnisse der rekursiven Fälle
5. evtl. Typ verallgemeinern
Beispiel: Summe aller Schlüssel eines Baumes
data Tree a = Leaf
| Branch (Tree a) a (Tree a)
1. Typdefinition: tsum :: Tree Int -> Int
2. Angabe aller Basis- und rekursiven Fälle:
tsum t = case t of
Leaf
-> ...
Branch l k r -> ...
3. Definition der Ergebnisse der Basisfälle: Leaf -> 0
4. Definition der Ergebnisse der rekursiven Fälle:
Branch l k r -> (tsum l) + k + (tsum r)
53
Wiederholung: Funktionen auf Listen und Bäumen
Operationen auf Listen:
I
Verdoppeln jedes Listenelements
I
Angabe gerade / ungerade für jedes Listenelement
I
Länge der Liste
I
Summe aller Listenelemente
Operationen auf Bäumen:
I
Verdoppeln jedes Schlüssels
I
Angabe gerade / ungerade für jeden Schlüssel
I
Anzahl aller Schlüssel
I
Summe aller Schlüssel
I
Inorder-Durchquerung
54
Wiederholung: Funktionen auf Listen
Beispiel: Verdoppeln jedes Elementes in einer Liste
double :: Int -> Int
double x = x + x
doubles :: [Int] -> [Int]
doubles xs = case xs of
[] -> []
(y:ys) -> (double y) : (doubles ys)
oder mit anonymer Funktion (λ-Notation):
doubles ::
doubles xs
[] ->
(y:ys)
[Int] -> [Int]
= case xs of
[]
-> ((\ x -> x + x) y) : (doubles ys)
evens :: [Int] -> [Bool]
evens xs = case xs of
[]
-> []
(y:ys) -> ((\x->(mod x 2 == 0)) y) : (evens ys)
55
Rekursionsmuster für Listen
gemeinsame Eigenschaft:
Ergebnis ist die Liste der Funktionswerte jedes Elementes der
Eingabeliste
I
Parameter:
I
I
auf jedes Element anzuwendende Funktion h :: a -> b
Liste vom Typ [a]
I
Ergebnis: Liste vom Typ [b]
I
Berechnung (Pattern Matching):
f xs = case xs of
[]
-> []
(x : xss) -> ( h x ) : ( f xss )
56
Rekursionsmuster map
Beschreibung des Rekursionsschemas
f x = case x of
[]
-> []
(x : xss) -> ( h x ) : ( f xss )
durch eine Funktion höherer Ordnung
mit der Funktion h :: a -> b als Argument
map :: ( a -> b ) -> [a] -> [b]
Anwendung: f = map h
ermöglicht kurze Funktionsdefinition, z.B.
doubles :: [ Int ] -> [ Int ]
doubles = map double
oder mit anonymer Funktion: doubles = map (\z -> z*2)
oder noch kürzer: doubles = map ( *2 )
57
filter
Beispiel: nur gerade Zahlen der Eingabeliste
ev :: Int -> Bool
ev = \x -> ( mod x 2 == 0 )
evens :: [Int] -> [Int]
evens xs = case xs of
[]
-> []
( x : xss ) -> if ev x
then x : ( evens xss )
else ( evens xss )
Funktion höherer Ordnung:
filter :: ( a -> Bool ) -> [a] -> [a]
filter p xs = case xs of
[]
-> []
( x : xss ) -> if ( p x )
then x : ( filter p xss )
else filter p xss
58
filter
ev :: Int -> Bool
ev = \x -> ( mod x 2 == 0 )
filter :: (a -> Bool) -> [a] -> [a]
filter p xs = case xs of
[]
-> []
( x : xss ) -> if ( p x )
then x : ( filter p xss )
else filter p xss
ermöglicht kurze Funktionsdefinitionen, z.B.:
evens = filter ev
oder
evens = filter ( \x -> ( mod x 2 == 0 ) )
filter ( < 100 ) ( map ( ^2 ) [ 0, 2 .. 10 ] )
59
List Comprehensions
Generatoren: [f x | x <- xs] = map f xs
Beispiel:
[ x^2 | x <- [1 .. 5]] = map (^2) [1 .. 5]
mehrere Generatoren:
[ f x1 .. xn |x1 <- .., ... , xn <- .. ]
Beispiel:
[ x + y | x <- [1 .. 5], y <- [0,10 .. 100]]
Man bemerke die Ähnlichkeit zur Mengenschreibweise:
{x + y | x ∈ {1, . . . , 5} ∧ x ∈ {0, 10, . . . , 100}}
Bedingungen: [f x | x<-xs, p x] = map f (filter p xs)
Beispiel:
[ x ^ 2 | x <- [1 ..5], mod x 2 == 0]
Man bemerke die Ähnlichkeit zur Mengenschreibweise:
{x 2 | x ∈ {1, . . . , 5} ∧ x ≡2 0}
60
Beispiele
[
[
[
[
[
x ^ 2 | x <- [ 1 ..5 ] ]
ev x | x <- [ 1 .. 10 ] ]
(x , x ^ 2 ) | x <- [ 1 ..5 ], ev x]
(x , y) | x <- [1 ..5], y <- [1 .. 5], x < y]
x ^ 2 | x <- [ 0, 1 .. 10 ], x ^ 2 < 100 ]
61
Mehr rekursive Funktionen auf Listen
data [a] = []
| a : [a]
Länge einer Liste:
len :: [a] -> Int
len xs = case xs of
[]
-> 0
( _ : xss ) -> 1 + (len xss)
Summe aller Listenelemente:
sum :: [Int] -> Int
sum xs = case xs of
[] -> 0
( x : xss ) -> x + (sum xss)
62
Mehr Rekursionsmuster für Listen
gemeinsame Eigenschaft:
I
Parameter:
I
I
I
Funktion cons :: a -> b -> b
zur Berechnung eines Wertes aus dem bisher berechneten
Wert und einem Listenelement
Wert nil :: b für leere Eingabeliste
Liste vom Typ [a]
I
Ergebnis vom Typ b
I
Berechnung (Pattern Matching):
f xs = case xs of
[]
-> nil
(x : xss) -> cons x ( f xss )
63
Rekursionschema fold
Funktion höherer Ordnung (mit Funktion als Argument)
foldr :: (a ->
foldr cons nil
[]
->
x : xss ->
b -> b) -> b -> [a] -> b
xs = case xs of
nil
cons x ( foldr cons nil xss )
ermöglicht kurze Funktionsdefinition, z.B.
len = foldr (\ x y -> 1 + x) 0
sum = foldr (\ x y -> x + y) 0
oder kurz: sum = foldr (+) 0
64
Rekursionsschemata über Bäume
doubles :: Tree Int -> [Int]
doubles t = case t of
Leaf
-> Leaf
Branch l k r -> Branch (doubles l) (k*2) (doubles r)
inorder :: Tree a -> [a]
inorder t = case t of
Leaf
-> []
Branch l k r -> ( inorder l )
++ [ k ]
++ ( inorder r )
sum :: Tree Int -> Int
sum t = case t of
Leaf
-> 0
Branch l k r -> ( sum l ) + k + ( sum r )
65
Rekursionsschema map über Bäume
f :: Tree a -> b
f t = case t of
Leaf
-> Leaf
Branch l k r -> Branch (f l) (g k) (f r)
Beispiel:
f = doubles
g = double
Rekursionsschema:
tmap :: (a -> b ) -> ( Tree a ) -> ( Tree b )
tmap f t = case t of
Leaf -> Leaf
Branch l k r -> Branch (tmap f l)
(f k)
(tmap f r)
66
Rekursionsschema fold über Bäume
f :: Tree a -> b
f t = case t of
Leaf
-> leaf
Branch l k r -> branch (f l) k (f r)
Beispiel:
f = inorder
leaf = []
branch x y z = x ++ [y] ++ z
Rekursionsschema:
tfold :: (b -> a ->
tfold branch leaf t
Leaf
->
Branch l k r ->
b -> b) -> b -> (Tree a) -> b
= case t of
leaf
branch (tfold branch leaf l)
k
(tfold branch leaf r)
67
Beispiele: fold über Bäume
tfold :: (b -> a ->
tfold branch leaf t
Leaf
->
Branch l k r ->
b -> b) -> b -> (Tree a) -> b
= case t of
leaf
branch (tfold branch leaf l)
k
(tfold branch leaf r)
inorder = tfold ( \ x y z -> x ++ [y] ++ z ) []
sum
= tfold ( \ l k r -> l + k + r ) 0
analog: Anzahl der Blätter, inneren Knoten, Tiefe
68
Zusammenfassung Rekursions-Schemata
Datentyp
Konstruktor
→
→
Schema
Funktion
Auswertung eines Rekursionsschemas für ein Objekt eines
algebraischen Datentyps:
jeden Konstruktor (= Funktionssymbol) durch entsprechende
Funktion ersetzen
kennen wir schon:
Datentyp
Rekursionsschema
Funktionssymbol f (Konstruktor)
=
=
=
Signatur Σ
Σ-Algebra (A, J·KA )
Funktion Jf KA auf A
69
Herunterladen