packages feed

iyql-0.0.5.1: src/main/haskell/Yql/Data/Trie.hs

-- Copyright (c) 2010, Diego Souza
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
--   * Redistributions of source code must retain the above copyright notice,
--     this list of conditions and the following disclaimer.
--   * Redistributions in binary form must reproduce the above copyright notice,
--     this list of conditions and the following disclaimer in the documentation
--     and/or other materials provided with the distribution.
--   * Neither the name of the <ORGANIZATION> nor the names of its contributors
--     may be used to endorse or promote products derived from this software
--     without specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-- | A [inefficient] implementation of Trie
module Yql.Data.Trie 
       ( -- * Types
         Trie()
         -- * Query
       , Yql.Data.Trie.null 
       , member
       , size
       , subtrie
         -- * Conversion
       , fromList
       , toList
         -- * Traversal
       , fold
         -- * Construction
       , empty
       , singleton
         -- * Combine
       , union
       ) where

import qualified Data.Map as M

data Trie k = Trie (M.Map k (Bool,Trie k))
            deriving (Show,Eq,Ord)

fold :: ([k] -> a -> a) -> a -> Trie k -> a
fold f0 z0 = fst . fold' f0 (z0,id)
  where fold' f z (Trie m) = M.foldrWithKey g z m
          where g k (leaf,t) (acc,ks)
                  | leaf      = (f (ks.(k:) $ []) (fold h acc t), ks)
                  | otherwise = fold' h (acc, ks) t
                    where h = f . (k:)

-- | Test if a given Trie is empty
null :: Trie k -> Bool
null (Trie m) = M.null m

-- | Creates an empty trie
empty :: Trie k
empty = Trie M.empty

-- | Returns a trie with a single value
singleton :: Ord k => [k] -> Trie k
singleton ks = fromList [ks]

-- | Maps a trie into list type, such as `toList . fromList = id'
toList :: Show k => Trie k -> [[k]]
toList = fold (:) []

-- | Maps a list into trie, such as `fromList . toList = id'.
fromList :: Ord k => [[k]] -> Trie k
fromList = foldr union empty . map fromList'
  where fromList' (k:ks) = Trie (M.singleton k (Prelude.null ks,fromList' ks))
        fromList' []     = empty

-- | The number of entries in the trie.
size :: Show k => Trie k -> Int
size = fold (const (1+)) 0

union :: Ord k => Trie k -> Trie k -> Trie k
union (Trie v0) (Trie v1) = Trie (M.unionWith unionValue v0 v1)
  where unionValue (a,t0) (b,t1) = (a || b,t0 `union` t1)

-- | Performs a prefix search.
subtrie :: Ord k => [k] -> Trie k -> Trie k
subtrie = flip (foldl f)
  where f (Trie m) k = snd $ M.findWithDefault (False,empty) k m

-- | Test whether a given prefix is in the trie.
member :: Ord k => [k] -> Trie k -> Bool
member ks t = fst $ foldl f (True,t) ks
  where f (_,Trie m) k = M.findWithDefault (False,empty) k m