-- | Higher Order Functions -- Examples to introduce and illustrate the power of higher order functions -- Functional Programming course 2018. -- Thomas Hallgren -------------------------------------------------------------------------------- {- This stared as a skeleton, the definitions were filled in during the lecture. -} import Prelude hiding (map,filter,sum,product,concat,foldr, takeWhile,dropWhile,lines) import Data.Char(isSpace) import Data.List(sort,group) -- * Passing functions as arguments -- | Using an existing function evens = filter even [1..10] -- [2,4,5,8,10] -- | Defining a helper function squares = map square [1..10] -- [1,4,9,16,25,36,49,64,81,100] where square x = x*x -- | Partial application --haha = map take2 ["Hallo","Haskell"] -- where take2 xs = take 2 xs haha = map (take 2) ["Hallo","Haskell"] -- ["Ha","Ha"] -- wrong argument order? --prefixes = map prefix [1..7] -- where prefix n = take n "Haskell" prefixes = map (flip take "Haskell") [1..7] prefixes' = map (`take` "Haskell") [1..7] -- ["H","Ha","Has","Hask","Haske","Haskel","Haskell"] -- ** Sections (partial applications of infix operators) m3 = map (*3) [1..10] -- [3,6,9,12,15,18,21,24,27,30] lt3 = filter (<3) [1..10] -- [1,2] gt3 = filter (3<) [1..10] -- [4,5,6,7,8,9,10] between = filter p [1..10] -- [4,5] where p n = 3x*x) [1..10] between' = filter (\n-> 3 [a] -> [a] -- uncurry take :: (Int,[a]) -> [a] example = map (uncurry take) [(2,"Haskell"),(3,"Function")] -- ["Ha","Fun"] -- * Defining higher order functions -- ** How to define map and filter map :: (a -> b) -> [a] -> [b] map f [] = [] map f (x:xs) = f x:map f xs filter :: (a -> Bool) -> [a] -> [a] filter p [] = [] filter p (x:xs) | p x = x:filter p xs | otherwise = filter p xs -- ** Replacing many first-order functions with one higher-order function -- Some examples of first order functions on lists sum :: Num a => [a] -> a sum [] = 0 sum (x:xs) = x+sum xs product :: Num a => [a] -> a product [] = 1 product (x:xs) = x * product xs concat :: [[a]] -> [a] concat [] = [] concat (xs:xss) = xs ++ concat xss -- Factor out the differences from the common pattern foldr :: (a->b->b) -> b -> [a] -> b foldr op z [] = z foldr op z (x:xs) = x `op` foldr op z xs -- Now functions like the ones above can be simple one-line definition sum' xs = foldr (+) 0 xs product' xs = foldr (*) 1 xs concat' xss = foldr (++) [] xss and' xs = foldr (&&) True xs -- ** More examples weather = "June is warm\nJuly is warm\nJanuary is cold\n" takeLine "" = "" takeLine (c:cs) | c=='\n' = "" | otherwise = c:takeLine cs takeWord "" = "" takeWord (c:cs) | isSpace c = "" | otherwise = c:takeWord cs -- Generalize takeLine and takeWord into takeWhile takeWhile p [] = [] takeWhile p (c:cs) | p c = c:takeWhile p cs | otherwise = [] takeLine' s = takeWhile (/='\n') s takeWord' s = takeWhile (not . isSpace) s dropWhile p [] = [] dropWhile p (x:xs) | p x = dropWhile p xs | otherwise = x:xs dropLine s = drop 1 (dropWhile (/='\n') s) -- lines weather == ["June is warm","July is warm","January is cold"] lines :: String -> [String] lines [] = [] lines s = takeLine s:lines (dropLine s) segments :: (a->Bool) -> [a] -> [[a]] segments p [] = [] segments p xs = takeWhile p xs: segments p (drop 1 (dropWhile p xs)) -- * A larger example: counting words in a string -- and produce nicely formatted output, most common word first, -- written in "point-free style" wordCounts :: String -> String wordCounts = unlines . map (\(n,w)->w++": "++show n) . reverse . sort . map (\ws-> (length ws,head ws)) . group . sort . words -- wordCounts weather == -- "is: 3\nwarm: 2\ncold: 1\nJune: 1\nJuly: 1\nJanuary: 1\n" -- Using function composition and eta reduction, -- instead of: f x = f1 (f2 (f3 x)) -- we can write: f = f1 . f2 . f3