Title: Haskell
1Haskell
2Topics
- Review/More Higher Order Functions
- Lambda functions
- Folds
3Higher Order Functions
4Higher-Order functions
- applyTwice (a -gt a) -gt a -gt a
- applyTwice f x f (f x)
- (a -gt 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.)
5Example zipWith
- zipWith' (a -gt b -gt c) -gt a -gt b -gt c
- zipWith' _ _
- zipWith' _ _
- zipWith' f (xxs) (yys) 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 dont 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
6Example flip
- flip' (a-gtb-gtc) -gt (b -gt a -gt 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?
7Flip Example
- 1..10 !! 3 gt 4
- infix operator, list first, followed by ix
- What if we want to define a function that
curries the index? First look at type
signatures. We want a function that takes the
index first, so we can curry it. - Maingt t (!!)
- (!!) a -gt Int -gt a
- This is not quite what we want curries first arg
which is the list, not the ix - Maingt let curryIx (1..10 !!)
- Maingt curryIx 4
- Maingt t (flip (!!))
- (flip (!!)) Int -gt c -gt c
- let nth ix (flip (!!)) ix
- let fourth nth 4
- fourth 1..10
- fourth 20,19..1
http//www.cs.arizona.edu/collberg/Teaching/372/2
008/Html/Html-10/index.html
8Lambda
9Lambda - 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
- -gt
- function body
- Example
- numLongChains Int
- numLongChains length (filter (\xs -gt length xs
gt 15) (map chain 1..100)) - compare to
- numLongChains Int
- numLongChains length (filter isLong (map chain
1..100)) - where isLong xs length xs gt 15
10When not to use lambda
- Dont use lambda when currying and partial
application work those are more readable - Example, use
- map (3) 1,6,3,2
- Not
- map (\x -gt x 3) 1,6,3,2
- Both work... but which would you rather read??
11More on lambda functions
- They can take multiple parameters
- zipWith (\a b -gt (a 30 3) / b) 5,4,3,2,1
1,2,3,4,5 - Can include pattern matching
- BUT, only one pattern (cant fall through as in
normal functions) - map (\(a, b) -gt a b) (1,2),(3,4)
12Folds
13Folds
- 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 xxs
- 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.
14Example recursive sum
- mySum (Num a) gt a -gt a -gt a
- mySum acc acc
- mySum acc (xxs) mySum (xacc) xs
- Maingt mySum 0 2,4,5
- 11
Trace with your partner
15More details
- A fold takes
- A binary function (e.g., , div, etc.)
- A starting value (accumulator)
- A list to fold up
- foldl () 0 2,4,5
- sum' (Num a) gt a -gt a
- sum' xs foldl (\acc x -gt 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
16Can use currying
- sum'' (Num a) gt a -gt a
- sum'' foldl () 0
- Maingt sum'' 3,5
- 8
- What happened to xs? The above returns a
partially applied function that takes a list. - In general, if have 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
17Quick Exercise
- sum could be done as a fold at the command line,
e.g., - Maingt 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)
18Right folds
19Right folds
- foldr is like foldl, except it eats up the
values starting from the right. - In some cases, the result is the same.
- Maingt foldl () 0 3,4,5
- 12
- Maingt foldr () 0 3,4,5
- 12
2,4,5
acc 0
fold
2, 4
acc 5
fold
2
acc 9
fold
acc 11
fold
20Right folds
- The accumulator value of a fold can be any type
including a list. - Maingt foldr (\x acc -gt (2) xacc) 2,3,4
- 4,9,16
- Note that the order of the arguments is reversed
from the order of the parameters to lambda
function - x acc parameters
- 2,3,4 arguments
- compare to left
- foldl (\acc x -gt acc x) 0 2,3,4
2,3, 4
acc
fold
2, 3
acc 16
fold
2
acc 9, 16
fold
acc 4,9,16
fold
21Right folds
- If arguments not reversed
- Maingt foldr (\x acc -gt (2) xacc) 2,3,4
- 2,3,4
- (nothing to eat up so resultacc)
acc 2,3,4
fold
22Can 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. Note the reverse
order in which acc values show up in the list. - Maingt foldl () 0 3,5,2,1
- 11
- Maingt scanl () 0 3,5,2,1
- 0,3,8,10,11
- Maingt foldr (\x acc -gt (2) xacc) 2,3,4
- 4,9,16
- Maingt scanr (\x acc -gt (2) xacc) 2,3,4
- 4,9,16 ,9,16,16,
- Maingt scanr (\x acc -gt (2) xacc) 2,3,4
- 2,3,4
23Right folds to implement map
- Like what we just did
- foldr (\x acc -gt (2) xacc) 2,3,4
- BUT use function passed as argument rather than
2 - map' (a -gt b) -gt a -gt b
- map' f xs foldr (\x acc -gt f x acc) xs
- map' (3) 1,2,3
- 6 (x3, xs1,2)
- 5 6 (x2, xs1)
- 4 5, 6 (x1, xs)
- 4,5,6
24Which to use?
- Could have done map with left fold
- map'' (a -gt b) -gt a -gt b
- map'' f xs foldl (\acc x -gt acc f x) xs
- Note that is slower than
- (why would that make sense?)
- SO, map' will be faster than map''
25Another example with Bool acc
- elem' (Eq a) gt a -gt a -gt Bool
- elem' y ys foldr (\x acc -gt 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 (well do another one in a minute)
26Two more folds
- foldr1 and foldl1
- Like foldr and foldl, but first (or last) element
of the list is the starting value - Cant be called with empty list
- Maingt scanl1 () 2,3,4
- 2,5,9
- Maingt scanl () 0 2,3,4
- 0,2,5,9
- foldl max 0 4,6,2
- foldl1 max -4,-6,-2
27foldl vs foldr
- Try these
- foldr (subtract) 0 5,4,3
- scanr (subtract) 0 5,4,3
- scanr (-) 0 5,4,3
- 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
http//stackoverflow.com/questions/4454559/curryin
g-subtraction https//wiki.haskell.org/Unary_opera
tor https//www.haskell.org/hoogle/?hooglesubtrac
t
28More fold examples
- reverse' a -gt a
- reverse' foldl (\acc x -gt x acc)
- OR
- reverse'' a -gt a
- reverse'' foldl (flip ())
- Quick exercise
- Trace reverse'' 1,2,3
- Remember flip f x y f y x
- Hint use scanl if youre stuck on this
29More fold examples
- filter' (a -gt Bool) -gt a -gt a
- filter' p foldr (\x acc -gt if p x then x acc
else acc) - last' a -gt a
- last' foldl1 (\_ x -gt x)
30Play 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. - Maingt integrate square 2 4 0.001
- 18.66066700000209
- Maingt integrate cube 2 4 0.001
- 59.97200299999252
31Sidebar
32Another 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 gt 18
- If f is subtract and starting value is 0, this
is - (((0 6) 5) 4) 3 gt -18
- compare to foldl 6 - (5 - (4 - (3 0))) gt 2
33Using 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 -gt Bool -gt Bool
- True x x
- False _ False
- Maingt and' (repeat False)
- False
- Foldr works with infinite lists IF binary
function doesnt always evaluate its second
parameter (as in ). Will not work with
infinite lists if second parameter is always
needed.
34Another look at infinite lists
- For foldl the recursion is
- f (... (f ( f (f z x1) x2) x3) ...) xn
- For foldr the recursion is
- f x1 (f x2 (f x3 (...(f xn z) ...)))
- So foldl must get to the end of the list. foldr,
however, evaluates the function with the first
element, then lazily evaluates the rest. So a
function that can determine the final result
based on the left-hand operand can be used with
foldr and an infinite list.
http//lambda.jstolarek.com/2012/09/why-foldr-work
s-for-infinite-lists-and-foldl-doesnt/
35Folds 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 -gt Bool
- and' xs foldr () True xs
- and True, False, True
- True (False (True True))
- Try scanr () True True, False