Fibonacci Sequence
A classic example of recursion - calculating Fibonacci numbers:Expected Output:How it works:
recursion.elr
- Base case:
if n <= 1 then nreturns n for 0 or 1 - Recursive case:
fib (n - 1) + fib (n - 2)breaks down the problem - Each recursive call works on a smaller input until reaching the base case
This naive Fibonacci implementation has exponential time complexity O(2^n). For production code, consider using memoization or iteration.
Factorial Function
Computing factorial using recursion:Usage Example:Expected Output:How it works:
- Base case:
if n == 0 then 1- factorial of 0 is 1 - Recursive case:
n * factorial (n - 1)- multiply n by factorial of (n-1) - The computation: 5 * 4 * 3 * 2 * 1 = 120
List Sum with Pattern Matching
Recursively sum a list using pattern matching:Expected Output:How it works:
- Base case:
[] -> 0- empty list sums to 0 - Recursive case:
x::xs -> x + sum xs- add head to sum of tail - Pattern matching destructures the list into head (
x) and tail (xs) - Process: 1 + (2 + (3 + 0)) = 6
Recursive List Processing
Many list operations are naturally recursive. Here’s a complete example:Expected Output:Key Concepts:
- Both functions use the same recursive pattern: base case for
[], recursive case forx::xs lengthcounts elements by adding 1 for each elementreversebuilds a new list with elements in reverse order
Recursion Best Practices
Always Define Base Cases
Every recursive function must have at least one base case that doesn’t recurse, or it will run forever.
Make Progress Toward Base Case
Each recursive call should work with a “smaller” input, getting closer to the base case.
Use Pattern Matching
Pattern matching makes recursive functions on algebraic data types elegant and safe.
Consider Tail Recursion
Tail-recursive functions (where the recursive call is the last operation) can be optimized by the compiler.
Next Steps
- Higher-Order Functions - Combine recursion with functions as values
- Data Types - Use recursion with custom data types