CS 224 Lab 1 - A Haskell interlude with Currying and Higher-Order Functions
Objectives
Perform the following tasks in the order given.
plus :: Int -> Int -> Int
plus x y = x + y
Well the expression plus 3 4 is really equivalent to ((plus 3) 4). The result of plus 3 is then applied to the argument 4. This means that the value of (plus 3) must also be a function!
Indeed, we could define a new function to be the result of (plus 3) as follows:
plusThree :: Int -> Int
plusThree = plus 3
We would get the result that we expect when using this new function.
> plusThree 4
7
This method of applying functions to one argument at a time is called currying (after Haskell B. Curry). Curried functions can be applied to one argument only, giving another function. Sometimes these new functions can be useful in their own right. Consider the following function:
twice :: (Int -> Int) -> Int -> Int
twice f x = f (f x)
The function twice takes as arguments a function and an integer and applies the function twice to the integer argument. We could use the function resulting in using only the first argument to get the following new functions:
add2 = twice (+1)
quad = twice square
What would be the result of the
expressions add2 3 and quad 2 ? Try them out.
The function twice is an example of a higher-order function. Higher-order functions take functions as parameters and can also return functions. You should already be familiar with the higher-order functions map and filter. The function map takes a function and a list and applies the function to every item in the list. The function filter takes a predicate and a list and returns a list of all the items that satisfy the predicate.
We can use filter to define the
function quickSort to sort a list. The idea behind quickSort
is to pick an element x in the list and divide the list
into three parts, all the items less than x, all the items
equal to x, and all the items greater than x. We
recursively sort the first and third parts and concatenate them all
together.
quickSort :: Ord a
=> [a] ->
[a]
quickSort [] = []
quickSort (x : xs) = (quickSort less) ++ (x
: equal) ++ (quickSort more) where
less = filter
(< x) xs
equal =
filter
(== x) xs
more =
filter
(> x) xs
What happens when we sort this list with quickSort? The reason is that the typicall ASCII ordering of characters specifies that upper case characters are "less than" lower case characters.
To make things more flexible we will redefine quickSort to take an extra argument which will be a comparison function that compares two values returns values LT, EQ, and GT (representing "less than", "equal" and "greater than").
quickSort' :: (a
-> a
-> Ordering)
-> [a]
-> [a]
quickSort' _ [] = []
quickSort' c (x : xs) = (quickSort'
c less) ++ (x : equal) ++ (quickSort' c more)
where
less = filter
(\y-> y
`c` x == LT) xs
equal =
filter
(\y -> y
`c` x == EQ) xs
more =
filter
(\y ->
y `c` x == GT) xs
There is a standard Haskell function compare
which is the usual comparison function. Try out
quickSort' compare
dictionary
Now we can write other comparison functions. For example,
descending x y = compare y x
We can use partial application due to currying to define a new function:
sortDescending :: Ord a => [a]
->
[a]
sortDescending = quickSort' descending
Try it out.
Assignment: Write a comparison function insensitive which compares Strings but is case-insensitive. So > quickSort' insensitive dictionary ["a","for","Haskell","have","I","thing"] You will want to use map and the function toLower. |
Another very useful higher-order function which is
already in the standard libray is zipWith that takes a function and
two lists as parameters and then joins the two lists by applying the
function.
For example,
>
zipWith (+) [4,2,5,6] [2,6,2,3]
[6,8,7,9]
> zipWith max [6,3,2,1]
[7,3,1,5]
[7,3,2,5]
Assignment: I have hidden zipWith inside the Example1 module. Define this function yourself. zipWith :: (a -> b-> c) -> [a] -> [b] -> [c] |
s2 = [x | x<-[0..100], x `mod` 2 == 0]
Assignment: Write a function factors n which computes a list of all the factors of n. Hint: The factors of n are all between 1 and n `div` 2 and of course evenly divide n. factors :: Integer -> [Integer] |