Funktionen höherer Ordnung

Werbung
Funktionale Programmierung
ALP I
Funktionen höherer Ordnung
Teil 2
SS 2013
Prof. Dr. Margarita Esponda
Prof. Dr. Margarita Esponda
Funktionale Programmierung
Funktionen höherer Ordnung
Nehmen wir an, wir möchten alle Zahlen innerhalb einer
Liste miteinander addieren
addAll:: (Num a) => [a] -> a
addAll [] = 0
addAll (x:xs) = x + addAll xs
oder die Und-Operation über alle Elemente einer Liste
berechnen
trueAll:: [Bool] -> Bool
trueAll [] = True
trueAll (x:xs) = x && (trueAll xs)
Prof. Dr. Margarita Esponda
Funktionale Programmierung
Funktionen höherer Ordnung
Gemeinsamkeiten von beiden Funktionen sind:
1) Binär-Operator
2) konstanter Wert, wenn die Liste leer ist.
3) gleiches Rekursions-Muster
Wir können eine verallgemeinerte Funktion definieren, die
beide Probleme löst
Beispiel:
trueAll = betweenAll (&&) True
addAll
= betweenAll (+) 0
multAll = betweenAll (*) 1
Verallgemeinerungen sind immer gut!
Prof. Dr. Margarita Esponda
Funktionale Programmierung
Funktionen höherer Ordnung
betweenAll :: (a -> a -> a) -> a -> [a] -> a
Binäre Operation
Wert der Funktion,
wenn die Liste leer ist
betweenAll f
k [] = k
betweenAll f
k (x:xs) = f x (betweenAll f k xs)
Prof. Dr. Margarita Esponda
Funktionale Programmierung
Funktionen höherer Ordnung
betweenAll :: (a -> a -> a) -> a -> [a] -> a
betweenAll f
k [] = k
betweenAll f
k (x:xs) = f x (betweenAll f k xs)
betweenAll
f
k
[x1, x2,…, xn-1, xn]
⇒ f x1 (betweenAll f k [x2,…, xn-1, xn])
⇒ f x1 (f x2 (betweenAll f k [x3,…, xn-1, xn]))
⇒ f x1 (f x2 (f x3 (betweenAll f k [x4,…, xn-1, xn])))
...
⇒ f x1 (f x2 (f x3 ( .…. (f xn-1 (f xn (betweenAll f k [])))...)
⇒ f x1 (f x2 (f x3 (… (f xn-1 (f xn k)))…)
...
⇒ f x1 w2
Prof. Dr. Margarita Esponda
Funktionale Programmierung
Funktionen höherer Ordnung
foldr-Funktion
In Haskell ist bereits eine allgemeine Funktion
vordefiniert, die Faltungs-Operator genannt wird
Definition:
foldr f z []
=z
foldr f z (x:xs) = f x (foldr f z xs)
foldr (*) 1 [1,2,3,4]
:
1
1
:
2
3
2
*
3
:
4
Prof. Dr. Margarita Esponda
=>
:
*
[]
=>
*
4
*
1
24
Funktionale Programmierung
Faltungs-Operatoren
Beispiele:
foldr (*) 1 [1..4]
⇒
(*) 1 (foldr (*) 1 [2,3,4])
⇒
(*) 1 ((*) 2 (foldr (*) 1 [3,4]))
⇒
(*) 1 ((*) 2 ((*) 3 (foldr (*) 1 [4])))
⇒
(*) 1 ((*) 2 ((*) 3 ((*) 4 (foldr (*) 1 []))))
⇒
(*) 1 ((*) 2 ((*) 3 ((*) 4 1)))
⇒
(*) 1 ((*) 2 ((*) 3 4))
⇒
(*) 1 ((*) 2 12)
⇒
(*) 1 24
⇒
24
Fakultät-Funktion
factorial n = foldr (*) 1 [1..n]
Prof. Dr. Margarita Esponda
Funktionale Programmierung
Funktionen höherer Ordnung
Folgende Standard-Funktionen von Haskell können mit
Hilfe des Faltungs-Operators definiert werden:
Beispiele:
sum :: (Num a) => [a] -> a
sum
= foldr (+) 0
product :: (Num a) => [a]
product
= foldr (*) 1
-> a
or :: [Bool] -> Bool
or
= foldr (||) False
or :: [Bool] -> Bool
and
= foldr (&&) True
Prof. Dr. Margarita Esponda
Funktionale Programmierung
Die Natur rekursiver Funktionen
Rekursive Funktionen haben oft folgende allgemeine Form:
f 0
=c
f (n+1) = h (f n )
Diese Art der Definitionen wird oft als Strukturelle
Rekursion über die natürlichen Zahlen bezeichnet.
Prof. Dr. Margarita Esponda
Funktionale Programmierung
Die Natur rekursiver Funktionen
Eine Funktionsdefinition dieser Form über die natürlichen
Zahlen sieht aus wie folgt:
Sei
1+1+1+ … +1+0 = die natürliche Zahl n.
Wenn wir die 0 mit c und (1+) mit h ersetzen, bekommen wir
folgenden Ausdruck
h(h(h(…h(h(c))…))),
in dem h n-mal auf c = f(0) angewendet wird.
f
0
f (n+1)
Prof. Dr. Margarita Esponda
= 0
= (1+) (f n)
f
0
f (n+1)
= c
= h (f n)
Funktionale Programmierung
Die Natur rekursiver Funktionen
Folgende Faltungsfunktion stellt eine Verallgemeinerung
der Funktionen mit dieser einfachen Grundform dar:
natFold :: (a->a) -> a -> Integer -> a
natFold h c 0 = c
natFold h c (n+1) = h (natFold h c n)
Eigene Potenz-Funktion:
potenz(n, m) = n m für n, m ∈Ν
potenz :: Integer → Integer → Integer
potenz n m = natFold (*n) 1 m
Prof. Dr. Margarita Esponda
Funktionale Programmierung
Rekursionsarten
Lineare Rekursion
Rekursive Funktionen, die in jedem Zweig ihrer Definition maximal
einen rekursiven Aufruf beinhalten, werden als linear rekursiv
bezeichnet.
Endrekursion (tail recursion)
Linear rekursive Funktionen werden als endrekursive Funktionen
klassifiziert, wenn der rekursive Aufruf in jedem Zweig der
Definition die letzte Aktion zur Berechnung der Funktion ist.
Prof. Dr. Margarita Esponda
Funktionale Programmierung
Funktionen höherer Ordnung
foldl-Funktion
Definition:
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f z []
=z
foldl f z (x:xs) = foldl f (f z x) xs
foldl f z [x1,x2, …,xn]
⇒
⇒
foldl f
(f z x1) [x2,…,xn]
foldl f
(f (f z x1) x2) [x3,…,xn]
...
Prof. Dr. Margarita Esponda
⇒
foldl f
⇒
(f...(f (f (f z x1) x2) x3)…)
(f...(f (f (f z x1) x2) x3)…) []
Funktionale Programmierung
Funktionen höherer Ordnung
foldl f z []
=z
foldl f z (x:xs) = foldl f (f z x) xs
foldl (*) 1 [8,6,4]
:
8
⇒
:
6
:
4
Prof. Dr. Margarita Esponda
*
[]
*
*
z
4
6
8
⇒
⇒
⇒
⇒
foldl (*)
((*) 1 8) [6,4]
foldl (*)
8 [6,4]
foldl (*)
((*) 8 6) [4]
foldl (*)
48
⇒
foldl f
((*) 48 4) []
⇒
foldl f
192 []
⇒
192
[4]
Funktionale Programmierung
Die foldl-Funktion
Wichtiges Beispiel von Endrekursion
foldl :: (b -> a -> b) -> b -> [a] -> b
foldl f z []
= z
foldl f z (x:xs) = foldl f (f z x) xs
Hier werden Zwischenergebnisse
akkumuliert und weitergeleitet.
Mit Hilfe von Faltungs-Operatoren können sehr leicht
endrekursive Funktionen definiert werden.
Prof. Dr. Margarita Esponda
Funktionale Programmierung
Beispiele:
maxi :: (Ord a) => [a] -> a
maxi (x:xs) = foldl max x xs
length :: [a] -> Int
length xs = foldl addOne 0 xs
where
addOne a b = a + 1
pow :: Integer -> Integer -> Integer
pow b n
Prof. Dr. Margarita Esponda
= foldl (*) 1 (take n [b,b..b])
Funktionale Programmierung
Beispiele endrekursiver Funktionen
Klassisches Beispiel einer nicht endrekursiven Definition ist:
Die Standarddefinition der reverse-Funktion
rev :: [a] -> [a]
rev [] = []
rev (x:xs) = rev xs ++ [x]
Berechnungsaufwand von rev:
Reduktionen
rev [x1, x2, …, xn] => rev [x2, …, xn] ++ [x1]
1
=> rev [x3, …, xn] ++ [x2] ++ [x1]
1
...
=> [xn] ++ [xn-1] ++ [x2] ++ [x1]
1
=> [] ++ [xn] ++ … ++ [x2] ++ [x1]
1
bis hier (n+1) Reduktionen!
Prof. Dr. Margarita Esponda
Funktionale Programmierung
Berechnungsaufwand von rev
bis hier (n+1) Reduktionen!
(++) :: [a] -> [a] -> [a]
(++) [] ys
= ys
(++) (x:xs) ys = x:(xs ++ ys)
Reduktionen
=> [] ++ [xn] ++ [xn-1] ++ … ++ [x2] ++ [x1]
=> [xn] ++ [xn-1] ++ … ++ [x2] ++ [x1]
1
=> [xn, xn-1] ++ … ++ [x2] ++ [x1]
2
=> [xn, xn-1 ,xn-2] ++ … ++ [x2] ++ [x1]
3
=> . . .
.
=> [xn, xn-1 , … ,x1]
n
Die gesamte Anzahl der Reduktionen ist:
Prof. Dr. Margarita Esponda
Quadratischer
Ausführungsaufwand!
Funktionale Programmierung
Eine effizientere Version von rev
quickRev xs = rev_helper xs []
where
rev_helper [] ys = ys
rev_helper (x:xs) ys = rev_helper xs (x:ys)
Berechnungsaufwand:
Reduktionen
quickRev [x1, x2, …, xn] => rev_helper [x1,…,xn] []
1
=> rev_helper [x2,…,xn] (x1:[])
1
n
=> rev_helper [x3,…,xn] (x2:x1:[]) 1
...
=> (xn:, … ,x2:x1:[])
…
1
=> (xn:, … ,x2:[x1])
1
...
=> (xn:, … , x3:[x2,x1])
…
1
lineare Komplexität
Prof. Dr. Margarita Esponda
2n = O(n)
n
Funktionale Programmierung
foldl f z []
=z
foldl f z (x:xs) = foldl f (f z x) xs
Die reverse-Funktion mit
Faltungsoperator
f
z
reverse_reloaded :: [a] -> [a]
reverse_reloaded xs = foldl (flip (:)) [] xs
Die flip-Funktion
vertauscht die
Argumente für die
Funktion f
Prof. Dr. Margarita Esponda
flip
:: (a -> b -> c) -> b -> a -> c
flip f x y
=
f y x
Funktionale Programmierung
Berechnungsverlauf:
reverse_reloaded [x1, x2, … , xn]
=> foldl (flip (:)) [] [x1, x2,…, xn]
foldl.2
=> foldl (flip (:)) ((flip (:)) [] x1) [x2,x3,…, xn]
=> foldl (flip (:)) ((:) x1 []) [x2,x3,…, xn]
=> foldl (flip (:)) (x1:[]) [x2,x3,…, xn]
=> foldl (flip (:)) [x1] [x2,x3,…, xn]
foldl.2
=> foldl (flip (:)) ((flip (:)) [x1] x2) [x3,…, xn]
=> foldl (flip (:)) ((:) x2 [x1]) [x3,…, xn]
=> foldl (flip (:)) (x2:[x1]) [x3,…, xn]
=> foldl (flip (:)) [x2, x1] [x3,…, xn]
foldl.2 => . . .
Prof. Dr. Margarita Esponda
Funktionale Programmierung
Funktionen höherer Ordnung
Anwendungsbeispiel der zipWith-Funktion:
Skalar-Produkt von zwei Vektoren
v1 . v2
v1 = (x1, x2, .. , xn)
v2 = (y1, y2, .. , yn)
ist
v1 . v2 = x1. y1 + x2 . y2 + .. + xn. yn
skalarProd ::[Int] -> [Int] -> Int
skalarProd xs ys
Prof. Dr. Margarita Esponda
= foldl (+) 0 (zipWith (*) xs ys)
Funktionale Programmierung
Funktionen höherer Ordnung
Folgende Funktion berechnet die Fibonacci-Zahlen in
linearer Zeit
O(n)
fibs :: [Integer]
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
fibs
tail fibs
zipWith (+)
0:1:1:2:3:5:8:...
1:1:2:3:5:...
1:2:3:5:8:...
Anwendungsbeispiel:
take 40 fibs
Prof. Dr. Margarita Esponda
Funktionale Programmierung
Funktionen höherer Ordnung
Funktionskomposition
f
A
B
g
g
°f
(.) :: (b → c) → (a → b) → (a → c)
(.)
Beispiel:
Prof. Dr. Margarita Esponda
g
f
x = (g (f x))
ungerade = not . gerade
C
Herunterladen