FM-SBLEX-3.0: src/lib/Trie.hs
-- A strict general trie for types with orderings
module Trie
(
Trie,
-- * Construction
empty,
fromList, fromListWith,
insert, insertWith,
-- * Query
lookup, member,
-- * Destruction
toList
) where
import qualified Data.Map as Map
import Data.Maybe (fromMaybe, isJust)
import Prelude hiding (lookup)
-- The value argument couble be Maybe b, but we
-- save some memory this way
data Trie a b = Node !(Map.Map a (Trie a b))
| ValNode !(Map.Map a (Trie a b)) !b
-- deriving (Show)
-- | An empty trie
empty :: Trie a b
empty = Node Map.empty
-- | Construct a trie from a list of (key, value) pairs.
fromList :: Ord a => [([a], b)] -> Trie a b
fromList = fromListWith const
-- | Construct a trie from a list of (key, value) pairs
-- with a combining function for values of equals keys.
-- The order in which the elements are inserted and passed
-- to the combining function is unspecified.
fromListWith :: Ord a => (b -> b -> b) -> [([a], b)] -> Trie a b
fromListWith f = fromListWith' f id
-- | Construct a trie from a list of (key, value) pairs
-- with a combining function for values of equals keys,
-- and a construction function used the first time a key is inserted.
-- The order in which the elements are inserted and passed
-- to the combining function is unspecified.
fromListWith' :: Ord a => (b -> c -> b) -> (c -> b) -> [([a], c)] -> Trie a b
fromListWith' f g = foldl (flip (uncurry (insertWith' f g))) empty -- foldl for strictness
-- | Insert an element into a trie.
insert :: Ord a => [a] -> b -> Trie a b -> Trie a b
insert = insertWith const
-- | Insert an element into a trie with a combining
-- function for the value of an existing key equal to
-- the given one.
insertWith :: Ord a => (b -> b -> b) -- ^ The first argument is the existing value,
-- the second is the new value.
-> [a] -> b -> Trie a b -> Trie a b
insertWith f = insertWith' f id
-- | Insert an element into a trie with a combining
-- function for the value of an existing key equal to
-- the given one, and a construction function used the
-- first time a key is inserted.
insertWith' :: Ord a => (b -> c -> b) -- ^ The first argument is the existing value,
-- the second is the new value.
-> (c -> b) -- ^ Used to transform the input value to a value
-- in the map if there is no existing value.
-> [a] -> c -> Trie a b -> Trie a b
insertWith' f g cs y = insertWith_ cs
where
insertWith_ [] (Node m) = ValNode m (g y)
insertWith_ [] (ValNode m v) = ValNode m (f v y)
insertWith_ (c:cs) t = updateMap h t
where h m = Map.insert c (insertWith_ cs t) m
where t = fromMaybe empty (Map.lookup c m)
updateMap h (Node m) = Node (h m)
updateMap h (ValNode m v) = ValNode (h m) v
-- | Lookup a key in a trie.
lookup :: (Monad m, Ord a) => [a] -> Trie a b -> m b
lookup [] (Node _) = fail "Not found"
lookup [] (ValNode _ v) = return v
lookup (c:cs) t = case Map.lookup c (getMap t) of
Nothing -> fail "Not found"
Just t -> lookup cs t
where getMap (Node m) = m
getMap (ValNode m _) = m
-- | Check if a key is in a trie.
member :: Ord a => [a] -> Trie a b -> Bool
member k tr = isJust (lookup k tr)
-- | Get the (key,value) pairs from a trie.
-- The results are ordered by key.
toList :: Trie a b -> [([a],b)]
toList t = collapse t []
where
collapse (Node m) xs = rest m xs
collapse (ValNode m v) xs = (reverse xs, v) : rest m xs
rest m xs = concat [collapse tr (c:xs) | (c,tr) <- Map.toList m]