Joseph Haugh
University of New Mexico
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
.sumdown 3
.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
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
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:
Step 1: Define the type
(***) :: Int -> Int -> Int
Step 2: Enumerate the cases
m *** 0 = ...
m *** 1 = ...
m *** n = ...
Step 3: Define the simple cases
m *** 0 = 0
m *** 1 = m
m *** n = ...
Step 4: Define the other cases
m *** 0 = 0
m *** 1 = m
m *** n = m + (m *** (n - 1))
Step 5: Generalize and simplify
What do you notice that needs to be generalized or simplified?
m
is 0 or 1?Int
s?m
or n
is negative?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?
m *** 0 = 0
0 *** n = 0
m *** 1 = m
1 *** n = n
m *** n = m + (m *** (n - 1))
Does our type signature need to be specific to Int
?
(***) :: Int -> Int -> Int
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
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))))
.
.
.
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))
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
!
(***) :: (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
!
Step 6: Test the function
3 *** 4 === 12
3 *** (-4) === -12
(-3) *** 4 === -12
(-3) *** (-4) === 12
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!
Step 1: Define the type
(^^^) :: Int -> Int -> Int
Step 2: Enumerate the cases
m ^^^ 0 = ...
m ^^^ 1 = ...
0 ^^^ n = ...
1 ^^^ n = ...
m ^^^ n = ...
Step 3: Define the simple cases
m ^^^ 0 = 1
m ^^^ 1 = m
0 ^^^ n = 0
1 ^^^ n = 1
Step 4: Define the other cases
m ^^^ 0 = 1
m ^^^ 1 = m
0 ^^^ n = 0
1 ^^^ n = 1
m ^^^ n = m * (m ^^^ (n - 1))
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
(^^^) :: (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))
Step 6: Test the function
2 ^^^ 3 === 8
3 ^^^ 2 === 9
0 ^^^ 3 === 0
1 ^^^ 3 === 1
9 ^^^ 0 === 1
7 ^^^ 1 === 7
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.
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.
Step 1: Define the type
euclid :: Int -> Int -> Int
Step 2: Enumerate the cases
euclid m n
| m == n = ...
| otherwise = ...
Step 3: Define the simple cases
euclid m n
| m == n = m
| otherwise = ...
Step 4: Define the other cases
euclid n m
| m == n = m
| m > n = euclid (m - n) n
| otherwise = euclid m (n - m)
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)
Step 6: Test the function
euclid 6 27 === 3
euclid 27 6 === 3
euclid 3 3 === 3
euclid 3 4 === 1