Joseph Haugh
University of New Mexico
What does this code produce?
[ x ^ 2 | x <- [1..5] ]
What does this code produce?
[ x ^ 2 | x <- [1..5] ]
[ 1, 4, 9, 16, 25 ]
What does this code produce?
[ (x, y, z) | x <- [1..3],
y <- [x..3],
z <- [y..3] ]
What does this code produce?
[ (x, y, z) | x <- [1..3],
y <- [x..3],
z <- [y..3] ]
[ (1, 1, 1), (1, 1, 2), (1, 1, 3),
(1, 2, 2), (1, 2, 3), (1, 3, 3),
(2, 2, 2), (2, 2, 3), (2, 3, 3),
(3, 3, 3) ]
What does this code produce?
xs = zip [1..5] [ 1, 4, 9, 16, 25 ]
[ snd x | x <- xs, even (fst x) ]
What does this code produce?
xs = zip [1..5] [ 1, 4, 9, 16, 25 ]
[ snd x | x <- xs, even (fst x) ]
[ 4, 16 ]
So far we have only defined functions in term of other
functions
For example we could define the factorial function as follows:
factorial :: Int -> Int
factorial n = product [1..n]
However, what if we wanted to directly define factorial?
Let’s first recall the mathematical definition of factorial:
0! = 1
n! = n * (n-1) * (n-2) * ... * 1
n! = n * (n-1)!
0! = 1
n! = n * (n-1) * (n-2) * ... * 1
n! = n * (n-1)!
We can relatively easily translate that into Haskell:
factorial :: Int -> Int -- The type is the same
factorial 0 = 1 -- Base case
factorial n = n * factorial (n-1) -- Recursive case
The first step to understanding recursion is to understand it mechanically, by which I mean the steps it takes during evaluation
Let’s see what factorial 3
evaluates to step by step:
factorial :: Int -> Int
factorial 0 = 1
factorial n = n * factorial (n-1)
factorial 3
| {applying factorial}
3 * factorial 2
| {applying factorial}
3 * (2 * factorial 1)
| {applying factorial}
3 * (2 * (1 * factorial 0))
| {applying factorial}
3 * (2 * (1 * 1))
| {applying *}
6
What if we had defined factorial like this?
factorial :: Int -> Int
factorial 0 = 0
factorial n = n * factorial (n-1)
Let’s see what factorial 3
evaluates to with this new definition:
factorial :: Int -> Int
factorial 0 = 0
factorial n = n * factorial (n-1)
factorial 3
| {applying factorial}
3 * factorial 2
| {applying factorial}
3 * (2 * factorial 1)
| {applying factorial}
3 * (2 * (1 * factorial 0))
| {applying factorial}
3 * (2 * (1 * 0))
| {applying *}
0
We get 0 no matter what number we pass in!
Our mistake was using the zero element instead of the identity element of multiplication.
A zero element is an element z such that for any element x:
x * z = z
Whereas an identity element is an element e such that for any element x:
x * e = x
We previously defined factorial
using product
:
factorial n = product [1..n]
But how do we define product
?
product' :: Num a => [a] -> a
product' [] = 1 -- Identity element of *
product' (x:xs) = x * product' xs
Let’s see what product' [2,3,4]
looks like:
product' :: Num a => [a] -> a
product' [] = 1
product' (x:xs) = x * product' xs
product' [2,3,4]
| {applying product'}
2 * product' [3,4]
| {applying product'}
2 * (3 * product' [4])
| {applying product'}
2 * (3 * (4 * product' []))
| {applying product'}
2 * (3 * (4 * 1))
| {applying *}
24
When first starting out with recursion it may help to write out the steps of evaluation of various arguments as we just did
Define the length'
function using recursion.
Also show the steps of evaluation of length' [1,2,3]
.
Reminder: length'
returns the number of elements in a list.
length' :: [a] -> Int
length' :: [a] -> Int
length' [] = 0
length' (_:xs) = 1 + length' xs
length' :: [a] -> Int
length' [] = 0
length' (_:xs) = 1 + length' xs
length' [1,2,3]
| {applying length'}
1 + length' [2,3]
| {applying length'}
1 + (1 + length' [3])
| {applying length'}
1 + (1 + (1 + length' []))
| {applying length'}
1 + (1 + (1 + 0))
| {applying +}
3
The reverse
function takes in a list and reverses it, for example:
> reverse "geralt"
"tlareg"
> reverse "hello"
"olleh"
How do we define reverse
?
When defining a recursive function, it may help to follow these steps:
reverse
takes a [Char] and returns a [Char]reverse' :: [Char] -> [Char]
reverse' [] = ...
reverse' (x:xs) = ...
reverse' [] = []
reverse' (x:xs) = ...
reverse' [] = []
reverse' (x:xs) = reverse' xs ++ [x]
reverse' :: [Char] -> [Char]
reverse' :: [a] -> [a]
> reverse' []
[]
> reverse' [1,2,3]
[3,2,1]
> reverse' "hello"
"olleh"
By following these steps, you can at least have a starting point for defining a recursive function.
Let’s try it again.
take
takes an Int n and a list xs and returns the first n elements of the list.
take' :: Int -> [a] -> [a]
take' 0 [] = ...
take' 0 (x:xs) = ...
take' n [] = ...
take' n (x:xs) = ...
take' 0 [] = []
take' 0 (x:xs) = []
take' n [] = []
take' n (x:xs) = ...
take' 0 [] = []
take' 0 (x:xs) = []
take' n [] = []
take' n (x:xs) = x : take' (n-1) xs
take' :: Int -> [a] -> [a]
take' 0 [] = []
take' 0 (x:xs) = []
take' n [] = []
take' n (x:xs) = x : take' (n-1) xs
take' :: Integral b => b -> [a] -> [a]
take' 0 [] = []
take' 0 (x:xs) = []
take' n [] = []
take' n (x:xs) = x : take' (n-1) xs
take' :: Integral b => b -> [a] -> [a]
take' 0 [] = []
take' 0 (x:xs) = []
take' n [] = []
take' n (x:xs) = x : take' (n-1) xs
take' :: Integral b => b -> [a] -> [a]
take' 0 _ = []
take' n [] = []
take' n (x:xs) = x : take' (n-1) xs
> take' 0 [1,2,3]
[]
> take' 100 []
[]
> take' 3 [1,2,3,4,5]
[1,2,3]
> take' 100 "hello"
"hello"
Define a function iota
which takes in an Int n
and returns a list of the numbers from 1 to n.
iota :: Int -> [Int]
iota :: Int -> [Int]
iota 0 = []
iota n = iota (n-1) ++ [n]
iota :: Int -> [Int]
iota n = [1..n]
iota :: (Eq a, Num a) => a -> [a]
iota 0 = []
iota n = iota (n-1) ++ [n]
Why is this wrong?
iota :: (Eq a, Num a) => a -> [a]
iota 0 = []
iota n = n : iota (n-1)
Let’s try to evaluate iota 3
step by step.
iota :: (Eq a, Num a) => a -> [a]
iota 0 = []
iota n = n : iota (n-1)
iota 3
| {applying iota}
3 : iota 2
| {applying iota}
3 : (2 : iota 1)
| {applying iota}
3 : (2 : (1 : iota 0))
| {applying iota}
3 : (2 : (1 : []))
| {applying :}
[3,2,1]