Skip to content
Part IA Michaelmas Term

Functional Arrays

Conventional vs. functional arrays

PropertyConventional arrayFunctional array
UpdateIn-place: a.(k) <- xReturns new array: update(A, k, x)
Old versionDestroyedPersists unchanged
StyleImperativePure functional
LookupO(1) (hardware)O(log n)

Functional arrays are a finite map from integers to data, where every update produces a new array rather than mutating the existing one. The challenge is doing this efficiently without copying the entire array.

Braun trees

The representation, credited to W. Braun, stores array elements in a balanced binary tree where the path to element i is determined by the binary representation of i:

Functional array tree

The subscript arithmetic:

if k = 1        → the root
if k mod 2 = 0  → go left, then recurse on k/2
if k mod 2 = 1  → go right, then recurse on k/2

Equivalently: write k in binary, discard the leading 1, reverse the remaining bits, and interpret 0 as “go left” and 1 as “go right”. This scheme guarantees a balanced tree, ensuring O(log n) access for all subscripts.

sub: array lookup

exception Subscript

let rec sub = function
  | Lf, _ -> raise Subscript
  | Br (v, t1, t2), k ->
      if k = 1 then v
      else if k mod 2 = 0 then
        sub (t1, k / 2)
      else
        sub (t2, k / 2)

Divides the subscript by 2 repeatedly until reaching 1. At each step, the parity determines which subtree to follow. If a leaf is reached before subscript 1, the position does not exist and Subscript is raised.

update: functional array update

let rec update = function
  | Lf, k, w ->
      if k = 1 then
        Br (w, Lf, Lf)        (* extend the array *)
      else
        raise Subscript        (* gap in the tree *)
  | Br (v, t1, t2), k, w ->
      if k = 1 then
        Br (w, t1, t2)         (* replace root value *)
      else if k mod 2 = 0 then
        Br (v, update (t1, k / 2, w), t2)
      else
        Br (v, t1, update (t2, k / 2, w))

Two cases for k = 1:

  • At a leaf: extend the array by creating a new branch (the array grows).
  • At a branch: replace the existing value (update in place functionally).

Like BST update, only the path from root to the target index is copied, giving O(log n) time and space. Unchanged subtrees are shared.

Why binary?

The subscript arithmetic works because binary decisions (left/right) reduce the search space by half at each step. This is the fundamental reason binary is important in computer science: an if-then-else leads to binary branching, which turns an O(n) cost into O(log n). Two is the smallest integer divisor that achieves this reduction.