packages feed

hackport-0.8.5.0: src/Data/Trie.hs

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE Strict #-}

module Data.Trie
    ( Trie(..)
    , singleton
    , lookup
    , toList
    ) where

import Control.Applicative
import Control.Monad
import Data.Binary
import qualified Data.DList as DList
import Data.DList (DList)
import qualified Data.Foldable as F
import qualified Data.Map.Strict as Map
import Data.Map.Strict (Map)
import GHC.Generics

import Prelude hiding (lookup)

-- | A tree with branches keyed by a 'Map', which is useful for storing
--   and searching list prefixes in a compact way. Each node is marked
--   with a type signifying list enedings.
--
--   Example: @"he"@ and @"hi"@ stored in a @'Trie' 'Char' '()'@ would look like:
--
--   @Nothing :< toList [('h', Nothing :< toList [('e', Just () :< toList []),('i', Just () :< toList [])])]@
data Trie k a = Maybe a :< Map k (Trie k a)
    deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic)

instance Ord k => Semigroup (Trie k a) where
    ma1 :< f1 <> ma2 :< f2 = (ma2 <|> ma1) :< Map.unionWith (<>) f1 f2

instance Ord k => Monoid (Trie k a) where
    mempty = Nothing :< Map.empty

instance (Binary k, Binary a) => Binary (Trie k a)

-- | Walk the 'Trie' using the given key list looking for a matching node.
lookup :: Ord k => Trie k a -> [k] -> Maybe (Trie k a)
lookup (_ :< mapK) (k:ks) =
    Map.lookup k mapK >>= (\c -> lookup c ks)
lookup t [] = Just t

-- | Return the path and value of every node marked with 'Just'
toList :: Trie k a -> [([k],a)]
toList = DList.toList . go DList.empty DList.empty
  where
    go :: DList ([k], a) -> DList k -> Trie k a -> DList ([k], a)
    go justs path (b :< m) =
        let justs' = case b of
                Just x -> justs `DList.snoc` (DList.toList path, x)
                Nothing -> justs
        in justs' <> F.foldMap'
            (\(x,t) -> go justs' (path `DList.snoc` x) t)
            (Map.toAscList m)

-- | Create a simple 'Trie' from a key list and a value.
singleton :: [k] -> a -> Trie k a
singleton l x = go l
  where
    go (k:ks) = Nothing :< Map.singleton k (go ks)
    go []     = Just x :< Map.empty