module L4B where import Test.QuickCheck import Data.List import Data.Char(isSpace) -- Higher-Order Functions -- dave 2017-09-21 -- Continued 5A part 1 (2017-09-26) ------------------------------------------ -- Taking similar definitions and -- making a function for the common -- programming pattern: data Card = Card {rank :: Rank, suit :: Suit} data Rank = Numeric Int | Jack -- etc data Suit = Hearts | Diamonds | Clubs | Spades deriving (Eq,Show) type Hand = [Card] countClubs :: Hand -> Int countClubs [] = 0 countClubs (c:cs) | suit c == Clubs = 1 + countClubs cs | otherwise = countClubs cs countSpades :: Hand -> Int countSpades h = count Spades h count :: Suit -> Hand -> Int count s [] = 0 count s (c:cs) | suit c == s = 1 + count s cs | otherwise = count s cs -- ** The most basic HOFs ** -- 1. map and filter examples, (Kahoot) -- ** Why do we need/want HOFs? ** -- . A common programming pattern: -- combining elements of a list -- (e.g. sum, product, and, or) {- sum' [] = 0 sum' (n:ns) = n + sum' ns -} prod [] = 1 prod (n:ns) = n * prod ns concat' [] = [] concat' (xs:xss) = xs ++ concat' xss foldr' f b [] = b foldr' f b (x:xs) = f x (foldr' f b xs) -- Foldr op b (a:b:c:...) == a `op` (b `op` (... `op` b)...) -- foldl op z (a:b:c:...) == ((base `op` a) `op` b) ...) song2 :: String song2 = unlines [ "I got my head checked" , "By a jumbo jet" , "It wasn't easy" , "But nothing is" , "No" ] -- Example use of foldr: unlines (c.f. lines) unlines' s = foldr joinLines "" s where joinLines s1 s2 = s1 ++ "\n" ++ s2 -- Example: takeLine, takeWord takeLine [] = [] takeLine (c:cs) | c /= '\n' = c:takeLine cs | otherwise = [] takeWord s = takeWhile (not . isSpace) s -- eta conversion -- takeWhile (\c -> not(isSpace c)) s -- where nonSpace c = not(isSpace c) takeWhile' p [] = [] takeWhile' p (c:cs) | p c = c:takeWhile' p cs | otherwise = [] -- f . g = \x -> f(g x) -- generalisation: takeWhile -- Defining takeLine using takeWhile, illustrating -- (i) anonymous functions (also known as lambda expressions) -- (ii) sections [slide] -- (iii) function composition ------------------------------------- -- More ways to build functions: Partial application -- A not very useful function: rep :: Int -> (String -> (Char -> String)) rep n s c = concat (replicate n (s++[c])) repfyr :: String -> Char -> String repfyr = rep 4 fyrfallig :: Char -> String fyrfallig = repfyr "hurra" -- redundant brackets in the type help explain partial application: -- Eta reduction -- use hlint to find this kind of thing (C-c C-v) -- Slides recapping function composition and partial application -- Aside: -- Fixity. How do you know when to skip brackets? threeLines s = unlines $ take 3 $ lines s -- $ (example: first three lines of a string) -- generalising zip, example -- numberLines :: String -> String -- number the lines in a text numberLines s = unlines $ zipWith makeLine [1..] (lines s) -- [makeLine n l | (n,l) <- zip [1..] (lines s) ] where makeLine num line = show num ++ "\t" ++ line -- zipWith' f l1 l2 = [f e1 e2 | (e1,e2) <-zip l1 l2]