TernaryTrees-0.2.0.0: Data/Map/StringMap.hs
module Data.Map.StringMap where
import Data.Binary
import Control.Monad
-- | Elem2 a b is used to hold elements of a list after insertion, and
-- indicate that we've reached the end of the list.
data CharEl a b = C !Char | Val b
deriving (Show)
-- | StringMap a is ternary tree. It is commonly used for storing word lists
-- like dictionaries for spell checking etc.
data StringMap a = SNode {-# UNPACK #-}!Char !(StringMap a) !(StringMap a) !(StringMap a)
| SElemNode a !(StringMap a) !(StringMap a) !(StringMap a)
| SEnd
deriving (Show, Eq)
-- | Inserts a new list of elements into a tree.
insert :: String -> a -> StringMap a -> StringMap a
-- General case
insert xss@(x:xs) val (SNode ele l e h) =
case compare x ele of
LT -> SNode ele (insert xss val l) e h
EQ -> SNode ele l (insert xs val e) h
GT -> SNode ele l e (insert xss val h)
-- Insert new elements quickly
insert xss@(x:xs) val SEnd =
insert' xss val
-- SEnd of word in non empty tree
insert [] val t@(SNode ele l e h) =
case compare '\0' ele of
EQ -> t
LT -> SNode ele (insert [] val l) e h
-- SEnd of word in empty tree
insert [] val SEnd =
SElemNode val SEnd SEnd SEnd
-- | Quickly build an initial tree.
insert' :: String -> a -> StringMap a
insert' (x:xs) a = SNode x SEnd (insert' xs a) SEnd
insert' [] a = SElemNode a SEnd SEnd SEnd
-- | Returns true if the string is in the StringMap
isElem :: String -> StringMap a -> Bool
isElem _ SEnd = False
isElem [] (SNode ele l e h) = ele == '\0' || isElem [] l
isElem xss@(x:xs) (SNode ele l e h) =
case compare x ele of
LT -> isElem xss l
EQ -> isElem xs e
GT -> isElem xss h
-- | Returns the number of non-Null Elems
treeSize :: StringMap a -> Int
treeSize SEnd = 0
treeSize (SElemNode _ l e h) = treeSize l + treeSize e + treeSize h
treeSize (SNode _ l e h) = 1 + treeSize l + treeSize e + treeSize h
-- | Counts how many entries there are in the tree.
numEntries :: StringMap a -> Int
numEntries SEnd = 0
numEntries (SElemNode _ l e h) = 1 + numEntries l + numEntries e + numEntries h
numEntries (SNode _ l e h) = numEntries l + numEntries e + numEntries h
-- | Creates a new tree from a list of 'strings'
fromList :: [(String,a)] -> StringMap a
fromList = foldl (\t (k,v) -> insert k v t) SEnd
instance Binary a => Binary (StringMap a) where
put SEnd = put (0 :: Word8)
-- Quite common, so speecialised
put (SNode ch SEnd SEnd SEnd) = do
putWord8 1
put ch
-- Also common, basically what insert' produces.
put (SNode ch SEnd e SEnd) = do
putWord8 2
put ch
put e
-- General case
put (SNode ch l e h) = do
putWord8 3
put ch
put l
put e
put h
get = do
tag <- getWord8
case tag of
0 -> return SEnd
1 -> do
ch <- get
return (SNode ch SEnd SEnd SEnd)
2 -> do
ch <- get
e <- get
return (SNode ch SEnd e SEnd)
3 -> liftM4 SNode get get get get