Lecture 09 More Recursive Functions

Joseph Haugh

University of New Mexico

Review

  • What is a recursive function?
  • What is the first of the six steps for defining a recursive function?
  • Define a function, sumdown, that takes a positive integer n and returns the sum from n to 0. For examples sumdown 3 should return 6 = 3 + 2 + 1 + 0.
  • Show the step by step evaluation of sumdown 3.

Review

sumdown :: Int -> Int
sumdown 0 = 0
sumdown n = n + sumdown (n - 1)

sumdown 3
| { applying sumdown }
3 + sumdown 2
| { applying sumdown }
3 + (2 + sumdown 1)
| { applying sumdown }
3 + (2 + (1 + sumdown 0))
| { applying sumdown }
3 + (2 + (1 + 0))
| { applying + }
6

Defining Multiplication Recursively

How can we define the (*) operator recursively?

Well lets think about how it works:

4 * 3 = 4 + 4 + 4
3 * 4 = 3 + 3 + 3 + 3

4 * 3 = 4 + (4 * 2)
      = 4 + 4 + (4 * 1)
      = 4 + 4 + 4

3 * 4 = 3 + (3 * 3)
      = 3 + 3 + (3 * 2)
      = 3 + 3 + 3 + (3 * 1)
      = 3 + 3 + 3 + 3

Defining Multiplication Recursively

Multiplication is repeated addition. For a product, m * n, first check if n is 0 in this case the result is 0, or if n is 1 then the result is m. Otherwise we add m to the result of m * (n - 1).

Let’s try to define this in Haskell using the 6 steps:

Defining Multiplication Recursively

Step 1: Define the type

(***) :: Int -> Int -> Int

Defining Multiplication Recursively

Step 2: Enumerate the cases

m *** 0 = ...
m *** 1 = ...
m *** n = ...

Defining Multiplication Recursively

Step 3: Define the simple cases

m *** 0 = 0
m *** 1 = m
m *** n = ...

Defining Multiplication Recursively

Step 4: Define the other cases

m *** 0 = 0
m *** 1 = m
m *** n = m + (m *** (n - 1))

Defining Multiplication Recursively

Step 5: Generalize and simplify

What do you notice that needs to be generalized or simplified?

  • What if m is 0 or 1?
  • Do we have to limit ourselves to Ints?
  • What if m or n is negative?

Defining Multiplication Recursively

Let’s step through the evaluation of 0 *** 3

m *** 0 = 0
m *** 1 = m
m *** n = m + (m *** (n - 1))
0 *** 3
| { applying *** }
0 + (0 *** 2)
| { applying *** }
0 + (0 + (0 *** 1))
| { applying *** }
0 + (0 + 0)
| { applying + }
0

We get the right answer but it wastes time adding 0s. Can we do better?

Defining Multiplication Recursively

m *** 0 = 0
0 *** n = 0
m *** 1 = m
1 *** n = n
m *** n = m + (m *** (n - 1))

Defining Multiplication Recursively

Does our type signature need to be specific to Int?

(***) :: Int -> Int -> Int

Defining Multiplication Recursively

No! All we need is the ability to add, subtract and check equality. So we can use Num and Eq:

(***) :: (Num a, Eq a) => a -> a -> a

Defining Multiplication Recursively

What about negative numbers?

Let’s step through the evaluation of 3 *** (-2)

m *** 0 = 0
0 *** n = 0
m *** 1 = m
1 *** n = n
m *** n = m + (m *** (n - 1))
3 *** (-2)
| { applying *** }
3 + (3 *** (-3))
| { applying *** }
3 + (3 + (3 *** (-4)))
| { applying *** }
3 + (3 + (3 + (3 *** (-5))))
.
.
.

Defining Multiplication Recursively

How can we fix this? We can use the sign of m and n to determine the sign of the result using guards and a helper function to calculate the multiplication of positive numbers.

First let’s define a helper function, mulPos, that does the same calculation as (***), which assumes positive input:

mulPos :: (Num a, Eq a) => a -> a -> a
mulPos m 0 = 0
mulPos 0 n = 0
mulPos m 1 = m
mulPos 1 n = n
mulPos m n = m + (mulPos m (n - 1))

Defining Multiplication Recursively

Next we can redefine, (***), using mulPos and guards to handle negative numbers:

(***) :: Num a => a -> a -> a
m *** n
  | m < 0 && n < 0 = mulPos (-m) (-n)
  | m < 0 = - (mulPos (-m) n)
  | n < 0 = - (mulPos m (-n))
  | otherwise = mulPos m n

Does this work?

We forgot Ord!

Defining Multiplication Recursively

(***) :: (Num a, Ord a) => a -> a -> a
m *** n
  | m < 0 && n < 0 = mulPos (-m) (-n)
  | m < 0 = - (mulPos (-m) n)
  | n < 0 = - (mulPos m (-n))
  | otherwise = mulPos m n

Why don’t we have to put Eq a?

Because Ord implies Eq!

Defining Multiplication Recursively

Step 6: Test the function

3    *** 4    === 12
3    *** (-4) === -12
(-3) *** 4    === -12
(-3) *** (-4) === 12

Exercise: Exponentiation

Multiplication is repeated addition. What about exponentiation?

It is repeated multiplication! For example:

2 ^ 3 = 2 * 2 * 2
3 ^ 2 = 3 * 3

Define a recursive function, (^^^) :: Int -> Int -> Int, that takes two positive integers m and n and returns n raised to the power of m. Reminder: Use the 6 steps!

Exercise: Exponentiation

Step 1: Define the type

(^^^) :: Int -> Int -> Int

Exercise: Exponentiation

Step 2: Enumerate the cases

m ^^^ 0 = ...
m ^^^ 1 = ...
0 ^^^ n = ...
1 ^^^ n = ...
m ^^^ n = ...

Exercise: Exponentiation

Step 3: Define the simple cases

m ^^^ 0 = 1
m ^^^ 1 = m
0 ^^^ n = 0
1 ^^^ n = 1

Exercise: Exponentiation

Step 4: Define the other cases

m ^^^ 0 = 1
m ^^^ 1 = m
0 ^^^ n = 0
1 ^^^ n = 1
m ^^^ n = m * (m ^^^ (n - 1))

Exercise: Exponentiation

Step 5: Generalize and simplify

What do you notice that needs to be generalized or simplified?

Can the type be generalized?

What is the type of (^)?

(^) :: (Num a, Integral b) => a -> b -> a

Exercise: Exponentiation

(^^^) :: (Num a, Integral b, Eq a, Eq b) => a -> b -> a
m ^^^ 0 = 1
m ^^^ 1 = m
0 ^^^ n = 0
1 ^^^ n = 1
m ^^^ n = m * (m ^^^ (n - 1))

Exercise: Exponentiation

Step 6: Test the function

2 ^^^ 3 === 8
3 ^^^ 2 === 9
0 ^^^ 3 === 0
1 ^^^ 3 === 1
9 ^^^ 0 === 1
7 ^^^ 1 === 7

A Question

Could you fully generalize this pattern?

Could you define a function which does repeated (^^^) then repeats the function you just defined and so on?

We will attempt this later in the semester.

Exercise: Euclid’s Algorithm

Define a recursive function, euclid :: Int -> Int -> Int, that takes two positive integers m and n and returns their greatest common divisor.

The algorithm is as follows:

If m is equal to n then the result is m. Otherwise the smaller number is subtracted from the larger number and the same process is then repeated.

Exercise: Euclid’s Algorithm

Step 1: Define the type

euclid :: Int -> Int -> Int

Exercise: Euclid’s Algorithm

Step 2: Enumerate the cases

euclid m n
  | m == n    = ...
  | otherwise = ...

Exercise: Euclid’s Algorithm

Step 3: Define the simple cases

euclid m n
  | m == n    = m
  | otherwise = ...

Exercise: Euclid’s Algorithm

Step 4: Define the other cases

euclid n m
  | m == n    = m
  | m > n     = euclid (m - n) n
  | otherwise = euclid m (n - m)

Exercise: Euclid’s Algorithm

Step 5: Generalize and simplify

euclid :: (Integral a, Ord a) => a -> a -> a
euclid n m
  | m == n    = m
  | m > n     = euclid (m - n) n
  | otherwise = euclid m (n - m)

Exercise: Euclid’s Algorithm

Step 6: Test the function

euclid 6 27 === 3
euclid 27 6 === 3
euclid 3 3  === 3
euclid 3 4  === 1