0% found this document useful (0 votes)
6 views32 pages

Haskell HigherOrderFunctions

Higher-order functions in Haskell are functions that take other functions as arguments or return them as results, allowing for more concise and readable code. Key examples include 'map', which applies a function to each element of a list, 'filter', which selects elements based on a predicate, and 'foldr', which reduces a list to a single value using a binary function. The document also discusses currying, operator sections, and higher-order list-breaking functions like 'takeWhile' and 'dropWhile'.

Uploaded by

Alexandro Junior
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views32 pages

Haskell HigherOrderFunctions

Higher-order functions in Haskell are functions that take other functions as arguments or return them as results, allowing for more concise and readable code. Key examples include 'map', which applies a function to each element of a list, 'filter', which selects elements based on a predicate, and 'foldr', which reduces a list to a single value using a binary function. The document also discusses currying, operator sections, and higher-order list-breaking functions like 'takeWhile' and 'dropWhile'.

Uploaded by

Alexandro Junior
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Haskell — Higher-Order Functions

Copyright c 2013 Christian Collberg

1/32
Higher-Order Functions

A function is Higher-Order if it takes a function as an


argument or returns one as its result.
Higher-order function aren’t weird; the differentiation
operation from high-school calculus is higher-order:

deriv :: (Float->Float)->Float->Float
deriv f x = (f(x+dx) - f x)/0.0001

Many recursive functions share a similar structure. We can


capture such “recursive patterns” in a higher-order function.
We can often avoid the use of explicit recursion by using
higher-order functions. This leads to functions that are
shorter, and easier to read and maintain.

2/32
Currying Revisited

We have already seen a number of higher-order functions. In


fact, any curried function is higher-order. Why? Well, when a
curried function is applied to one of its arguments it returns a
new function as the result.
Uh, what was this currying thing?
A curried function does not have to be applied to all its
arguments at once. We can supply some of the arguments,
thereby creating a new specialized function. This function
can, for example, be passed as argument to a higher-order
function.

3/32
Currying Revisited. . .

How is a curried function defined?


A curried function of n arguments (of types t1 , t2 , · · · , tn )
that returns a value of type t is defined like this:

fun :: t1 -> t2 -> · · · -> tn -> t

This is sort of like defining n different functions (one for each


->). In fact, we could define these functions explicitly, but
that would be tedious:

fun1 :: t2 -> · · · -> tn -> t


fun1 a2 · · · an = · · ·

fun2 :: t3 -> · · · -> tn -> t


fun2 a3 · · · an = · · ·

4/32
Currying Revisited. . .

Duh, how about an example?


Certainly. Lets define a recursive function get nth n xs
which returns the n:th element from the list xs:

get nth 1 (x: ) = x


get nth n ( :xs) = get nth (n-1) xs

get nth 10 "Bartholomew" ⇒ ’e’

Now, let’s use get nth to define functions get second,


get third, get fourth, and get fifth, without using
explicit recursion:
get second = get nth 2 get fourth = get nth 4
get third = get nth 3 get fifth = get nth 5

5/32
Currying Revisited. . .

get fifth "Bartholomew" ⇒ ’h’

map (get nth 3)


["mob","sea","tar","bat"] ⇒
"bart"

So, what’s the type of get second?


Remember the Rule of Cancellation?
The type of get nth is Int -> [a] -> a.
get second applies get nth to one argument. So, to get the
type of get second we need to cancel get nth’s first type:
\\\ -> [a] -> a ≡ [a] -> a.
Int

6/32
Patterns of Computation

Mappings
Apply a function f to the elements of a list L to make a new
list L′ . Example: Double the elements of an integer list.
Selections
Extract those elements from a list L that satisfy a predicate p
into a new list L′ . Example: Extract the even elements from
an integer list.
Folds
Combine the elements of a list L into a single element using a
binary function f . Example: Sum up the elements in an
integer list.

7/32
The map Function

map takes two arguments, a function and a list. map creates a


new list by applying the function to each element of the input
list.
map’s first argument is a function of type a -> b. The second
argument is a list of type [a]. The result is a list of type [b].

map :: (a -> b) -> [a] -> [b]


map f [ ] = [ ]
map f (x:xs) = f x : map f xs

We can check the type of an object using the :type


command. Example: :type map.

8/32
The map Function. . .

map :: (a -> b) -> [a] -> [b]


map f [ ] = [ ]
inc [1,2,3,4]
map f (x:xs)= f x : map f xs

inc x = x + 1
map

map inc [1,2,3,4] ⇒ [2,3,4,5]


[inc 1,inc 2,inc 3,inc 4]

[2,3,4,5]

9/32
The map Function. . .

map :: (a -> b) -> [a] -> [b]


map f [ ] = [ ]
map f (x:xs) = f x : map f xs

map f [ ] = [ ] means: “The result of applying the function f to


the elements of an empty list is the empty list.”
map f (x:xs) = f x : map f xs means: “applying f to the list
(x:xs) is the same as applying f to x (the first
element of the list), then applying f to the list xs,
and then combining the results.”

10/32
The map Function. . .

Simulation:

map square [5,6] ⇒


square 5 : map square [6] ⇒
25 : map square [6] ⇒
25 : (square 6 : map square [ ]) ⇒
25 : (36 : map square [ ]) ⇒
25 : (36 : [ ]) ⇒
25 : [36] ⇒
[25,36]

11/32
The filter Function

Filter takes a predicate p and a list L as arguments. It returns


a list L′ consisting of those elements from L that satisfy p.
The predicate p should have the type a -> Bool, where a is
the type of the list elements.
Examples:

filter even [1..10] ⇒ [2,4,6,8,10]


filter even (map square [2..5]) ⇒
filter even [4,9,16,25] ⇒ [4,16]
filter gt10 [2,5,9,11,23,114]
where gt10 x = x > 10 ⇒ [11,23,114]

12/32
The filter Function. . .

We can define filter using either recursion or list


comprehension.
Using recursion:

filter :: (a -> Bool) -> [a] -> [a]


filter [] = []
filter p (x:xs)
| p x = x : filter p xs
| otherwise = filter p xs

Using list comprehension:

filter :: (a -> Bool) -> [a] -> [a]


filter p xs = [x | x <- xs, p x]

13/32
The filter Function. . .

filter :: (a->Bool)->[a]->[a]
filter [] = []
even [1,2,3,4]
filter p (x:xs)
| p x = x : filter p xs
| otherwise = filter p xs f
[even 1, even 2,
i
even 3, even 4]
l
filter even [1,2,3,4] ⇒ [2,4] t
[False, True,
e
False, True] r

[2,4]

14/32
The filter Function. . .

doublePos doubles the positive integers in a list.


getEven :: [Int] -> [Int]
getEven xs = filter even xs

doublePos :: [Int] -> [Int]


doublePos xs = map dbl (filter pos xs)
where dbl x = 2 * x
pos x = x > 0
Simulations:
getEven [1,2,3] ⇒ [2]

doublePos [1,2,3,4] ⇒
map dbl (filter pos [1,2,3,4]) ⇒
map dbl [2,4] ⇒ [4,8]
15/32
fold Functions

A common operation is to combine the elements of a list into


one element. Such operations are called reductions or
accumulations.
Examples:

sum [1,2,3,4,5] ≡
(1 + (2 + (3 + (4 + (5 + 0))))) ⇒ 15
concat ["H","i","!"] ≡
("H" ++ ("i" ++ ("!" ++ ""))) ⇒ "Hi!"

Notice how similar these operations are. They both combine


the elements in a list using some binary operator (+, ++),
starting out with a “seed” value (0, "").

16/32
fold Functions. . .

Haskell provides a function foldr (“fold right”) which


captures this pattern of computation.
foldr takes three arguments: a function, a seed value, and a
list.
Examples:

foldr (+) 0 [1,2,3,4,5] ⇒ 15


foldr (++) "" ["H","i","!"] ⇒ "Hi!"

foldr:

foldr :: (a->b->b) -> b -> [a] -> b


foldr f z [ ] = z
foldr f z (x:xs) = f x (foldr f z xs)

17/32
fold Functions. . .

Note how the fold process is started by combining the last


element xn with z. Hence the name seed.
foldr(⊕)z[x1 · · · xn ] = (x1 ⊕ (x2 ⊕ (· · · (xn ⊕ z))))

Several functions in the standard prelude are defined using


foldr:

and,or :: [Bool] -> Bool


and xs = foldr (&&) True xs
or xs = foldr (||) False xs

? or [True,False,False] ⇒
foldr (||) False [True,False,False] ⇒
True || (False || (False || False)) ⇒ True

18/32
fold Functions. . .

Remember that foldr binds from the right:

foldr (+) 0 [1,2,3] ⇒ (1+(2+(3+0)))

There is another function foldl that binds from the left:

foldl (+) 0 [1,2,3] ⇒ (((0+1)+2)+3)

In general:
foldl(⊕)z[x1 · · · xn ] = (((z ⊕ x1 ) ⊕ x2 ) ⊕ · · · ⊕ xn )

19/32
fold Functions. . .

In the case of (+) and many other functions

foldl(⊕)z[x1 · · · xn ] = foldr(⊕)z[x1 · · · xn ]

However, one version may be more efficient than the other.

20/32
fold Functions. . .

⊕ ⊕

x1 ⊕
⊕ xn
x2 ⊕
⊕ x3
x3
⊕ x2

xn z x1
z
foldr ⊕ z [x1 · · · xn ] foldl ⊕ z [x1 · · · xn ]

21/32
Operator Sections

We’ve already seen that it is possible to use operators to


construct new functions:
(*2) – function that doubles its argument
(>2) – function that returns True for numbers > 2.

Such partially applied operators are known as operator


sections. There are two kinds:
(op a) b = b op a

(*2) 4 = 4 * 2 = 8
(>2) 4 = 4 > 2 = True
(++ "\n") "Bart" = "Bart" ++ "\n"

22/32
Operator Sections. . .

(a op) b = a op b

(3:) [1,2] = 3 : [1,2]= [3,1,2]


(0<) 5 = 0 < 5 = True
(1/) 5 = 1/5

Examples:
(+1) – The successor function.
(/2) – The halving function.
(:[]) – The function that turns an element into a singleton
list.
More Examples:

? filter (0<) (map (+1) [-2,-1,0,1])


[1,2]
23/32
takeWhile & dropWhile

We’ve looked at the list-breaking functions drop & take:

take 2 [’a’,’b’,’c’] ⇒ [’a’,’b’]


drop 2 [’a’,’b’,’c’] ⇒ [’c’]

takeWhile and dropWhile are higher-order list-breaking


functions. They take/drop elements from a list while a
predicate is true.

takeWhile even [2,4,6,5,7,4,1] ⇒


[2,4,6]
dropWhile even [2,4,6,5,7,4,1] ⇒
[5,7,4,1]

24/32
takeWhile & dropWhile. . .

takeWhile :: (a->Bool) -> [a] -> [a]


takeWhile p [ ] = [ ]
takeWhile p (x:xs)
| p x = x : takeWhile p xs
| otherwise = [ ]

dropWhile :: (a->Bool) -> [a] -> [a]


dropWhile p [ ] = [ ]
dropWhile p (x:xs)
| p x = dropWhile p xs
| otherwise = x:xs

25/32
takeWhile & dropWhile. . .

Remove initial/final blanks from a string:

dropWhile ((==) ’ ’) " Hi!" ⇒


"Hi!"

takeWhile ((/=) ’ ’) "Hi! " ⇒


"Hi!"

26/32
Summary

Higher-order functions take functions as arguments, or return


a function as the result.
We can form a new function by applying a curried function to
some (but not all) of its arguments. This is called partial
application.
Operator sections are partially applied infix operators.

27/32
Summary. . .

The standard prelude contains many useful higher-order


functions:
map f xs creates a new list by applying the function f to
every element of a list xs.
filter p xs creates a new list by selecting only those
elements from xs that satisfy the predicate p
(i.e. (p x) should return True).
foldr f z xs reduces a list xs down to one element, by
applying the binary function f to successive
elements, starting from the right.
scanl/scanr f z xs perform the same functions as
foldr/foldl, but instead of returning only the
ultimate value they return a list of all
intermediate results.
28/32
Exercise

Exercise (a):
Define the map function using a list comprehension.
Template:

map f x = [ · · · | · · · ]

Exercise (b):
Use map to define a function lengthall xss which takes a
list of strings xss as argument and returns a list of their
lengths as result.
Examples:

? lengthall ["Ay", "Caramba!"]


[2,8]

29/32
Exercise

1 Give a accumulative recursive definition of foldl.


2 Define the minimum xs function using foldr.
3 Define a function sumsq n that returns the sum of the
squares of the numbers [1 · · · n]. Use map and foldr.
4 What does the function mystery below do?

mystery xs =
foldr (++) [] (map sing xs)
sing x = [x]

Examples:

minimum [3,4,1,5,6,3] ⇒ 1

30/32
Exercise. . .

Define a function zipp f xs ys that takes a function f and


two lists xs=[x1 , · · · , xn ] and ys=[y1 , · · · , yn ] as argument,
and returns the list [f x1 y1 , · · · , f xn yn ] as result.
If the lists are of unequal length, an error should be returned.
Examples:

zipp (+) [1,2,3] [4,5,6] ⇒ [5,7,9]

zipp (==) [1,2,3] [4,2,2] ⇒ [False,True,False]

zipp (==) [1,2,3] [4,2] ⇒ ERROR

31/32
Exercise

Define a function filterFirst p xs that removes the first


element of xs that does not have the property p.
Example:

filterFirst even [2,4,6,5,6,8,7] ⇒


[2,4,6,6,8,7]

Use filterFirst to define a function filterLast p xs


that removes the last occurence of an element of xs without
the property p.
Example:

filterLast even [2,4,6,5,6,8,7] ⇒


[2,4,6,5,6,8]

32/32

You might also like