Recursion

There are no for/while loops — use recursion, Enum, or comprehensions.

There are no for/while loops — use recursion, Enum, or comprehensions.

10.1 Basic recursion

defmodule Rec do
  def sum([]), do: 0
  def sum([h | t]), do: h + sum(t)

  def factorial(0), do: 1
  def factorial(n) when n > 0, do: n * factorial(n - 1)

  def print_n(0), do: :ok
  def print_n(n) do
    IO.puts(n)
    print_n(n - 1)
  end
end

10.2 Tail-call optimisation with accumulators

A call in tail position doesn't grow the stack. (On the BEAM, body recursion is also quite efficient; prefer clarity, but use tail recursion for infinite loops like process loops.)

defmodule Tail do
  def sum(list), do: do_sum(list, 0)
  defp do_sum([], acc), do: acc
  defp do_sum([h | t], acc), do: do_sum(t, acc + h)

  def reverse(list), do: do_reverse(list, [])
  defp do_reverse([], acc), do: acc
  defp do_reverse([h | t], acc), do: do_reverse(t, [h | acc])
end

10.3 Map & reduce by hand

defmodule MyList do
  def map([], _f), do: []
  def map([h | t], f), do: [f.(h) | map(t, f)]

  def reduce([], acc, _f), do: acc
  def reduce([h | t], acc, f), do: reduce(t, f.(h, acc), f)

  def filter([], _p), do: []
  def filter([h | t], p), do: if(p.(h), do: [h | filter(t, p)], else: filter(t, p))
end

🧪 Part I Exercises

Write Math.power(x, n) using tail recursion.

Write MyList.length/1, MyList.max/1, and MyList.flatten/1.

Implement FizzBuzz using case on {rem(n, 3), rem(n, 5)}.

Write fib/1 both naively and with an accumulator.

Implement range(a, b) returning a list, without using Range.

Write a function that removes consecutive duplicates: [1,1,2,3,3,1] -> [1,2,3,1].

Write Greeter.greet/2 with a default :en language and clauses for :es, :fr.

Write a with pipeline that parses two strings to integers and divides them, returning {:error, reason} on any failure.

Write a custom guard is_positive_integer/1 and use it.

Implement palindrome?/1 for strings, ignoring case and spaces.