Algorithm Search⌘K

Proofs: Persistent stack

These are the human proofs of Persistent stack, at full length.

The persistent stack meets the stack laws and never changes an old version full

This proof shows: correctness.

Write seq(s)\text{seq}(s) for the sequence of items of the stack ss, from the top down: seq(nil)=⟨⟩\text{seq}(\text{nil}) = \langle \rangle, and seq(s)=⟨s.item⟩⋅seq(s.next)\text{seq}(s) = \langle s.item \rangle \cdot \text{seq}(s.next) for a node ss. Write ∣s∣|s| for the length of seq(s)\text{seq}(s).

Lemma 1 (the invariant holds). At each moment, every node satisfies (I1) its fields have not changed since it was made, (I2) s.size=1+∣s.next∣s.size = 1 + |s.next| where ∣nil∣=0|\text{nil}| = 0, and (I3) the chain s,s.next,s.next.next,…s, s.next, s.next.next, \ldots reaches nil.

Proof. By induction on the number of operations done. Before the first operation there is no node. POP, TOP, IS-EMPTY and SIZE write no field and make no node, so they keep all three statements. PUSH(s,x)(s, x) at line L1 makes one node rr with r.item=xr.item = x, r.next=sr.next = s and r.size=1+SIZE(s)r.size = 1 + \text{SIZE}(s), and writes no other field, so (I1) holds for the old nodes. The chain from rr is rr followed by the chain from ss, which reaches nil by the hypothesis, so (I3) holds for rr. SIZE(s)(s) returns s.sizes.size, which is ∣s∣|s| by (I2) and (I3) for ss (induction along the finite chain), or 0 for nil. So r.size=1+∣s∣=1+∣r.next∣r.size = 1 + |s| = 1 + |r.next|, which is (I2) for rr. □\square

By (I3), seq(s)\text{seq}(s) is a finite sequence for each stack ss, and by (I1), seq(s)\text{seq}(s) depends only on fields that never change.

Lemma 2 (old versions are unchanged). If a stack value vv exists at some moment, then seq(v)\text{seq}(v) is the same at every later moment.

Proof. seq(v)\text{seq}(v) is determined by the fields itemitem and nextnext of the nodes on the chain from vv, and by Lemma 1 no field of an existing node changes. A node made later is not on this chain, because the chain consists of nodes that existed when vv existed, and nextnext fields do not change. □\square

Theorem (the stack laws). For each stack ss and item xx:

  1. PUSH(s,x)(s, x) returns a stack rr with seq(r)=⟨x⟩⋅seq(s)\text{seq}(r) = \langle x \rangle \cdot \text{seq}(s), and seq(s)\text{seq}(s) is unchanged.
  2. If s≠nils \ne \text{nil} and seq(s)=⟨y⟩⋅w\text{seq}(s) = \langle y \rangle \cdot w, then POP(s)(s) returns (y,t)(y, t) with seq(t)=w\text{seq}(t) = w, and TOP(s)(s) returns yy.
  3. If s=nils = \text{nil}, POP(s)(s) and TOP(s)(s) report underflow.
  4. IS-EMPTY(s)(s) is true exactly when seq(s)=⟨⟩\text{seq}(s) = \langle \rangle, and SIZE(s)=∣s∣(s) = |s|.

Proof. (1) Line L1 makes rr with r.item=xr.item = x and r.next=sr.next = s, so seq(r)=⟨x⟩⋅seq(s)\text{seq}(r) = \langle x \rangle \cdot \text{seq}(s) by the definition of seq. Lemma 2 gives that seq(s)\text{seq}(s) is unchanged. (2) If s≠nils \ne \text{nil}, line L2 does not stop, and line L3 returns s.item=ys.item = y and t=s.nextt = s.next, with seq(t)=w\text{seq}(t) = w by the definition of seq. Lines L4 and L5 return s.item=ys.item = y in the same way. (3) Lines L2 and L4 test exactly s=nils = \text{nil}. (4) seq(s)=⟨⟩\text{seq}(s) = \langle \rangle exactly when s=nils = \text{nil}, because a node contributes its item. SIZE returns ∣s∣|s| by (I2) and (I3), as in the proof of Lemma 1. □\square

Corollary (full persistence). Let v0,v1,…,vmv_0, v_1, \ldots, v_m be the versions made by any sequence of operations, where each update takes any earlier version as input. Then at the end, seq(vi)\text{seq}(v_i) is the sequence that the stack laws give for the chain of updates that made viv_i from the empty stack.

Proof. By induction on ii. The version v0v_0 is nil. The version viv_i was made by PUSH or POP from some vjv_j with j<ij < i. At that moment, seq(vj)\text{seq}(v_j) was, by the hypothesis, the right sequence, so by the theorem seq(vi)\text{seq}(v_i) was the right sequence for viv_i. By Lemma 2 it is still the same at the end. ■\blacksquare

Append. Lines A1 to A4 push the items of ss onto tt from the bottom item of ss up to the top item. By part 1 of the theorem and induction on the number of pushes, after the pushes of the bottom jj items of ss the sequence of rr is those jj items, in the order they have in ss, followed by seq(t)\text{seq}(t). With j=kj = k this is seq(s)⋅seq(t)\text{seq}(s) \cdot \text{seq}(t). Each push makes one node, so APPEND makes kk nodes.

Back to Persistent stack