{------------------------------------------------------------------------------ TABLES A Table is a set of entries, each containing a key and an associated value, the key being used to look up the value. In database-style applications, the value may be a record, and the key may be a field in it. The normal effect of sharing of subexpressions should avoid serious space problems. However, `computed' keys may cause a space problem. Keys are assumed to be unique. The effect of non-unique keys can be obtained by associated a list value such as [v1,v2,...] with each key. With the `enterList' function, the first entry for a key takes precedence over any later ones with the same key. This allows a table to be built `lazily', the entries in the list only being evaluated as needed to satisfy `find' calls. REQUIREMENTS: The results module `result.g' must be loaded before this one. The key type must be ordered (an instance of class Ord). EXPORTS: Table k v the type of tables; k and v are the key and value types newTable an empty table enter t k v add entry to t (no effect if old entry for k exists) enterList t es add a list of (key,val) pairs to t update t k v change entry in t (or add new entry if necessary) updateList t es change a list of (key,val) pairs in t find t k lookup k in t giving (success v) or (failure "not found") delete t k remove entry in t for key k (if any) entries t return list of all (key,val) pairs in t in key order ------------------------------------------------------------------------------} module Table where import Result -- The implementation here uses a binary search tree, giving `log n' time -- operations, provided that the tree remains well-balanced. Eventually, there -- should be a constant-time version with the same semantics. data Table k v = Empty | Fork (Table k v) (k,v) (Table k v) newTable = Empty find Empty key = failure "not found" find (Fork left (k,v) right) key | key < k = find left key | key == k = success v | key > k = find right key enter Empty key val = Fork Empty (key,val) Empty enter (Fork left (k,v) right) key val | key < k = Fork (enter left key val) (k,v) right | key == k = Fork left (k,v) right | key > k = Fork left (k,v) (enter right key val) update Empty key val = Fork Empty (key,val) Empty update (Fork left (k,v) right) key val | key < k = Fork (update left key val) (k,v) right | key == k = Fork left (key,val) right | key > k = Fork left (k,v) (update right key val) delete Empty key = Empty delete (Fork left (k,v) right) key | key < k = Fork (delete left key) (k,v) right | key == k = graft left right | key > k = Fork left (k,v) (delete right key) where graft left Empty = left graft left right = Fork left e right' where (e,right') = leftmost right leftmost (Fork Empty e r) = (e,r) leftmost (Fork l e r) = (e2, Fork l' e r) where (e2,l') = leftmost l -- `enterList t es' adds a list of new entries. It is lazy in es (but may build -- a poorly balanced tree). enterList t [] = t enterList Empty (e:res) = Fork left e right where k = fst e left = enterList Empty [e1 | e1<-res, fst e1 < k] right = enterList Empty [e1 | e1<-res, fst e1 > k] enterList (Fork left e right) es = Fork left' e right' where k = fst e left' = enterList left [e1 | e1<-es, fst e1 < k] right' = enterList right [e1 | e1<-es, fst e1 > k] -- `updateList t es' makes a list of updates. It is strict in es, and optimised -- to produce a well balanced tree. it can be used with es==[] purely to -- rebalance the tree. updateList t es = balance (mergeKey (entries t) (unique (sortKey es))) where balance [] = Empty balance es = Fork left (es!!m) right where left = balance (take m es) right = balance (drop (m+1) es) m = length es `div` 2 unique [] = [] unique [e] = [e] unique ((k1,v1):(k2,v2):res) = if k1==k2 then unique ((k2,v2):res) else (k1,v1) : unique ((k2,v2):res) sortKey kvs = foldr insertKey [] kvs where insertKey kv [] = [kv] insertKey (k1,v1) ((k2,v2):res) | k1 <= k2 = (k1,v1):(k2,v2):res | otherwise = (k2,v2):insertKey (k1,v1) res mergeKey [] kvs = kvs mergeKey kvs [] = kvs mergeKey ((k1,v1):kvs1) ((k2,v2):kvs2) | k1 <= k2 = (k1,v1) : mergeKey kvs1 ((k2,v2):kvs2) | otherwise = (k2,v2) : mergeKey ((k1,v1):kvs1) kvs2 -- `entries t' returns the list of entries in t, sorted by key. Inefficient -- unless tree-optimised version of ++ is used. entries Empty = [] entries (Fork left e right) = entries left ++ [e] ++ entries right