Download presentation
Presentation is loading. Please wait.
Published byShon Woods Modified over 8 years ago
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 xs ys 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" zip "hello" [1,2,3,4,5] let subIt x y = x - y let subItFlipped = flip subIt subIt 5 3 subItFlipped 5 3 Why would this be useful?
7
Lambda
8
Lambda - Anonymous function we use when that function is only needed once Typically use to pass to a higher-order function Syntax: \ (kind of like ) 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 isLong xs = 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
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 fold acc 0 [2,4,5] fold acc 2 [4,5] fold acc 6 [5][5] fold acc 11 []
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
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 fold acc 0 [2,4,5] fold acc 5 [2, 4] fold acc 9 [2][2] fold acc 11 []
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 acc parameters [] [2,3,4] arguments compare to left: foldl (\acc x -> acc + x) 0 [2,3,4] fold acc [] [2,3, 4] fold acc [16] [2, 3] fold acc [9, 16] [2][2] fold acc [4,9,16] []
19
Right folds If arguments not reversed: *Main> foldr (\x acc -> (^2) x:acc) [2,3,4] [] [2,3,4] (nothing to “eat up” so result=acc) fold acc [2,3,4] [] [2, 3]
20
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]]
21
Right folds – to implement map Like what we just did foldr (\x acc -> (^2) x:acc) [] [2,3,4] BUT use function passed as argument rather than ^2 map' :: (a -> b) -> [a] -> [b] map' f xs = foldr (\x acc -> f x : acc) [] xs map' (+3) [1,2,3] 3 : [ ] 2 : [3] 1 : [2,3] [1,2,3]
22
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''
23
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 elem' 'c' ['a'..'d'] Trace with your partner (we’ll do another one in a minute)
24
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 foldl max [4,6,2] foldl 0 max [4,6,2] foldl1 max [-4,-6,-2] foldl 0 max [-4,-6,-2] – doesn’t work! needs type signature
25
foldl vs foldr Try these: foldr (subtract) 0 [5,4,3] scanr (subtract) 0 [5,4,3] scanr (subtract) [5,4,3] 0 -- doesn’t work, why? foldl (subtract) 0 [5,4,3] scanl (subtract) 0 [5,4,3] foldl (flip (subtract)) 0 [5,4,3] foldl1 (subtract) [5,4,3] scanl1 (flip (subtract)) [5,4,3]
26
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 Hint: use scanl if you’re stuck on this
27
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)
28
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 foldr on [3,4,5,6] this is essentially f (f (f ( f z 6) 5) 4) 3 If f is + and starting value is 0, this is: (((6 + 0) + 5) + 4) + 3 => 18 If f is subtract and starting value is 0, this is: (((0 – 6) – 5) – 4) – 3 => -18 compare to foldl: 6 - (5 - (4 - (3 – 0))) => 2
29
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)) Try: scanr (&&) True [True, False]
30
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.
31
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
Similar presentations
© 2024 SlidePlayer.com. Inc.
All rights reserved.