- Eliminated nodup_vars check.
- Unification and matching functions now check types of term variables / sorts
of type variables when applying a substitution.
- Thm.instantiate now takes (ctyp * ctyp) list instead of (indexname * ctyp) list
as argument, to allow for proper instantiation of theorems containing
type variables with same name but different sorts.
3 Author: Lawrence C Paulson, Cambridge University Computer Laboratory
4 Copyright 1993 University of Cambridge
6 Discrimination nets: a data structure for indexing items
9 E. Charniak, C. K. Riesbeck, D. V. McDermott.
10 Artificial Intelligence Programming.
11 (Lawrence Erlbaum Associates, 1980). [Chapter 14]
13 match_term no longer treats abstractions as wildcards; instead they match
14 only wildcards in patterns. Requires operands to be beta-eta-normal.
21 exception DELETE and INSERT
22 val delete: (key list * 'a) * 'b net * ('a * 'b -> bool) -> 'b net
23 val delete_term: (term * 'a) * 'b net * ('a * 'b -> bool) -> 'b net
25 val insert: (key list * 'a) * 'a net * ('a*'a -> bool) -> 'a net
26 val insert_term: (term * 'a) * 'a net * ('a*'a -> bool) -> 'a net
27 val lookup: 'a net * key list -> 'a list
28 val match_term: 'a net -> term -> 'a list
29 val key_of_term: term -> key list
30 val unify_term: 'a net -> term -> 'a list
31 val dest: 'a net -> (key list * 'a) list
32 val merge: 'a net * 'a net * ('a*'a -> bool) -> 'a net
38 datatype key = CombK | VarK | AtomK of string;
41 fun string_of_bound i = "*B*" ^ chr (i div 256) ^ chr (i mod 256);
43 (*Keys are preorder lists of symbols -- Combinations, Vars, Atoms.
44 Any term whose head is a Var is regarded entirely as a Var.
45 Abstractions are also regarded as Vars; this covers eta-conversion
46 and "near" eta-conversions such as %x.?P(?f(x)).
48 fun add_key_of_terms (t, cs) =
49 let fun rands (f$t, cs) = CombK :: rands (f, add_key_of_terms(t, cs))
50 | rands (Const(c,_), cs) = AtomK c :: cs
51 | rands (Free(c,_), cs) = AtomK c :: cs
52 | rands (Bound i, cs) = AtomK (string_of_bound i) :: cs
53 in case (head_of t) of
59 (*convert a term to a list of keys*)
60 fun key_of_term t = add_key_of_terms (t, []);
63 (*Trees indexed by key lists: each arc is labelled by a key.
64 Each node contains a list of items, and arcs to children.
65 Keys in the association list (alist) are stored in ascending order.
66 The empty key addresses the entire net.
67 Lookup functions preserve order in items stored at same level.
69 datatype 'a net = Leaf of 'a list
70 | Net of {comb: 'a net,
72 alist: (string * 'a net) list};
75 val emptynet = Net{comb=empty, var=empty, alist=[]};
78 (*** Insertion into a discrimination net ***)
80 exception INSERT; (*duplicate item in the net*)
83 (*Adds item x to the list at the node addressed by the keys.
84 Creates node if not already present.
85 eq is the equality test for items.
86 The empty list of keys generates a Leaf node, others a Net node.
88 fun insert ((keys,x), net, eq) =
89 let fun ins1 ([], Leaf xs) =
90 if gen_mem eq (x,xs) then raise INSERT else Leaf(x::xs)
91 | ins1 (keys, Leaf[]) = ins1 (keys, emptynet) (*expand empty...*)
92 | ins1 (CombK :: keys, Net{comb,var,alist}) =
93 Net{comb=ins1(keys,comb), var=var, alist=alist}
94 | ins1 (VarK :: keys, Net{comb,var,alist}) =
95 Net{comb=comb, var=ins1(keys,var), alist=alist}
96 | ins1 (AtomK a :: keys, Net{comb,var,alist}) =
97 let fun newpair net = (a, ins1(keys,net))
98 fun inslist [] = [newpair empty]
99 | inslist((b: string, netb) :: alist) =
100 if a=b then newpair netb :: alist
101 else if a<b then (*absent, ins1ert in alist*)
102 newpair empty :: (b,netb) :: alist
103 else (*a>b*) (b,netb) :: inslist alist
104 in Net{comb=comb, var=var, alist= inslist alist} end
105 in ins1 (keys,net) end;
107 fun insert_term ((t,x), net, eq) = insert((key_of_term t, x), net, eq);
109 (*** Deletion from a discrimination net ***)
111 exception DELETE; (*missing item in the net*)
113 (*Create a new Net node if it would be nonempty*)
114 fun newnet {comb=Leaf[], var=Leaf[], alist=[]} = empty
115 | newnet {comb,var,alist} = Net{comb=comb, var=var, alist=alist};
117 (*add new (b,net) pair to the alist provided net is nonempty*)
118 fun conspair((b, Leaf[]), alist) = alist
119 | conspair((b, net), alist) = (b, net) :: alist;
121 (*Deletes item x from the list at the node addressed by the keys.
122 Raises DELETE if absent. Collapses the net if possible.
123 eq is the equality test for items. *)
124 fun delete ((keys, x), net, eq) =
125 let fun del1 ([], Leaf xs) =
126 if gen_mem eq (x,xs) then Leaf (gen_rem (eq o swap) (xs,x))
128 | del1 (keys, Leaf[]) = raise DELETE
129 | del1 (CombK :: keys, Net{comb,var,alist}) =
130 newnet{comb=del1(keys,comb), var=var, alist=alist}
131 | del1 (VarK :: keys, Net{comb,var,alist}) =
132 newnet{comb=comb, var=del1(keys,var), alist=alist}
133 | del1 (AtomK a :: keys, Net{comb,var,alist}) =
134 let fun newpair net = (a, del1(keys,net))
135 fun dellist [] = raise DELETE
136 | dellist((b: string, netb) :: alist) =
137 if a=b then conspair(newpair netb, alist)
138 else if a<b then (*absent*) raise DELETE
139 else (*a>b*) (b,netb) :: dellist alist
140 in newnet{comb=comb, var=var, alist= dellist alist} end
141 in del1 (keys,net) end;
143 fun delete_term ((t,x), net, eq) = delete((key_of_term t, x), net, eq);
145 (*** Retrieval functions for discrimination nets ***)
149 (*Ordered association list lookup*)
150 fun oassoc ([], a: string) = raise OASSOC
151 | oassoc ((b,x)::pairs, a) =
152 if b<a then oassoc(pairs,a)
156 (*Return the list of items at the given node, [] if no such node*)
157 fun lookup (Leaf(xs), []) = xs
158 | lookup (Leaf _, _::_) = [] (*non-empty keys and empty net*)
159 | lookup (Net{comb,var,alist}, CombK :: keys) = lookup(comb,keys)
160 | lookup (Net{comb,var,alist}, VarK :: keys) = lookup(var,keys)
161 | lookup (Net{comb,var,alist}, AtomK a :: keys) =
162 lookup(oassoc(alist,a),keys) handle OASSOC => [];
165 (*Skipping a term in a net. Recursively skip 2 levels if a combination*)
166 fun net_skip (Leaf _, nets) = nets
167 | net_skip (Net{comb,var,alist}, nets) =
169 (foldr (fn ((_,net), nets) => net::nets) (var::nets) alist)
172 (** Matching and Unification**)
174 (*conses the linked net, if present, to nets*)
175 fun look1 (alist, a) nets =
176 oassoc(alist,a) :: nets handle OASSOC => nets;
178 (*Return the nodes accessible from the term (cons them before nets)
179 "unif" signifies retrieval for unification rather than matching.
180 Var in net matches any term.
181 Abs or Var in object: if "unif", regarded as wildcard,
182 else matches only a variable in net.
184 fun matching unif t (net,nets) =
185 let fun rands _ (Leaf _, nets) = nets
186 | rands t (Net{comb,alist,...}, nets) =
188 f$t => foldr (matching unif t) nets (rands f (comb,[]))
189 | Const(c,_) => look1 (alist, c) nets
190 | Free(c,_) => look1 (alist, c) nets
191 | Bound i => look1 (alist, string_of_bound i) nets
198 Var _ => if unif then net_skip (net,nets)
199 else var::nets (*only matches Var in net*)
200 (*If "unif" then a var instantiation in the abstraction could allow
201 an eta-reduction, so regard the abstraction as a wildcard.*)
202 | Abs _ => if unif then net_skip (net,nets)
203 else var::nets (*only a Var can match*)
204 | _ => rands t (net, var::nets) (*var could match also*)
207 fun extract_leaves l = List.concat (map (fn Leaf(xs) => xs) l);
209 (*return items whose key could match t, WHICH MUST BE BETA-ETA NORMAL*)
210 fun match_term net t =
211 extract_leaves (matching false t (net,[]));
213 (*return items whose key could unify with t*)
214 fun unify_term net t =
215 extract_leaves (matching true t (net,[]));
220 fun cons_fst x (xs, y) = (x :: xs, y);
222 fun dest (Leaf xs) = map (pair []) xs
223 | dest (Net {comb, var, alist}) =
224 map (cons_fst CombK) (dest comb) @
225 map (cons_fst VarK) (dest var) @
226 List.concat (map (fn (a, net) => map (cons_fst (AtomK a)) (dest net)) alist);
231 fun add eq (net, keys_x) =
232 insert (keys_x, net, eq) handle INSERT => net;
234 fun merge (net1, net2, eq) =
235 Library.foldl (add eq) (net1, dest net2);