home *** CD-ROM | disk | FTP | other *** search
- {------------------------------------------------------------------------------
- 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
-