04 Recursion
More information about recursion can be found in 04 Recursion
What is Recursion?
Recursion is the process of repeating items in a self-similar way. Matryoshka dolls are a good example of recursion.
In Haskell, the most important data structure is defined in a recursive way. A list is either empty or it is a value followed by a list.
data [a] = [] | a : [a]
This is also why lists in Haskell can be infinite! Finite lists always require an empty list ([]) at the end.
The same principle can be applied to functions. Recursion is, if a function is defined in terms of itself.
factorial :: Integer -> Integer
factorial 0 = 1
factorial n = n * factorial (n - 1)
Progress and Controlling
A recursive function must make progress towards a base case. If the base case is never reached, the function will never terminate.
countFromTo :: Int -> Int -> [Int]
countFromTo from to | from < to = from : (countFromTo (from+1) to) -- count upwards towards to
| from > to = from : (countFromTo (from-1) to) -- count downwards towards to
| otherwise = [to] -- base case
If a function requires some preparation before the recursive call, it is common to define a helper function.
gcd :: (Integral a) => a -> a -> a
gcd 0 0 = error "gcd 0 0 is undefined" gcd x y = gcd' (abs x) (abs y)
where gcd' a 0 = a
gcd' a b = gcd' b (a `rem` b)
Mutual Recursion
Mutual recursion is a special case of recursion, where two or more functions are defined in terms of each other.
even :: Int -> Bool
even 0 = True
even n = odd (n-1)
odd :: Int -> Bool
odd 0 = False
odd n = even (n-1)
Tail Recursion
A function is tail recursive, if the recursive call is the last thing the function does (outermost call).
See the difference between the two implementations of sum below.
sum :: Num a => [a] -> a -- not tail recursive sum [] = 0
sum (i:is) = i + sum is -- addition is the last thing the function does
sum :: Num a => [a] -> a -- tail recursive sum l = sum' 0 l
where sum' acc [] = acc
sum' acc (i:is) = sum' (i+acc) is -- tail call
Loops vs Recursion
Every loop can be expressed as a recursive function and vice versa.
- Loops are controlled by a variable that changes with each iteration
- The loops condition is the negation of the base case
- The loops body is the recursion call
int sum(int n) {
int sum = 0;
while(n > 0) {
sum += n;
n--;
}
return sum;
}
sum :: Int -> Int
sum 0 = 0
sum n = n + sum (n-1)