Algorithm Search⌘K

Proofs: Zipper

These are the human proofs of Zipper, at full length.

Zipper moves keep the represented tree, and replace changes only the focus full

This proof shows: correctness.

Setting. Trees are nil or nodes (l,x,r)(l, x, r). A path is nil or c::pc :: p, where cc is a crumb (left,x,s)(\text{left}, x, s) or (right,x,s)(\text{right}, x, s). Define

plug(t,nil)=t,plug(t,(left,x,s)::p)=plug((t,x,s),p),plug(t,(right,x,s)::p)=plug((s,x,t),p).\text{plug}(t, \text{nil}) = t, \quad \text{plug}(t, (\text{left}, x, s) :: p) = \text{plug}((t, x, s), p), \quad \text{plug}(t, (\text{right}, x, s) :: p) = \text{plug}((s, x, t), p).

A position in a tree TT is a sequence of directions from the root. Define the position of a path by pos(nil)=⟨⟩\text{pos}(\text{nil}) = \langle\rangle and pos(c::p)=pos(p)⋅⟨d⟩\text{pos}(c :: p) = \text{pos}(p) \cdot \langle d \rangle, where dd is the direction of cc. The zipper (t,p)(t, p) represents the tree plug(t,p)\text{plug}(t, p) with the position pos(p)\text{pos}(p).

Lemma 1. For every path pp and trees t,t′t, t', the tree plug(t′,p)\text{plug}(t', p) equals plug(t,p)\text{plug}(t, p) with the subtree at pos(p)\text{pos}(p) replaced by t′t', and the subtree of plug(t,p)\text{plug}(t, p) at pos(p)\text{pos}(p) is tt.

Proof. By induction on the length of pp. For p=nilp = \text{nil} the position is the root, and both claims are clear. For p=(left,x,s)::p′p = (\text{left}, x, s) :: p', the tree plug(t,p)\text{plug}(t, p) is plug((t,x,s),p′)\text{plug}((t, x, s), p'). By induction its subtree at pos(p′)\text{pos}(p') is (t,x,s)(t, x, s), so its subtree at pos(p′)⋅⟨left⟩=pos(p)\text{pos}(p') \cdot \langle \text{left} \rangle = \text{pos}(p) is tt. Also by induction, plug((t′,x,s),p′)\text{plug}((t', x, s), p') is plug((t,x,s),p′)\text{plug}((t, x, s), p') with the subtree at pos(p′)\text{pos}(p') replaced by (t′,x,s)(t', x, s), which is the same as replacing only the left child of that subtree, at pos(p)\text{pos}(p), by t′t'. The right case is symmetric. □\square

Lemma 2 (moves). If (t,p)(t, p) represents (T,π)(T, \pi), then LEFT, RIGHT and UP, when they succeed, return zippers that represent TT with the position π⋅⟨left⟩\pi \cdot \langle\text{left}\rangle, π⋅⟨right⟩\pi \cdot \langle\text{right}\rangle and π\pi without its last direction.

Proof. LEFT applies to t=(l,x,r)t = (l, x, r) (lines L2 and L3) and returns (l,(left,x,r)::p)(l, (\text{left}, x, r) :: p) (line L4). By definition plug(l,(left,x,r)::p)=plug((l,x,r),p)=T\text{plug}(l, (\text{left}, x, r) :: p) = \text{plug}((l, x, r), p) = T, and the position is pos(p)⋅⟨left⟩\text{pos}(p) \cdot \langle\text{left}\rangle. RIGHT is the same with (right,x,l)(\text{right}, x, l) and plug(r,(right,x,l)::p)=plug((l,x,r),p)\text{plug}(r, (\text{right}, x, l) :: p) = \text{plug}((l, x, r), p) (lines L5 and L6). UP applies to p=(d,x,s)::p′p = (d, x, s) :: p' (lines L7 and L8). If d=leftd = \text{left} it returns ((t,x,s),p′)((t, x, s), p') (line L9), and plug((t,x,s),p′)=plug(t,p)=T\text{plug}((t, x, s), p') = \text{plug}(t, p) = T; if d=rightd = \text{right} it returns ((s,x,t),p′)((s, x, t), p') (line L10), and the same holds. The position is pos(p′)\text{pos}(p'), which is π\pi without its last direction. □\square

Lemma 3 (replace and top). If (t,p)(t, p) represents (T,π)(T, \pi), then REPLACE((t,p),t′)((t, p), t') represents TT with the subtree at π\pi replaced by t′t', at the same position. TOP returns a zipper with an empty path whose focus is TT.

Proof. Line L11 returns (t′,p)(t', p), and Lemma 1 gives the first claim. TOP applies UP while the path is not nil (line L12). Each UP shortens the path by one and keeps the tree by Lemma 2, so the loop ends after ∣p∣|p| steps with (t′′,nil)(t'', \text{nil}), and t′′=plug(t′′,nil)=Tt'' = \text{plug}(t'', \text{nil}) = T. □\square

Theorem. Every sequence of operations from from_tree(T_0) gives zippers whose represented trees and positions are those of the abstract type, to_tree returns the represented tree, and no operation changes what an existing zipper represents.

Proof. from_tree(T_0) returns (T0,nil)(T_0, \text{nil}), which represents T0T_0 at the root. By Lemmas 2 and 3 each operation maps a zipper for (T,π)(T, \pi) to a zipper for the correct result, so by induction on the number of operations every zipper is correct, and to_tree returns the focus of TOP, which is the represented tree. No line writes to an existing node, crumb or path; each builds new cells that point to old ones. The meaning plug(t,p)\text{plug}(t, p) of a zipper depends only on the cells reachable from it, which never change, so every earlier zipper keeps its meaning. ■\blacksquare

Back to Zipper