Derivative-based matching decides membership full
This proof shows: correctness, termination.
Write ν(x) for the NULLABLE bit of a node x, and a−1L={v:av∈L} for the left quotient of a language L by a symbol a. For a word u, u−1L={v:uv∈L}. Then ε−1L=L, (ua)−1L=a−1(u−1L), and u∈L if and only if ε∈u−1L.
Lemma 1 (constructors). For all expressions x,y,x1,…,xj: L(CAT(x,y))=L(x)L(y), L(ALT(x1,…,xj))=L(x1)∪⋯∪L(xj) and L(STAR(x))=L(x)∗. The bit ν stored with each node it returns is true if and only if ε is in its language.
Proof. Each case of lines L10 to L12 uses a language identity. For CAT: ∅L=L∅=∅ and {ε}L=L{ε}=L. For ALT: union is associative, commutative and idempotent, so the union of the L(xi) equals the union of the languages of the members of the flattened set; L(∅)=∅ adds nothing; an empty union is ∅, and a union of one member is that member. For STAR: ∅∗={ε}∗={ε}, and (L∗)∗=L∗. When a new node is made, its bit is ν(x)∧ν(y) for a concatenation, the disjunction of the members' bits for a union, and true for a star. By induction on the order in which nodes are made, each bit is correct: ε∈L(x)L(y) if and only if ε∈L(x) and ε∈L(y); ε is in a union if and only if it is in a member; and ε∈L∗ always. The base nodes ∅, ε and the symbols have the bits false, true and false. Hash-consing returns an existing node only when the node is equal, with the same language and bit. □
Lemma 2 (derivative). For every node r and symbol a, L(D(r,a))=a−1L(r).
Proof. By induction on the structure of r. Memoization returns a value computed earlier for the same pair, so it does not change the result.
- r=∅: a−1∅=∅. r=ε: no word av equals ε, so a−1{ε}=∅. This is line L5.
- r=b, a symbol: a−1{b} is {ε} if b=a and ∅ otherwise. This is line L6.
- r=s1∣⋯∣sj: a−1(L1∪⋯∪Lj)=a−1L1∪⋯∪a−1Lj. By induction and Lemma 1, line L7 returns an expression for this set.
- r=st: let av∈L(s)L(t), so av=xy with x∈L(s) and y∈L(t). If x=ε, then x=ax′ with x′∈a−1L(s), and v=x′y∈(a−1L(s))L(t). If x=ε, then ε∈L(s) and v∈a−1L(t). Conversely, both cases give av∈L(s)L(t). So
a−1(L(s)L(t))=(a−1L(s))L(t)∪(a−1L(t) if ε∈L(s), else ∅). By Lemma 1, ν(s) tells which case applies, and line L8 returns an expression for this set, by induction and Lemma 1.
- r=s∗: let av∈L(s)∗. Then av=x1⋯xl with l≥1 and each xi∈L(s); drop the empty factors, so that x1 is not empty. Then x1=ax′ with x′∈a−1L(s), and v=x′x2⋯xl∈(a−1L(s))L(s)∗. Conversely, if v=x′y with ax′∈L(s) and y∈L(s)∗, then av=(ax′)y∈L(s)∗. So a−1(L(s)∗)=(a−1L(s))L(s)∗, which is what line L9 returns, by induction and Lemma 1, since the node r itself has language L(s)∗. □
Termination. A call D(r,a) evaluates D only on (r,a) and on pairs (z,a) for children z of r. The children of a node were made before it, so they are strictly below it in the finite DAG of nodes reachable from r, and the evaluation is a postorder over this DAG, with each pair evaluated at most once by memoization. Each step makes finitely many calls of the constructors, each of which does finitely many set and table operations. So each call of D stops. The loop of lines L1 to L3 runs at most n times. So DERIV-MATCH stops.
Theorem. DERIV-MATCH(r0,w) returns true if and only if w∈L(r0).
Proof. Let ri be the value of r after i rounds of the loop. By induction on i, L(ri)=(a1⋯ai)−1L(r0). For i=0 this is ε−1L(r0)=L(r0). For i≥1, by Lemma 2,
L(ri)=ai−1L(ri−1)=ai−1((a1⋯ai−1)−1L(r0))=(a1⋯ai)−1L(r0). If line L3 returns false in round i, then ri=∅ and (a1⋯ai)−1L(r0)=∅. Every word w with prefix a1⋯ai has w=a1⋯aiv, and w∈L(r0) would put v in this empty set. So w∈/L(r0), and false is correct.
Otherwise the loop ends with r=rn, and line L4 returns ν(rn). By Lemma 1, this is true if and only if ε∈L(rn)=w−1L(r0), which holds if and only if w∈L(r0). ■