Tonλ's blog May the λ be with you

PIH - ch6 - Recursive functions

by @ardumont on

In this chapter were introduced the recursion, basic mechanism to loop in Haskell.

Exponentiation

Define the exponentiation operator ↑ for non-negative integers using the same pattern of recursion as the multiplication operator , and show how 2 ↑ 3 is evaluated using your definition.

(^) :: Int -> Int -> Int
_ ^ 0 = 1
x ^ n = x * (x ^ (n-1))
2 ^ 3 = 2 * (2 ^ 2)
      = 2 * 2 * (2 ^ 1)
      = 2 * 2 * 2 * (2 ^ 0)
      = 2 * 2 * 2 * 1
      = 8

evaluated

Using the definitions given in this chapter, show how length [1, 2, 3], drop 3 [1, 2, 3, 4, 5], and init [1, 2, 3] are evaluated.

length

Given the definition:

length :: [a] -> Int
length [] = 0
length (_:xs) = 1 + length xs
length [1,2,3] = 1 + length [2,3]
               = 1 + 1 + length [3]
               = 1 + 1 + 1 + length []
               = 1 + 1 + 1 + 0
               = 3

drop

Given the definition:

drop :: Int -> [a] -> [a]
drop 0 ys = ys
drop _ [] = []
drop n (_:ys) = drop (n-1) ys
drop 3 [1,2,3,4,5] = drop 2 [2,3,4,5]
                   = drop 1 [3,4,5]
                   = drop 0 [4,5]
                   = [4,5]

init

Given the definition:

init :: [a] -> [a]
init [_] = []
init (x:xs) = x:(init xs)
init [1,2,3] = 1:(init [2,3])
             = 1:2:(init [3])
             = 1:2:[]
             = [1,2]

functions

Without looking at the definitions from the standard prelude, define the following library functions using recursion: – and – concat – replicate – (!!) – elem

Note: most of these functions are in fact defined in the prelude using other library functions, rather than using explicit recursion.

and

– Decide if all logical values in a list are True: and :: [Bool] → Bool

and :: [Bool] -> Bool
and [] = True
and (x:xs) = x && (and xs)

Example:

*Ch6> and [True, True, True]
True
*Ch6> and [True, False, True]
False

concat

– Concatenate a list of lists: concat :: [[a] ] → [a]

concat :: [[a]] -> [a]
concat [xs] = xs
concat (xs:xxs) = xs ++ (concat xxs)

Example:

*Ch6> concat [[1..10], [2,4], [20..25]]
[1,2,3,4,5,6,7,8,9,10,2,4,20,21,22,23,24,25]

replicate

– Produce a list with n identical elements: replicate :: Int → a → [a]

replicate :: Int -> a -> [a]
replicate 0 _ = []
replicate n x = x:(replicate (n-1) x)

Example:

*Ch6> replicate 10 'a'
"aaaaaaaaaa"
*Ch6> replicate 5 9
[9,9,9,9,9]

(!!)

– Select the nth element of a list: (!!) :: [a] → Int → a

(!!) :: [a] -> Int -> a
(x:_) !! 0 = x
(_:xs) !! n = xs !! (n-1)

Example:

*Ch6> [1,2,3] !! 2
3
*Ch6> [1,2,3] !! 0
1

elem

– Decide if a value is an element of a list: elem :: Eq a ⇒ a → [a] → Bool

elem :: Eq a => a -> [a] -> Bool
elem _ [] = False
elem x (y:ys) | x == y    = True
              | otherwise = elem x ys

Example:

*Ch6> elem 1 [10,20,30]
False
*Ch6> elem 10 [10,20,30]
True
*Ch6> elem 40 [10,20,30,40]
True

merge

Define a recursive function merge :: Ord a ⇒ [a] → [a] → [a] that merges two sorted lists to give a single sorted list.

For example:

> merge [2, 5, 6] [1, 3, 4] [1, 2, 3, 4, 5, 6]

Note: your definition should not use other functions on sorted lists such as insert or isort, but should be defined using explicit recursion.

merge :: Ord a => [a] -> [a] -> [a]
merge [] ys = ys
merge xs [] = xs
merge (x:xs) (y:ys) | x <= y    = x : merge xs     (y:ys)
                    | otherwise = y : merge (x:xs) ys

Example:

*Ch6> merge [2,5,6] [1,3,4]
[1,2,3,4,5,6]
*Ch6> merge [10..20] [1..10]
[1,2,3,4,5,6,7,8,9,10,10,11,12,13,14,15,16,17,18,19,20]

msort

Using merge, define a recursive function msort :: Ord a ⇒ [a] → [a] that implements merge sort, in which the empty list and singleton lists are already sorted, and any other list is sorted by merging together the two lists that result from sorting the two halves of the list separately.

Hint: First define a function halve :: [a] → ([a], [a]) that splits a list into two halves whose lengths differ by at most one.

First, following the hint, we define a halve function:

halve :: [a] -> ([a], [a])
halve xs = splitAt (length xs `div` 2) xs

As an example:

*Ch6> halve [1,2,3]
([1],[2,3])
*Ch6> fst ([1],[2,3])
[1]
*Ch6> snd ([1],[2,3])
[2,3]

Now:

msort :: Ord a => [a] -> [a]
msort [] = []
msort [x] = [x]
msort xs = merge (msort fh) (msort sh)
           where
             (fh, sh) = halve xs

Example:

*Ch6> msort [3,2,90,54,1]
[1,2,3,54,90]

sum

Using the five-step process, define the library functions that calculate the sum of a list of numbers, take a given number of elements from the start of a list, and select the last element of a non-empty list.

sum

Calculate the sum of a list of numbers.

Step 1 - define the types

sum :: Num a => [a] -> a

Step 2 - enumerate the cases

Then what are the cases:

sum []
sum (x:xs)

Step 3 - Define the simple case

Then defining it:

sum [] = 0

0 is the identity of the sum.

Step 4 - Define the other cases

sum (x:xs) = x + sum xs

Step 5 - Generalise and simplify

First, what do we got?

sum :: Num a => [a] -> a
sum [] = 0
sum (x:xs) = x + sum xs

This follows the same recursion pattern that the one for product, which is encapsulated in the foldr function, thus simplifying gives:

sum :: Num a => [a] -> a
sum = foldr (+) 0

Example:

*Ch6> summ [3,2,90,54,1]
150

take

take a given number of elements.

Step 1 - define the types

take :: Int -> [a] -> [a]

Step 2 - enumerate the cases

Then what are the cases:

  • we take no elements in any list
  • or we take some elements in an empty list.
take 0 _ =
take _ [] =

Step 3 - Define the simple case

Either way, we return an empty list.

take 0 _ = []
take _ [] = []

Step 4 - Define the other cases

We take the head of the list, it becomes the head of a new list. The tail of the list is made by taking n-1 elements in xs.

take n (x:xs) = x : (take (n-1) xs)

Step 5 - Generalise and simplify

First, what do we got?

take :: Int -> [a] -> [a]
take 0 _ = []
take _ [] = []
take n (x:xs) = x : (take (n-1) xs)

Example:

*Ch6> take 0 [1..10]
[]
*Ch6> take 10 []
[]
*Ch6> take 3 [1..10]
[1,2,3]

last

Select the last element of a non empty list.

Step 1 - define the types

last :: [a] -> a

Step 2 - enumerate the cases

Then what are the cases:

last []
last [x]

Step 3 - Define the simple case

Then defining it:

last [] = []
last [x] = x

Step 4 - Define the other cases

last (_:xs) = last xs

Step 5 - Generalise and simplify

First, what do we got?

last :: [a] -> a
last [x] = x
last (x:xs) = last xs

Example:

*Ch6> last [1..10]
10

Latest posts