home Grundlagen der Funktionalen Programmierung: Funktionen höherer Ordnung Prof. Dr. Uwe Schmidt FH Wedel

Funktionen höherer Ordnung

weiter

weiter

Definition
Eine Funktion höherer Ordnung besitzt Parameter von einem Funktionstyp oder der Resultattyp ist ein Funktionstyp
merke
Funktionen mit mehreren Parametern, die mit Currying arbeiten, sind Funktionen höherer Ordnung
weiter
Beispiel
add :: Int -> (Int -> Int)
 
add x = λ y -> x + y
weiter
2. Beispiel
Eine Funktion 2 mal auf einen Wert anwenden
 
twice :: (a -> a) -> a -> a
 
twice f x = f (f x)
weiter

weiter

Listenverarbeitung mit Funktionen höherer Ordnung

Beispiel
Alle Elemete einer Liste ohne Berücksichtigung ihrer Position verarbeiten
 
map :: (a -> b) -> [a] -> [b]
 
map f []       = []
map f (x : xs) = f x : map f xs
weiter
2. Beispiel
Filtern von Listen
 
filter :: (a -> Bool) -> [a] -> [a]
 
filter p []        = []
filter p (x : xs)
  | p x            = x : filter p xs
  | otherwise      =     filter p xs
weiter
vordefinierte Funktionen
für All- und Existenz-Quantor
 
all :: (a -> Bool) -> [a] -> Bool
 
all p []       = True
all p (x : xs) = p x && all p xs
 
any :: (a -> Bool) -> [a] -> Bool
 
any p []       = False
any p (x : xs) = p x || any p xs
weiter
foldr
Falten von Listen
Schema
für die Verarbeitung von Listen
 
f :: [a] -> b
 
f []       = c
f (x : xs) = x `op` f xs
weiter
Beispiele
sum :: Num a => [a] -> a
sum []            = 0              -- c  = 0
sum (x : xs)      = x + sum xs     -- op = +
 
product :: Num a => [a] -> a
product []       = 1               -- c  = 1
product (x : xs) = x * product xs  -- op = *
 
or :: [Bool] -> Bool
or []            = False           -- c  = False
or (x : xs)      = x || or xs      -- op = ||
 
and :: [Bool] -> Bool
and []           = True            -- c  = True
and (x : xs)     = x && and xs     -- op = &&
 
concat :: [[a]] -> [a]
concat []        = []              -- c  = []
concat (x : xs)  = x ++ concat xs  -- op = ++
weiter
Ziel
Verarbeitungsschema parametrisieren und für alle Beispiele wiederverwenden
weiter
Abstrahieren
Eine Konstante in einer Funktion zu einem Parameter machen
weiter
foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
 
foldr op c []       = c
foldr op c (x : xs) = x `op` foldr op c xs
weiter
Beispiele
sum     = foldr (+)  0
product = foldr (*)  1
and     = foldr (&&) True
or      = foldr (||) False
concat  = foldr (++) []
 
all p   = foldr (λ x r  -> p x && r) True
any p   = foldr (λ x r  -> p x || r) False
 
map f   = foldr (λ x rs -> f x : rs) []
weiter
merke
Lineare Rekursion
weiter
Arbeitsweise
x1 : (x2 : (x3 : []))
 
-- [] ersetzen durch c
-- :  ersetzen durch op
 
x1 `op` (x2 `op` (x3 `op` c))
weiter
merke
Manchmal muss eine Funktion erst in eine Normalform gebracht werden,
um sie in ein foldr zu transformieren.
weiter
Beispiel
Länge einer Liste
 
length :: [a] -> Int
 
length []       = 0
length (x : xs) = 1 + length xs
 
==>
 
length []       = 0
length (x : xs) = x `op` length xs
  where
   x `op` n = 1 + n
 
==>
 
length = foldr (λ x n -> 1 + n) 0
weiter
2. Beispiel
reverse
 
reverse :: [a] -> [a]
 
reverse []       = []
reverse (x : xs) = reverse xs ++ [x]
 
==>
 
reverse []       = []
reverse (x : xs) = x `op` reverse xs
  where
   x `op` rs = rs ++ [x]
 
==>
 
reverse = foldr (λ x rs -> rs ++ [x]) []
weiter
3. Beispiel
filter
 
filter :: (a -> Bool) -> [a] -> [a]
 
filter p []        = []
filter p (x : xs)
  | p x            = x : filter p xs
  | otherwise      =     filter p xs
 
==>
 
filter p []        = []
filter p (x : xs)  = x `op` filter p xs
  where
    x' `op` rs
      | p x'       = x' : rs
      | otherwise  =      rs
 
==>
 
filter p = foldr op []
  where
    x `op` rs
      | p x        = x : rs
      | otherwise  =     rs
weiter
Berechnungsschema
für foldr
 
  foldr op c [x0, x1, ..., xn]
=
  x0 `op` (x1 `op` (... (xn `op` c)...))
weiter
merke
Die Berechnung wird rechtsassoziativ durchgeführt.
weiter
?
Gibt es ein fold, das linksassoziativ arbeitet?
Beispiel
Aufsummieren
 
sum :: Num a => [a] -> a
 
sum xs = sum' 0 xs                      -- .0
  where
    sum' r []       = r                 -- .1
    sum' r (x : xs) = sum' (r + x) xs   -- .2
weiter
Auswerten
  sum [1, 2, 3]
={ .0 }
  sum' 0 [1, 2, 3]
={ .2 }
  sum' (0 + 1) [2, 3]
={ .2 }
  sum' ((0 + 1) + 2) [3]
={ .2 }
  sum' (((0 + 1) + 2) + 3) []
={ .1 }
  ((0 + 1) + 2) + 3
={ rechnen }
  (1 + 2) + 3
={ rechnen }
  3 + 3
={ rechnen }
  6
weiter
Rekursions-Schema
aus sum und sum' ableiten
 
f xs = f' c xs                        -- c
 
f' acc []       = acc
f' acc (x : xs) = f' (acc `op` x) xs  -- op
weiter
foldl
foldl :: (b -> a -> b) -> b -> [a] -> b
 
foldl op acc []       = acc
foldl op acc (x : xs) = foldl op (acc `op` x) xs
weiter
merke
Endrekursion
weiter
Berechnungsschema
für foldl
 
  foldl op c [x0, x1, ..., xn]
=
  (...((c `op` x0) `op` x1) ...) `op` xn
weiter
merke
Die Berechnung wird linksassoziativ durchgeführt.
weiter
Beispiele
sum     = foldl (+)  0
product = foldl (*)  1
and     = foldl (&&) True
or      = foldl (||) False
concat  = foldl (++) []
 
all p   = foldl (λ r x -> r && p x) True
any p   = foldl (λ r x -> r || p x) False
 
map f   = foldl (λ rs x -> rs ++ [f x]) []
weiter
?
Wann foldr, wann foldl?
weiter
merke
Nicht einheitlich zu beantworten
weiter
Beispiel
reverse mit foldl
 
reverse :: [a] -> [a]
 
reverse = reverse' []
  where
    reverse' acc [] = acc
    reverse' acc (x : xs) = reverse' (x : acc) xs
 
==>
 
reverse = foldl (:) []
weiter
merke
foldr: and, or (kurze Auswertung)
merke
foldr: concat (lineare Laufzeit vs. quadratische)
merke
foldr: map (lineare Laufzeit vs. quadratische)
merke
foldl: sum, product (Endrekursion)
merke
foldl: reverse (lineare Laufzeit vs. quadratische)
weiter
?
Gibt es Funktionen höherer Ordnung auf Listen, die nicht mit einem fold verarbeitet werden können?
Beispiel
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
 
zipWith op []       ys       = []
zipWith op xs       []       = []
zipWith op (x : xs) (y : ys) = (x `op` y) : zipWith op xs ys
Anwendung
Vektor-Operationen
 
addV, subV :: Num a => [a] -> [a] -> [a]
addV = zipWith (+)
subV = zipWith (-)
 
sprod :: Num a => [a] -> [a] -> [a]
sprod = zipWith (*)

weiter

Funktionskomposition

Operator
ASCII: ., sonst ∘
weiter
Funktionskomposition
f ∘ g :: (b -> c) -> (a -> b) -> (a -> c)
 
f ∘ g = λ x -> f (g x)
weiter
Beispiele
  odd n = not (even n)
==>
  odd = not ∘ even
 
  twice f x = f (f x)
==>
  twice f = f ∘ f
 
  sumSqrEven xs = sum (map (^2) (filter even xs))
==>
  sumSqrEven = sum ∘ map (^2) ∘ filter even
weiter
merke
Funktionskomposition ist assoziativ
 
f ∘ (g ∘ h) = (f ∘ g) ∘ h
weiter
merke
Funktionskomposition besitzt ein neutrales Element
 
id :: a -> a
id = λ x -> x
weiter
gut
Alle Funktionen vom Typ a -> a bilden einen Monoid (eine Halbgruppe mit neutralem Element)
weiter
Verallgemeinerung
der Funktionskomposition auf Listen von Funktionen
 
compose :: [a -> a] -> (a -> a)
 
compose = foldr () id
weiter
Funktionskomposition
von links nach rechts
 
(>>>) :: (a -> b) -> (b -> c) -> (a -> c)
 
(>>>) = flip ()
 
-- mit
 
flip :: (a -> b -> c) -> (b -> a -> c)
 
flip op = λ x y -> y `op` x
 
--
 
h ∘ g ∘ f = f >>> g >>> h
weiter

weiter

Beispiele zum Kapitel

   1module HigherOrderedFct
   2where
   3
   4import Prelude hiding ( map
   5                      , filter
   6                      , any
   7                      , all
   8                      , takeWhile
   9                      , dropWhile
  10                      , sum
  11                      , product
  12                      , or
  13                      , and
  14                      , concat
  15                      , reverse
  16                      , foldl
  17                      , foldr
  18                      , flip
  19                      , id
  20                      , (.)
  21                      )
  22
  23twice           :: (a -> a) -> a -> a
  24twice f x       = f (f x)
  25
  26map             :: (a -> b) -> [a] -> [b]
  27map f xs        = [f x | x <- xs]
  28
  29
  30map' :: (a -> b) -> [a] -> [b]
  31map' f []       = []
  32map' f (x : xs) = f x : map' f xs
  33
  34
  35filter          :: (a -> Bool) -> [a] -> [a]
  36filter p xs     = [x | x <- xs, p x]
  37
  38
  39filter'         :: (a -> Bool) -> [a] -> [a]
  40filter' p []    = []
  41filter' p (x : xs)
  42    | p x       = x : filter' p xs
  43    | otherwise =     filter' p xs
  44
  45
  46all            :: (a -> Bool) -> [a] -> Bool
  47all p []       = True
  48all p (x : xs) = p x && all p xs
  49
  50
  51any            :: (a -> Bool) -> [a] -> Bool
  52any p []       = False
  53any p (x : xs) = p x || any p xs
  54
  55
  56takeWhile       :: (a -> Bool) -> [a] -> [a]
  57takeWhile p []  = []
  58takeWhile p (x : xs)
  59    | p x       = x : takeWhile p xs
  60    | otherwise = []
  61
  62
  63dropWhile :: (a -> Bool) -> [a] -> [a]
  64dropWhile p []  = []
  65dropWhile p (x : xs)
  66    | p x       = dropWhile p xs
  67    | otherwise = x : xs
  68
  69-- foldr
  70
  71sum             :: Num a => [a] -> a
  72sum []          = 0
  73sum (x : xs)    = x + sum xs
  74
  75product         :: Num a => [a] -> a
  76product []      = 1
  77product (x : xs)
  78                = x * product xs
  79
  80or              :: [Bool] -> Bool
  81or []           = False
  82or (x : xs)     = x || or xs
  83
  84and             :: [Bool] -> Bool
  85and []          = True
  86and (x : xs)    = x && and xs
  87
  88concat          :: [[a]] -> [a]
  89concat []       = []
  90concat (xs : xss)
  91                = xs ++ concat xss
  92
  93reverse         :: [a] -> [a]
  94reverse []      = []
  95reverse (x : xs)= reverse xs ++ [x]
  96
  97sumR            :: Num a => [a] -> a
  98sumR            = foldr (+)  0
  99
 100productR        :: Num a => [a] -> a
 101productR        = foldr (*)  1
 102
 103orR             :: [Bool] -> Bool
 104orR             = foldr (||) False
 105
 106andR            :: [Bool] -> Bool
 107andR            = foldr (&&) True
 108
 109concatR         :: [[a]] -> [a]
 110concatR         = foldr (++) []
 111
 112
 113foldr           :: (a -> b -> b) -> b -> [a] -> b
 114foldr op c []   = c
 115foldr op c (x : xs)
 116                = x `op` foldr op c xs
 117
 118lengthR         :: [a] -> Integer
 119lengthR         = foldr (\ x n -> 1 + n) 0
 120
 121reverseR        :: [a] -> [a]
 122reverseR        = foldr (\ x xs -> xs ++ [x]) []
 123
 124mapR            :: (a -> b) -> [a] -> [b]
 125mapR f          = foldr (\ x xs -> f x : xs) []
 126
 127filterR         :: (a -> Bool) -> [a] -> [a]
 128filterR p       = foldr (\ x xs -> if p x
 129                                   then x : xs
 130                                   else xs
 131                  ) []
 132
 133suml            :: Num a => [a] -> a
 134suml            = sum' 0
 135    where
 136      sum' acc []       = acc
 137      sum' acc (x : xs) = sum' (acc + x) xs
 138
 139reverse'        :: [a] -> [a]
 140reverse' xs     = rev [] xs
 141  where
 142    rev acc []       = acc
 143    rev acc (x : xs) = rev (x : acc) xs
 144
 145foldl               :: (b -> a -> b) -> b -> [a] -> b
 146foldl op acc []     = acc
 147foldl op acc (x:xs) = foldl op (acc `op` x) xs
 148
 149sumL            :: Num a => [a] -> a
 150sumL            = foldl (+)  0
 151
 152productL        :: Num a => [a] -> a
 153productL        = foldl (*)  1
 154
 155orL             :: [Bool] -> Bool
 156orL             = foldl (||) False
 157
 158andL            :: [Bool] -> Bool
 159andL            = foldl (&&) True
 160
 161concatL         :: [[a]] -> [a]
 162concatL         = foldl (++) []
 163
 164lengthL         :: [b] -> Integer
 165lengthL         = foldl (\ r x -> r + 1) 0
 166
 167reverseL        :: [a] -> [a]
 168reverseL        = foldl (\ r x -> x : r) []
 169
 170-- ghci: :set +s
 171
 172t1 = length (reverseR [1..10000])
 173t2 = length (reverseL [1..10000])
 174t3 = length (concatR (replicate 10000 "a"))
 175t4 = length (concatL (replicate 10000 "a"))
 176
 177infixr 9 .
 178
 179(.) :: (b -> c) -> (a -> b) -> (a -> c)
 180f . g = \ x -> f (g x)
 181
 182id :: a -> a
 183id = \ x -> x
 184
 185compose :: [a -> a] -> (a -> a)
 186compose = foldr (.) id
 187
 188(>>>) :: (a -> b) -> (b -> c) -> (a -> c)
 189(>>>) = flip (.)
 190
 191composeL :: [a -> a] -> (a -> a)
 192composeL = foldl (>>>) id
 193
 194flip :: (a -> b -> c) -> (b -> a -> c)
 195flip op = \ y x -> x `op` y
 196
 197fs :: [Integer -> Integer]
 198fs = [(+1), (*2), (`div` 3), (^2)]
 199
 200r1 = compose  fs 5
 201r2 = composeL fs 5

weiter

Die Quelle

HigherOrderedFct.hs

Letzte Änderung: 27.11.2019
© Prof. Dr. Uwe Schmidt
Prof. Dr. Uwe Schmidt FH Wedel