Deklarative Programmierung

Werbung
Was bisher geschah
I
Deklarative vs. imperative Programmierung
I
Deklarative Programmierung: FP, LP, FLP, CLP
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
39
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
40
Currying
Idee:
Jede Funktion mit mehreren Argumenten lässt sich durch
Funktionen mit einem Argument darstellen
in mathematischer Notation: (A × B) → C ist isomorph zu
A → (B → C)
41
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 (f 3) 2
42
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: fsum (*2) (+1) 4,
fsum len head [ 2 .. 5 ]
I
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"
43
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}
44
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)
45
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
Summe aller Schlüssel
I
Inorder-Durchquerung
46
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 λ-Notation:
doubles ::
doubles xs
[] ->
(y:ys)
[Int] -> [Int]
= case xs of
[]
-> ((\ x -> x + x) y) : (doubles ys)
even_test :: [Int] -> [Bool]
even_test xs = case xs of
[] -> []
(y:ys)->((\x->(mod x 2 == 0))y):(even_test ys)
47
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 : xxs) -> ( h x ) : ( f xxs )
48
Rekursionsmuster map
Beschreibung des Rekursionsschemas
f x = case x of
[]
-> []
(x : xxs) -> ( h x ) : ( f xxs )
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 = map double
oder mit anonymer Funktion
doubles = map (\ z -> z + z)
even_test = map ( \ x -> ( mod x 2 == 0) )
49
filter
Beispiel: nur gerade Zahlen der Eingabeliste
evens :: [Int] -> [Int]
evens xs = case xs of
[] -> []
(x : xxs) -> if (mod x 2 == 0)
then x : (evens xxs)
else (evens xxs)
Funktion höherer Ordnung:
filter :: (a -> Bool) -> [a] -> [a]
filter p xs = case xs of
[] -> []
(x:xxs) -> if (p x)
then x:(filter p xxs)
else filter p xxs
ermöglicht kurze Funktionsdefinitionen, z.B.:
evens = filter ev
50
List Comprehensions
Generatoren: [f x | x <- xs] = map f xs
Beispiel:
[ x ^ 2 | x <- [1 .. 5]] = map (^2) [1 ..
mehrere Generatoren:
[ f x1 .. xn |x1 <- .., .. xn <- .. ]
Beispiel:
[ x + y | x <- [1 .. 5], y <- [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]
51
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 ]
52
Mehr rekursive Funktionen auf Listen
data [a] = []
| a : [a]
Länge einer Liste:
len :: [a] -> Int
len xs = case xs of
[]
-> 0
( _ : xxs ) -> 1 + (len xxs)
Summe aller Listenelemente:
sum :: [Int] -> Int
sum xs = case xs of
[] -> 0
( x : xxs ) -> x + (sum xxs)
53
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 : xs) -> cons x ( f xxs )
54
Rekursionschema fold
Funktion höherer Ordnung (mit Funktion als Argument)
fold :: (a -> b -> b) -> b -> [a] -> b
fold cons nil xs = case xs of
[] -> nil
x : xxs -> cons x ( fold cons nil xxs )
ermöglicht kurze Funktionsdefinition, z.B.
len = fold (\ x y -> 1 + x) 0
sum = fold (\ x y -> x + y) 0
oder kurz
sum = fold (+) 0
55
Rekursionsschemata über Bäume
inorder :: Tree a -> [a]
inorder t = case t of
Leaf {} -> []
Branch {} -> inorder (left t)
++ [key t] ++ inorder (right t)
sum :: Tree Int -> Int
sum t = case t of
Leaf {} -> 0
Branch {} -> sum (left t)
+ key t + sum (right t)
56
Rekursionsschema fold über Bäume
f :: Tree a -> b
f t = case t of
Leaf {} -> leaf
Branch {} ->
branch (f (left t)) (key t) (f (right t))
f = inorder
leaf = [] ; branch x y z = x ++ [y] ++ z
tfold :: (b -> a -> b -> b) -> b -> (Tree a) -> b
tfold branch leaf t = case t of
Leaf {} -> leaf
Branch {} ->
branch (tfold branch leaf $ left t)
(key t) (tfold branch leaf $ right t)
inorder = fold ( \ x y z -> x ++ [y] ++ z ) []
sum
= fold ( \ l k r -> l + k + r ) 0
57
Zusammenfassung /Rekursions-Schemata
Datentyp → Schema
Konstruktor → Funktion
Auswertung eines Rekursionsschemas für ein Objekt eines
algebraischen Datentyps:
jeden Konstruktor (= Funktionssymbol) durch die
entsprechende Funktion ersetzen
Datentyp = Signatur Σ,
Rekursionsschema = Σ-Algebra.
58
Herunterladen