RECURSIVE FUNCTIONS

BASIC CONCEPTS

In [1]:
fac1 :: Int -> Int
fac1 n = product [1..n]
In [2]:
fac1 5
120
In [3]:
fac :: Int -> Int
fac 0 = 1
fac n = n * fac (n-1)
In [4]:
fac 5
120
  fac 5
= 5 * fac 4
= 5 * 4 * fac 3
= 5 * 4 * 3 * fac 2
= 5 * 4 * 3 * 2 * fac 1
= 5 * 4 * 3 * 2 * 1 * fac 0
= 5 * 4 * 3 * 2 * 1 * 1
= 120
In [5]:
-- multiplication??
(#) :: Int -> Int -> Int
m # 0 = 0
m # n = m + (m # (n - 1))
In [6]:
4 # 3
(#) 4 3
4 * 3
(*) 4 3
12
12
12
12
  4 # 3
= 4 + (4 # 2)
= 4 + (4 + (4 # 1))
= 4 + (4 + (4 + (4 # 0)))
= 4 + (4 + (4 + 0))
= 12

RECURSION ON LISTS

In [81]:
product1 :: Num a => [a] -> a
product1 []     = 1
product1 (n:ns) = n * product1 ns
Use foldr
Found:
product1 [] = 1 product1 (n : ns) = n * product1 ns
Why Not:
product1 ns = foldr (*) 1 ns
In [82]:
product1 [2,4,6]
48
In [83]:
length1 :: [a] -> Int
length1 []     = 0
length1 (_:xs) = 1 + length1 xs
In [84]:
length1 [1,2,3]
3
In [85]:
reverse1 :: [a] -> [a]
reverse1 []     = []
reverse1 (x:xs) = reverse xs ++ [x]
In [86]:
reverse1 [1,2,3]
[3,2,1]
  reverse1 [1,2,3]
= reverse1 [2,3] ++ [1]
= (reverse1 [3] ++ [2]) ++ [1]
= ((reverse1 [] ++ [3]) ++ [2]) ++ [1]
= (([] ++ [3]) ++ [2]) ++ [1]
= ([3] ++ [2]) ++ [1]
= [3,2] ++ [1]
= [3,2,1]
In [7]:
-- Concatenation??
(##) :: [a] -> [a] -> [a]
[]     ## ys = ys
(x:xs) ## ys = x : (xs ## ys)
In [8]:
[1,2,3] ## [4,5,6]
[1,2,3] ++ [4,5,6]
[1,2,3,4,5,6]
[1,2,3,4,5,6]
  [1,2,3] ## [4,5,6]
= 1 : ([2,3] ## [4,5,6])
= 1 : (2 : ([3] ## [4,5,6]))
= 1 : (2 : (3 : ([] ## [4,5,6])))
= 1 : (2 : (3 : [4,5,6]))
= [1,2,3,4,5,6]

insertion sort

In [9]:
insert :: Ord a => a -> [a] -> [a]
insert x []   = [x]
insert x (y:ys)
  | x <= y    = x : y : ys
  | otherwise = y : insert x ys
In [10]:
insert 3 [1,2,4,5]
[1,2,3,4,5]
  insert 3 [1,2,4,5]
= 1 : insert 3 [2,4,5]
= 1 : 2 : insert 3 [4,5]
= 1 : 2 : [3,4,5]
= [1,2,3,4,5]
In [11]:
isort :: Ord a => [a] -> [a]
isort []       = []
isort (x : xs) = insert x (isort xs)
Use foldr
Found:
isort [] = [] isort (x : xs) = insert x (isort xs)
Why Not:
isort xs = foldr insert [] xs
In [12]:
isort [4,3,6,5,9]
isort ["john","alice","jim"]
[3,4,5,6,9]
["alice","jim","john"]

MULTIPLE ARGUMENTS

In [13]:
zip1 :: [a] -> [b] -> [(a,b)]
zip1 []     _      = []
zip1 _      []     = []
zip1 (x:xs) (y:ys) = (x,y) : zip1 xs ys
In [14]:
zip1 ['a','b','c'] [1,2,3,4]
[('a',1),('b',2),('c',3)]
  zip1 ['a','b','c'] [1,2,3,4]
= ('a',1) : zip1 ['b','c'] [2,3,4]
= ('a',1) : ('b',2) : zip1 ['c'] [3,4]
= ('a',1) : ('b',2) : ('c',3) : zip1 [] [4]
= ('a',1) : ('b',2) : ('c',3) : []
= [('a',1),('b',2),('c',3)]
In [15]:
drop1 :: Int -> [a] -> [a]
drop1 0 xs     = xs
drop1 _ []     = []
drop1 n (_:xs) = drop1 (n-1) xs
In [16]:
drop1 3 [1,2,3,4,5,6]
[4,5,6]

MULTIPLE RECURSION

In [17]:
fib :: Int -> Int
fib 0 = 0
fib 1 = 1
fib n = fib (n-2) + fib (n-1)
In [18]:
fib 4
3
In [19]:
qsort []     = []
qsort (x:xs) = qsort ys ++ [x] ++ qsort zs
           where
              ys = [a | a <- xs, a <= x]
              zs = [b | b <- xs, b > x]
In [20]:
qsort [4,3,6,5,9]
[3,4,5,6,9]

MUTUAL RECURSION

In [21]:
even :: Int -> Bool
even 0 = True
even n = odd (n-1)

odd :: Int -> Bool
odd 0 = False
odd n = even (n-1)
In [22]:
even 8
even 13
odd 7
odd 20
True
False
True
False
  even 4
= odd 3
= even 2
= odd 1
= even 0
= True
In [24]:
evens :: [a] -> [a]
evens []     = []
evens (x:xs) = x : odds xs

odds :: [a] -> [a]
odds []     = []
odds (_:xs) = evens xs
In [26]:
evens "abcde"
evens [0,1,2,3,4,5,6,7,8,9]
"ace"
[0,2,4,6,8]
  evens "abcde"
= 'a' : odds "bcde"
= 'a' : evens "cde"
= 'a' : 'c' : odds "de"
= 'a' : 'c' : evens "e"
= 'a' : 'c' : 'e' : odds ""
= 'a' : 'c' : 'c' : []
= "ace"

ADVICE ON RECURSION

Example 1: product of numbers in a list

STEP 1: Define the type

product :: [Int] -> Int

STEP 2: Enumerate the cases

product []     = 
product (n:ns) =

STEP 3: Define the simple cases (base case)

product [] = 1

STEP 4: Define the other cases (recursive cases)

product (n:ns) = n * product ns

STEP 5: Generalize and simplify

product :: Num a => [a] -> a
product = foldr (*) 1

Example 2: drop n elements from front of a list

STEP 1: Define the type

drop :: Int -> [a] -> a

STEP 2: Enumerate the cases

drop 0 []     = 
drop 0 (x:xs) =
drop n []     = 
drop n (x:xs) =

STEP 3: Define the simple cases (base case)

drop 0 []     = []
drop 0 (x:xs) = x:xs
drop n []     = []
drop n (x:xs) =

STEP 4: Define the other cases (recursive cases)

drop 0 []     = []
drop 0 (x:xs) = x:xs
drop n []     = []
drop n (x:xs) = drop (n-1) xs

STEP 5: Generalize and simplify

drop :: Integral b => b -> [a] -> a
drop 0 xs     = xs
drop n []     = []
drop n (x:xs) = drop (n-1) xs

We can replace n by in 2nd equation and x by in 3rd equation

drop :: Integral b => b -> [a] -> a
drop 0 xs     = xs
drop _ []     = []
drop n (_:xs) = drop (n-1) xs

Example 3: init list returns all but last item in a non-empty list

STEP 1: Define the type

init :: [a] -> [a]

STEP 2: Enumerate the cases

init (x:xs) =

STEP 3: Define the simple cases (base case is 1-element list)

init (x:xs) 
  | null xs   = []    -- 1-element case
  | otherwise =

STEP 4: Define the other cases (recursive cases)

init (x:xs) 
  | null xs   = []
  | otherwise = x : init xs

STEP 5: Generalize and simplify (nothing to generalize here; but can be simplified)

init :: [a] -> [a]
init [_]    = []
init (x:xs) = x : init xs