-- | Higher Order Functions -- Examples to introduce and illustrate the power of higher order functions -- Functional Programming course 2016. -- Thomas Hallgren {- This is just a skeleton, the definitions will be 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) -- * First 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 base [] = base foldr op base (x:xs) = x `op` foldr op base xs sum' xs = foldr (+) 0 xs product' xs = foldr (*) 1 xs concat' xss = foldr (++) [] xss 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 -- * Can we rewrite map & filter too with foldr? -- Yes, but is the code shorter and more readable than -- when defining them directly with recursion as above? map' f xs = foldr op [] xs where op x ys = f x:ys filter' p xs = foldr op [] xs where op x ys | p x = x:ys | otherwise = ys -- * More examples takeLine [] = [] takeLine ('\n':cs) = [] takeLine (c:cs) = c:takeLine cs takeWord [] = [] takeWord (c:cs) | not (isSpace c) = c:takeWord cs | otherwise = [] takeWhile :: (a->Bool) -> [a] -> [a] takeWhile p [] = [] takeWhile p (x:xs) | p x = x:takeWhile p xs | otherwise = [] takeLine' s = takeWhile (/='\n') s takeWord' s = takeWhile (not . isSpace) s dropWhile :: (a->Bool) -> [a] -> [a] dropWhile p [] = [] dropWhile p (x:xs) | p x = dropWhile p xs | otherwise = x:xs dropLine s = dropWhile (/='\n') s lines :: String -> [String] lines [] = [] lines s = takeLine s:lines (drop 1 (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, -- written in "point-free style" countWords :: String -> String countWords = unlines . map (\(n,w)->w++": "++show n) . reverse . sort . map (\ws->(length ws,head ws)) . group . sort . words {- Examples of tail recursion -- Tail recursive last [x] = x last (x:xs) = last xs -- Not tail recursive length [] = 0 length (x:xs) = 1 + length xs -- Tail recursive length xs = len 0 xs where len n [] = n len n (x:xs) = len (n+1) xs -}