-- | Higher Order Functions
-- Examples to introduce and illustrate the power of higher order functions
-- Functional Programming course 2017.
-- Thomas Hallgren
--------------------------------------------------------------------------------
{-
This started as a skeleton, the definitions were 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)
--
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
filter' p xs = foldr keep [] xs
where keep x ys | p x = x:ys
| otherwise = ys
-- * 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 u [] = u
foldr op u (x:xs) = x `op` foldr op u xs
sum' xs = foldr (+) 0 xs
product' xs = foldr (*) 1 xs
concat' xss = foldr (++) [] xss
-- * More examples
weather = "June is warm\nJuly is warm\nJanuary is cold\n"
takeLine "" = ""
takeLine (c:cs) | c/='\n' = c:takeLine cs
| otherwise = ""
takeWord "" = ""
takeWord (c:cs) | not (isSpace c) = c:takeWord cs
| otherwise = ""
takeWhile p [] = []
takeWhile p (x:xs) | p x = x:takeWhile p xs
| otherwise = []
--------------------------------------------------------------------------------
-- * For ways to create functions without giving them a name
-- This is useful when we pass functions as arguments to higher order functions
takeLine_ s = takeWhile notNl s
where notNl c = c/='\n' -- giving the function a name
takeLine' s = takeWhile (/='\n') s -- sections
takeWord' s = takeWhile (not . isSpace) s -- function composition
takeWord'' s = takeWhile (\c->not (isSpace c)) s -- lambda abstraction
-- f x = ...x....x...
-- f = \ x -> ...x...x...
notEqual x y = x/=y
takeWord''' s = takeWhile (notEqual '\n') s -- partial application
--------------------------------------------------------------------------------
-- * Functions returning functions (example from the slides)
pick :: Int -> (a,a) -> a
pick 1 = fst
pick 2 = snd
--------------------------------------------------------------------------------
-- * 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
--------------------------------------------------------------------------------
-- * Filled in after the lecture
dropWhile :: (a->Bool) -> [a] -> [a]
dropWhile p [] = []
dropWhile p (x:xs) | p x = dropWhile p xs
| otherwise = x:xs
dropLine s = dropWhile (/='\n') s
-- | Break a string into lines (first order)
lines :: String -> [String]
lines "" = []
lines s = takeLine s : lines (drop 1 (dropLine s))
-- | Generalize lines into segments (higher order)
segments :: (a->Bool) -> [a] -> [[a]]
segments p [] = []
segments p xs = takeWhile p xs : segments p (drop 1 (dropWhile p xs))