R Recursive Functions

A recursive function calls itself during execution. It solves a complex problem by breaking it down into a smaller version of the same problem, calling itself with that smaller input, and stopping when it reaches a base case — a simple condition that produces an answer without further recursion.

The Recipe for Any Recursive Function

recursive_fn <- function(input) {
  # Step 1: Base case (stop condition)
  if (input reaches simplest form) {
    return(simple answer)
  }

  # Step 2: Recursive call (smaller problem)
  return(recursive_fn(smaller input))
}

Example 1: Factorial

factorial_r <- function(n) {
  if (n == 0 || n == 1) return(1)   # base case
  return(n * factorial_r(n - 1))    # recursive call
}

factorial_r(5)   # 120
Call stack for factorial_r(5):
  factorial_r(5) = 5 × factorial_r(4)
                       = 4 × factorial_r(3)
                             = 3 × factorial_r(2)
                                   = 2 × factorial_r(1)
                                               = 1  ← base case
  Unwinding: 2×1=2 → 3×2=6 → 4×6=24 → 5×24=120

Example 2: Fibonacci Sequence

fibonacci <- function(n) {
  if (n == 1) return(0)   # F(1) = 0
  if (n == 2) return(1)   # F(2) = 1
  return(fibonacci(n - 1) + fibonacci(n - 2))
}

fibonacci(7)   # 8  (sequence: 0,1,1,2,3,5,8,13,...)
sapply(1:8, fibonacci)   # 0 1 1 2 3 5 8 13

Example 3: Sum of a Vector

my_sum <- function(v) {
  if (length(v) == 0) return(0)           # base case
  return(v[1] + my_sum(v[-1]))            # first element + rest
}

my_sum(c(10, 20, 30, 40))   # 100

Example 4: Flatten a Nested List

flatten <- function(lst) {
  result <- c()
  for (item in lst) {
    if (is.list(item)) {
      result <- c(result, flatten(item))  # recurse into sub-list
    } else {
      result <- c(result, item)
    }
  }
  return(result)
}

nested <- list(1, list(2, 3), list(4, list(5, 6)))
flatten(nested)   # 1 2 3 4 5 6

Recursion vs Iteration

          Recursion              Iteration (Loop)
──────────────────────────────────────────────────────
Elegant for tree-like data     Faster for simple repetition
Can run deep (stack limit)     No stack depth limit in R
Can be slower (overhead)       Generally more efficient in R
Mirrors mathematical def.      Straightforward to debug

Stack Overflow Risk

R has a recursion depth limit (around 700-1000 calls by default). Calling a recursive function with a very large input exceeds this limit.

# This will error if n is very large
factorial_r(10000)   # Error: C stack usage is too close to the limit

For large inputs, convert to a loop. For moderate inputs, recursion is fine and often cleaner.

Memoization: Speed Up Recursive Functions

library(memoise)

fib_memo <- memoise(function(n) {
  if (n <= 1) return(n)
  fib_memo(n-1) + fib_memo(n-2)
})

fib_memo(30)   # fast — previously computed values are cached

Use recursion when the problem naturally decomposes into smaller identical subproblems — tree traversal, nested data flattening, mathematical sequences, divide-and-conquer algorithms. For flat, repetitive tasks on large data, prefer loops or vectorized operations.

Leave a Comment

Your email address will not be published. Required fields are marked *