1 / 29

Haskell

Haskell. Chapter 5, Part II. Topics. Review/More Higher Order Functions Lambda functions Folds. Higher Order Functions. Higher-Order functions. applyTwice :: (a -> a) -> a -> a applyTwice f x = f (f x) (a -> a) is a function parentheses are needed.

duane
Download Presentation

Haskell

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Haskell Chapter 5, Part II

  2. Topics • Review/More Higher Order Functions • Lambda functions • Folds

  3. Higher Order Functions

  4. Higher-Order functions applyTwice :: (a -> a) -> a -> a applyTwice f x = f (f x) • (a -> a) is a function • parentheses are needed. • a is (of course) a type parameter, maybe Int, String, etc. • BUT, parameter and result must have the same type • Try: • applyTwice(+3) 10 • applyTwice (++ " woot") "say" • applyTwice (3:) [1] • Note that we are passing partially applied functions (e.g., 3:, +3, etc.)

  5. Example: zipWith zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c] zipWith' _ [] _ = [] zipWith' _ _ [] = [] zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xsys • joins two lists by applying function to corresponding elements • must handle cases where lists are not equal length • lists don’t need to have same type • Try • zipWith' (+) [4,2,5] [2,6,2] • zipWith' (max) [4,2,5, 3] [2,6,2] • zipWith' (++) ["foo ", "bar "] ["fighters", "bells"] • zipWith' (*) (replicate 5 2) [1..] // replicates 2 5x • zipWith' (zipWith' (*)) [[2,3],[4,6]] [[10,20],[100, 200]]

  6. Example: flip flip' :: (a->b->c) -> (b -> a -> c) flip' f = g where g x y = f y x • Try: • zip [1,2,3,4,5] "hello" • flip' zip [1,2,3,4,5] "hello"

  7. Lambda

  8. Lambda - l • Anonymous function we use when that function is only needed once • Typically use to pass to a higher-order function • Syntax: • \ (kind of like l) • function parameters • -> • function body • Example: numLongChains :: Int numLongChains = length (filter (\xs -> length xs > 15) (map chain [1..100])) • compare to: numLongChains :: Int numLongChains = length (filter isLong (map chain [1..100])) where isLongxs = length xs > 15

  9. When not to use lambda • Don’t use lambda when currying and partial application work… those are more readable • Example, use: • map (+3) [1,6,3,2] • Not • map (\x -> x + 3) [1,6,3,2] • Both work... but which would you rather read??

  10. More on lambda functions • They can take multiple parameters zipWith (\a b -> (a * 30 + 3) / b) [5,4,3,2,1] [1,2,3,4,5] • Can include pattern matching • BUT, only one pattern (can’t fall through as in normal functions) • map (\(a, b) -> a + b) [(1,2),(3,4)]

  11. Folds

  12. Folds • A programming language can make it quicker to write code if it includes language constructs that capture common patterns • Think about common recursive pattern: • Base case: empty list • Pattern match x:xs • Perform some action on x and (recursively) on xs • In Haskell, this is what a fold does! • Can be used whenever you want to traverse a list once and return something.

  13. More details • A fold takes: • A binary function (e.g., +, div, etc.) • A starting value (accumulator) • A list to fold up sum' :: (Num a) => [a] -> a sum' xs = foldl (\acc x -> acc + x) 0 xs • sum’ [2,4,5] • 0 + 2 • 2 + 4 • 6 + 5 • 11 [2,4,5] acc 0 fold [4,5] acc 2 fold [5] acc 6 fold [] acc 11 fold

  14. Can use currying sum'' :: (Num a) => [a] -> a sum'' = foldl (+) 0 • *Main> sum'' [3,5] • 8 • What happened to xs? The above returns a partially applied function that takes a list. • In general, if have fn foo a = bar b a • can rewrite as foo = bar b • then call foo a • Note that the definition is more concise without the lambda

  15. Quick Exercise sum could be done as a fold at the command line, e.g., • *Main>foldl (+) 0 [3,4,5] • 12 EXERCISE • Use a fold1 to create the product of the numbers in a list (just do this at the GHCi prompt, no function definition) • Use a foldl to append strings stored in a list to an initial string of “Hello ” • Use a foldl to subtract a list of numbers from an initial value (could be subtracting purchases from your wallet, for example)

  16. Right folds

  17. Right folds • foldr is like foldl, except it “eats up” the values starting from the right. • In some cases, the result is the same. • *Main>foldl (+) 0 [3,4,5] • 12 • *Main>foldr (+) 0 [3,4,5] • 12 [2,4,5] acc 0 fold [2, 4] acc 5 fold [2] acc 9 fold [] acc 11 fold

  18. Right folds The accumulator value of a fold can be any type – including a list. • *Main>foldr (\x acc -> (^2) x:acc) [] [2,3,4] • [4,9,16] • Note that the order of the arguments is reversed from the order of the parameters (x accparameters, [] [2,3,4] arguments) If arguments not reversed: • *Main>foldr (\x acc -> (^2) x:acc) [2,3,4] [] • [2,3,4] • (nothing to “eat up” so result=acc) [2,3, 4] acc [] fold [2, 3] acc [16] fold [2] acc [9, 16] fold [] acc [4,9,16] fold

  19. Can I trace this? • scanl and scanr (and scanl1, scanr1) are like foldl and foldr, except they report intermediate accumulator states. • Used to monitor the progress of a function that can be implemented as a fold. • *Main>foldl (+) 0 [3,5,2,1] • 11 • *Main> scanl (+) 0 [3,5,2,1] • [0,3,8,10,11] • *Main>scanr (\x acc -> (^2) x:acc) [] [2,3,4] • [[4,9,16],[9,16],[16],[]] • *Main>scanr (\x acc -> (^2) x:acc) [2,3,4] [] • [[2,3,4]]

  20. Right folds – to implement map • Like what we just did • foldr(\x acc -> (^2) x:acc) [] [2,3,4] • BUTuse function passed as argument rather than ^2 map' :: (a -> b) -> [a] -> [b] map' fxs = foldr (\x acc -> f x : acc) [] xs • map' (+3) [1,2,3] • 3 : [ ] • 2 : [3] • 1 : [2,3] • [1,2,3]

  21. Which to use? • Could have done map with left fold: map'' :: (a -> b) -> [a] -> [b] map'' f xs = foldl (\acc x -> acc ++ [f x]) [] xs • Note that ++ is slower than : • (why would that make sense?) • SO, map' will be faster than map''

  22. Another example – with Bool acc elem' :: (Eq a) => a -> [a] -> Bool elem' y ys = foldr (\x acc -> if x == y then True else acc) False ys • Note that accumulator starts with False • This code will work with an empty list Trace with your partner (we’ll do another one in a minute)

  23. Two more folds • foldr1 and foldl1 • Like foldr and foldl, but first (or last) element of the list is the starting value • Can’t be called with empty list • *Main> foldl1 (+) [2,3,4] • 9 maximum' :: (Ord a) => [a] -> a maximum' = foldl1 max

  24. More fold examples reverse' :: [a] -> [a] reverse' = foldl (\acc x -> x : acc) [] • OR reverse'' :: [a] -> [a] reverse'' = foldl (flip (:)) [] • Quick exercise: • Trace reverse'' [1,2,3] • Remember: flip f x y = f y x

  25. More fold examples filter' :: (a -> Bool) -> [a] -> [a] filter' p = foldr (\x acc -> if p x then x : acc else acc) [] last' :: [a] -> a last' = foldl1 (\_ x -> x)

  26. Another look at folds • Can view as successive applications of some function to elements in a list • Assume right fold, binary function f, starting acc z • do foldron [3,4,5,6] • this is essentially • f 3 (f 4 (f 5 ( f 6 z))) • If f is + and starting value is 0, this is: • 3 + (4 + (5 + (6 + 0)))

  27. Folds and infinite lists • && returns True if all elements are True, False if any element is False • So as soon as a False is encountered, the result is False and' :: [Bool] -> Bool and' xs = foldr (&&) True xs • and [True, False, True] • True && (False && (True && True))

  28. Using and with infinite list • repeat False • False && (False && (False && (False …. • Haskell is lazy. Only generates items as needed. • && returns False if one of its parameters is False. (&&) :: Bool -> Bool -> Bool True && x = x False && _ = False • *Main> and' (repeat False) • False • Foldr works with infinite lists IF binary function doesn’t always evaluate its second parameter (as in &&). Will not work with infinite lists if second parameter is always needed.

  29. Play and Share – higher order functions • Write a function divisibleBy such that: divisibleBy 2 4 returns True, divisibleBy 2 5 returns False • Try: map (divisibleBy 2) [2,3,4] • Write a function divisibleByFive that returns a partially applied divisibleBy function • Try: map divisibleByFive [2,4,5] • Write isDivisibleByFive that uses a lambda function with map to achieve the same result (e.g., returns [False, False, True] for [2,4,5]) Suggested by a former student: • Create a higher-order function named integrate that takes a function, range, and step size and computes approximate numerical integration by evaluating the function at each step. • *Main> integrate square 2 4 0.001 • 18.66066700000209 • *Main> integrate cube 2 4 0.001 • 59.97200299999252

More Related