Programmierkurs II Higher Order Functions • ÜbergabeparameterkönnenFunktionensein. • RückgabewertekönnenFunktionensein. HigherOrderFunctions • InHaskell nimmtjedeFunktiongenaueinenParameter entgegen. • WiekönnennunFunktionenmehrereParameterannehmen? – CURRYING! *Main> :t max max :: Ord a => a -> a -> a *Main> :t max 3 max 3 :: (Num a, Ord a) => a -> a Currying *Main> max 3 5 5 *Main> (max 3) 5 5 • InHaskell nimmtjedeFunktiongenaueinenParameter entgegen. • WiekönnennunFunktionenmehrereParameterannehmen? – CURRYING! *Main> :t max max :: Ord a => a -> (a -> a) *Main> :t max 3 max 3 :: (Num a, Ord a) => a -> a Currying *Main> max 3 5 5 *Main> (max 3) 5 5 • WennwireineFunktionmitzuwenigenParameteraufrufen, bekommenwireineFunktionzurück,dienichtvollständig ausgeführtwurde. • DadurchkönnenwirFunktionenbauen,welchewiranandere Funktionenweiterreichen,odereinfach–onthe fly- mitDaten füttern. multThree :: (Num a) => a -> a -> a -> a multThree x y z = x * y * z Vorteil Code SchreibenwireineFunktion,dieeinenübergebenenWertmit derZahl100vergleicht: compareWithHundred :: (Num a, Ord a) => a -> Ordering compareWithHundred x = compare 100 x Warumbenötigenwirüberhauptdas‘x‘? compareWithHundred' :: (Num a, Ord a) => a -> Ordering compareWithHundred' = compare 100 Code Vorteil SchreibenwireineFunktion,dieeinenübergebenenWertmit derZahl10addiert: add10 :: (Num a) => a -> a add10 = (+10) CheckenwirdieTypsignatur: *Main> :t (+10) (+10) :: Num a => a -> a Beispiel- Operatoren Waspassiert,wennwireinepartielleFunktionanzeigenwollen? *Main> multThree 3 4 <interactive>:30:1: No instance for (Show (a0 -> a0)) (maybe you haven't applied enough arguments to a function?) arising from a use of ‘print’ In the first argument of ‘print’, namely ‘it’ In a stmt of an interactive GHCi command: print it Beispiel– Funktionenanzeigen SchreibenwireineFunktion,welcheeineübergebeneFunktion zweimalaufihrArgumentanwendet: applyTwice :: applyTwice FunktionalsÜbgergabeparameter SchreibenwireineFunktion,welcheeineübergebeneFunktion zweimalaufihrArgumentanwendet: applyTwice :: (a -> a) -> a -> a applyTwice FunktionalsÜbgergabeparameter SchreibenwireineFunktion,welcheeineübergebeneFunktion zweimalaufihrArgumentanwendet: applyTwice :: (a -> a) -> a -> a applyTwice f x = f (f x) Beispiel: FunktionalsÜbgergabeparameter SchreibenwireineFunktion,welcheeineübergebeneFunktion zweimalaufihrArgumentanwendet: applyTwice :: (a -> a) -> a -> a applyTwice f x = f (f x) Beispiel: *Main> applyTwice (+3) 10 16 *Main> applyTwice ("Ha" ++) "llo" "HaHallo" FunktionalsÜbgergabeparameter zip' zip' zip' zip' :: [a] _ [] = [] _ = (x:xs) -> [b] -> [(a,b)] [] [] (y:ys) = (x,y):zip' xs ys WirddurcheinezusätzlicheFunktionerweitert: zipWith' zipWith' zipWith' zipWith' :: (a -> _ [] _ = _ _ [] = f (x:xs) b -> c) -> [a] -> [b] -> [c] [] [] (y:ys) = Erweiterungvonzip zip' zip' zip' zip' :: [a] _ [] = [] _ = (x:xs) -> [b] -> [(a,b)] [] [] (y:ys) = (x,y):zip' xs ys WirddurcheinezusätzlicheFunktionerweitert: zipWith' zipWith' zipWith' zipWith' :: (a -> _ [] _ = _ _ [] = f (x:xs) b -> c) -> [a] -> [b] -> [c] [] [] (y:ys) = f x y : zipWith' f xs ys Erweiterungvonzip Code BeieinerFaltungwerdendieElementeeinerListemitHilfeeines Operatorszusammengefasst. Beispiele: • Summe • Produkt • Listenkonkatenation Faltungen WasfälltbeidiesenbeidenFunktionenauf? sum' :: (Num a) => [a] -> a sum' [] = 0 sum' (x:xs) = x + sum' xs product' :: (Num a) => [a] -> a product' [] = 1 product' (x:xs) = x * sum' xs Faltungen DefinitionmitStartwertundeinerzweistelligenFunktionf auf einerListex: f x1 (f x2 ...(f xn s)) DieFunktionfoldR benötigtalsoeineFunktionf,einenStartwerts und eineListe. foldR :: (_ -> _ -> _) -> _ -> [_] -> _ foldR _ s [] = s foldR f s (x:xs) = f x (foldR f s xs) FaltungvonrechtsmitStartwert DefinitionmitStartwertundeinerzweistelligenFunktionf auf einerListex: f x1 (f x2 ...(f xn s)) DieFunktionfoldR benötigtalsoeineFunktionf,einenStartwerts und eineListe. foldR :: (b -> a -> a) -> a -> [b] -> a foldR _ s [] = s foldR f s (x:xs) = f x (foldR f s xs) FaltungvonrechtsmitStartwert DieFunktionsdefinitionlässtsichanalogzurSummedefinieren,nurdass wirdiezusätzlichenParameteranstellevon0und+verwenden. foldR :: (b -> a -> a) -> a -> [b] -> a foldR _ s [] = s foldR f s (x:xs) = f x (foldR f s xs) sum' :: (Num a) => [a] -> a sum' [] = 0 sum' (x:xs) = x + sum' xs FaltungvonrechtsmitStartwert- Vergleich Beispiel: Liste [1,2,3] [1,2] [1] [] Startwert 0 + 3 + 5 + 6 FaltungvonrechtsmitStartwert Code: sum'' :: [Int] -> Int sum'' xs = foldr (+) 0 xs SummenfunktiondurchFaltung Code: sum'' :: [Int] -> Int sum'' xs = foldr (+) 0 xs Beispiel: sum [1, 2, 3] foldr (+) 0 [1, 2, 3] foldr (+) 0 (1:(2:(3:[]))) (1+(2+(3+0))) SummenfunktiondurchFaltung Code: product'' :: [Int] -> Int product'' xs = foldr (*) 1 xs Beispiel: product [1, 2, 3] foldr (*) 1 [1, 2, 3] foldr (*) 1 (1:(2:(3:[]))) (1*(2*(3*1))) ProduktdurchFaltung Vereinfachen? sum'' :: [Int] -> Int sum'' xs = foldr (+) 0 xs product'' :: [Int] -> Int product'' xs = foldr (*) 1 xs Vereinfachen xs kannausderGleichunggestrichenwerden: sum'' :: [Int] -> Int sum'' = foldr (+) 0 product'' :: [Int] -> Int product'' = foldr (*) 1 Vereinfachen Code: length' :: [a] -> Int length' [] = 0 length' (_:xs) = 1 + length' xs Auswertung: length‘ [1, 2, 3] = length‘ (1:(2:(3:[]))) = 1+(1+(1+0)) = 3 length durchFaltung- Vorlesung Code: length' :: [a] -> Int length' [] = 0 length' (_:xs) = 1 + length' xs Auswertung: \_ n -> 1 + n length‘ [1, 2, 3] = length‘ (1:(2:(3:[]))) = 1+(1+(1+0)) = 3 length durchFaltung - Vorlesung Code: length' :: [a] -> Int length' [] = 0 length' (_:xs) = 1 + length' xs Auswertung: length‘ [1, 2, 3] = length‘ (1:(2:(3:[]))) = 1+(1+(1+0)) = 3 \_ n -> 1 + n Einsetzen: length'' :: [a] -> Int length'' = foldr (\_ n -> 1 + n) 0 length durchFaltung - Vorlesung Code: reverse' :: [a] -> [a] reverse' [] = [] reverse' (x:xs) = reverse‘ xs ++ [x] Auswertung: reverse‘ [1, 2, 3] = reverse‘ (1:(2:(3:[]))) = (([] ++ [3]) ++ [2]) ++ [1] reverse durchFaltung- Vorlesung Code: reverse' :: [a] -> [a] reverse' [] = [] reverse' (x:xs) = reverse‘ xs ++ [x] Auswertung: \x xs -> xs ++ [x] reverse‘ [1, 2, 3] = reverse‘ (1:(2:(3:[]))) = (([] ++ [3]) ++ [2]) ++ [1] reverse durchFaltung- Vorlesung Code: reverse' :: [a] -> [a] reverse' [] = [] reverse' (x:xs) = reverse‘ xs ++ [x] Auswertung: reverse‘ [1, 2, 3] = reverse‘ (1:(2:(3:[]))) = (([] ++ [3]) ++ [2]) ++ [1] \x xs -> xs ++ [x] Einsetzen: reverse'' :: [a] -> [a] reverse'' = foldr (\x xs -> xs ++ [x]) [] reverse durchFaltung- Vorlesung Code: map' :: (a -> b) -> [a] -> [b] map' f [] = [] map' f(x:xs) = f x : map‘ f xs Auswertung: map‘ f [1, 2, 3] = map‘ f (1:(2:(3:[]))) = f 1:(f 2: (f 3: [])) map durchFaltung Code: map' :: (a -> b) -> [a] -> [b] map' f [] = [] map' f(x:xs) = f x : map‘ f xs Auswertung: \x xs -> f x : xs map‘ f [1, 2, 3] = map‘ f (1:(2:(3:[]))) = f 1:(f 2: (f 3: [])) map durchFaltung Code: map' :: (a -> b) -> [a] -> [b] map' f [] = [] map' f(x:xs) = f x : map‘ f xs Auswertung: \x xs -> f x : xs map‘ f [1, 2, 3] = map‘ f (1:(2:(3:[]))) = f 1:(f 2: (f 3: [])) Einsetzen: map' :: (a -> b) -> [a] -> [b] map' f = foldr (\x xs -> f x : xs) [] map durchFaltung Wiekannmandiemap-FunktionmitHilfevonfoldr darstellen? map' :: (a -> b) -> [a] -> [b] map' f = foldR ((:).f) [] map durchFaltung- Alternative Waskommtraus? *Main> foldr ($) 7 [(/2), (*10), sqrt, (+2)] *Main> scanr ($) 7 [(/2), (*10), sqrt, (+2)] Faltung- Beispiel Waskommtraus? *Main> foldr ($) 7 [(/2), (*10), sqrt, (+2)] 15.0 *Main> scanr ($) 7 [(/2), (*10), sqrt, (+2)] [15.0,30.0,3.0,9.0,7.0] Code Faltung- Beispiel MitStartwert: foldR :: (b -> a -> a) -> a -> [b] -> a foldR _ s [] = s foldR f s (x:xs) = f x (foldR f s xs) OhneStartwert: foldR' :: (a -> a -> a) -> [a] -> a foldR' f (x:xs) = f x (foldR' f xs) Einfachmaltesten: *Main> foldR' (+) [1..10] FaltungvonrechtsOHNEStartwert WirbenötigenabermindestenseinElementinderListe! foldR' :: (a -> a -> a) -> [a] -> a foldR' _ [x] = x foldR' f (x:xs) = f x (foldR' f xs) VordefinierteFunktioninderPrelude:foldr1 FaltungvonrechtsOHNEStartwert DefinitionmitStartwertundeinerzweistelligenFunktionf auf einerListex: f (f (f s x1) x2) xn AndersalsbeiderFaltungvonrechtswirddieListealsonicht zuerstganzbiszumEndedurchlaufen,bevormitderAuswertung derFunktionf begonnenwird. FaltungvonlinksmitStartwert Beispiel: Startwert 1 * 1 * 2 * 3 Liste [1,2,3] [2,3] [3] [] FaltungvonlinksmitStartwert foldL :: (a -> b -> a) -> a -> [b] -> a foldL _ s [] = s foldL f s (x:xs) = foldL f (f s x) xs VergleichzufoldR foldR :: (b -> a -> a) -> a -> [b] -> a foldR _ s [] = s foldR f s (x:xs) = f x (foldR f s xs) FaltungvonlinksmitStartwert VorsichtinderGleichungwirddieFunktionfoldL aufgerufen! foldL' :: (a -> a -> a) -> [a] -> a foldL' f (x:xs) = foldL f x xs VordefinierteFunktioninderPrelude:foldl1 FaltungvonlinksOHNEStartwert WasistdasErgebnis? *Main> foldl (-) 1 [1..3] *Main> foldr (-) 1 [1..3] UnterschiedzwischenLinks- undRechtsfaltung WasistdasErgebnis? *Main> foldl (-) 1 [1..3] -5 *Main> foldr (-) 1 [1..3] 1 foldl (-) 1 [1..3] = = = = -(-(-11)2)3 -(- 0 2)3 -(-2)3 -5 foldr (-) 1 [1..3] = = = = -1(-2(-31)) -1(-22) -10 1 UnterschiedzwischenLinks- undRechtsfaltung Code Aufgabe: WennwirüberalleElementeeinerunendlichenListedieWurzel ziehenunddieeinzelnenErgebnisseaufsummieren,wieviele ElementeausderListewerdenmaximalgezogenbisdieZahl 1000überschrittenwird. Einbisschenüberlegen... Aufgabe: WennwirüberalleElementeeinerunendlichenListedieWurzel ziehenunddieeinzelnenErgebnisseaufsummieren,wieviele ElementeausderListewerdenmaximalgezogenbisdieZahl 1000überschrittenwird. *Main> length (takeWhile (<1000) (scanl1 (+) (map sqrt [1..]))) Einbisschenüberlegen... Aufgabe: WennwirüberalleElementeeinerunendlichenListedieWurzel ziehenunddieeinzelnenErgebnisseaufsummieren,wieviele ElementeausderListewerdenmaximalgezogenbisdieZahl 1000überschrittenwird. *Main> length (takeWhile (<1000) (scanl1 (+) (map sqrt [1..]))) *Main> length . takeWhile (<1000) . scanl1 (+) . map sqrt $ [1..] Einbisschenüberlegen...