CSc 372 - Comparative Programming Languages
11 : Haskell -- Higher-Order Functions
Christian Collberg
Department of Computer Science
University of Arizona
- 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:
- 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.
- 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.
How is a curried function defined?
- A curried function of
arguments (of types
) that
returns a value of type t is defined
like this:
- This is sort of like defining
different functions
(one for each ->). In fact, we could define
these functions explicitly, but that would be tedious:
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:
- Now, let's use get_nth to define functions
get_second, get_third, get_fourth,
and get_fifth, without using explicit recursion:
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:
Int
-> [a] -> a
[a] -> a.
Mappings
- Apply a function
to the elements
of a list
to make a new list
.
Example: Double the elements of an integer list.
Selections
- Extract those elements from a list
that satisfy a predicate
into a new list
.
Example: Extract the even elements from an integer list.
Folds
- Combine the elements of a list
into
a single element using a binary function
.
Example: Sum up the elements in an integer list.
- 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].
- We can check the type of an object using the
:type command. Example: :type map.
- 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.''
Simulation:
- Filter takes a predicate
and a list
as arguments. It returns a list
consisting
of those elements from
that satisfy
.
- The predicate
should have the type
a -> Bool, where a is the
type of the list elements.
Examples:
- We can define filter using either recursion or list
comprehension.
Using recursion:
Using list comprehension:
- doublePos doubles the positive integers in a list.
Simulations:
- A common operation is to combine the elements
of a list into one element. Such operations are
called reductions or accumulations.
Examples:
- 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, "").
- 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:
- Note how the fold process is started by
combining the last element
with
z. Hence the name seed.
- Several functions in the standard prelude
are defined using foldr:
- Remember that foldr binds from the right:
- There is another function foldl that binds from the left:
- In the case of (+) and many other
functions
- However, one version may be more efficient than the other.
fold
- 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
.
- Such partially applied operators are know as
operator sections. There are two kinds:
(op a) b = b op a
(a op) b = a op b
Examples:
- (+1) -
- The successor function.
- (/2) -
- The halving function.
- (:[]) -
- The function that turns an
element into a singleton list.
More Examples:
- We've looked at the list-breaking functions
drop & take:
- takeWhile and dropWhile are
higher-order list-breaking functions. They
take/drop elements from a list while a
predicate is true.
- Remove initial/final blanks from a string:
- 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.
- 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.
Homework (a):
- Define the map function using a
list comprehension.
Template:
Homework (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:
- Give a accumulative recursive definition of foldl.
- Define the minimum xs function using foldr.
- Define a function sumsq n that returns the sum
of the squares of the numbers
. Use
map and foldr.
- What does the function mystery below do?
Examples:
- Define a function zipp f xs ys that
takes a function f and two
lists xs=[
] and
ys=[
] as argument,
and returns the list
[
]
as result.
- If the lists are of unequal length, an error should be
returned.
Examples:
- Define a function filterFirst p xs that
removes the first element of xs that does not
have the property p.
Example:
- Use filterFirst to define a function
filterLast p xs that
removes the last occurence of an element of xs
without the property p.
Example:
Christian S. Collberg
2005-09-16