pred-trie 0.2.4 → 0.3.0
raw patch · 10 files changed
+306/−674 lines, 10 filesdep +attoparsecdep +containersdep +pred-triedep −quickcheck-instancesdep −tastydep −tasty-hunitdep ~basedep ~composition-extra
Dependencies added: attoparsec, containers, pred-trie, sets, text, tries
Dependencies removed: quickcheck-instances, tasty, tasty-hunit, tasty-quickcheck
Dependency ranges changed: base, composition-extra
Files
- bench/Bench.hs +53/−0
- bench/SimpleVShort.hs +0/−64
- pred-trie.cabal +37/−26
- src/Data/Trie/Pred.hs +143/−0
- src/Data/Trie/Pred/Disjoint.hs +0/−64
- src/Data/Trie/Pred/Disjoint/Tail.hs +0/−134
- src/Data/Trie/Pred/Step.hs +73/−0
- src/Data/Trie/Pred/Unified.hs +0/−112
- src/Data/Trie/Pred/Unified/Tail.hs +0/−261
- test/Spec.hs +0/−13
+ bench/Bench.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE+ OverloadedStrings+ #-}++module Main where+++import Prelude hiding (lookup)+import Data.Trie.Pred+import Data.Trie.Pred.Step (PredStep (..), PredSteps (..))+import Data.Trie.Class+import Data.Trie.Map (MapStep (..))+import qualified Data.Map as Map+import Data.List.NonEmpty+import qualified Data.List.NonEmpty as NE+import qualified Data.Text as T+import Data.Attoparsec.Text+import Criterion.Main+import Data.Set.Class as Sets+++doubleLit :: RootedPredTrie T.Text Double+doubleLit = RootedPredTrie Nothing $ PredTrie+ (MapStep $ unUnion $ foldMap (Union . genStep) [1..100])+ (PredSteps [])+ where genStep n = Map.singleton (T.pack $ show n) (Just n, Nothing)++doubleAtto :: RootedPredTrie T.Text Double+doubleAtto = RootedPredTrie Nothing $ PredTrie mempty $ PredSteps+ [PredStep "d" (eitherToMaybe . parseOnly double) (Just id) mempty]+ where+ eitherToMaybe (Left _) = Nothing+ eitherToMaybe (Right a) = Just a+++main = defaultMain+ [ bgroup "Lit vs. Pred"+ [ bgroup "Lit"+ [ bench "1" $ whnf (lookup ["1"]) doubleLit+ , bench "2" $ whnf (lookup ["21"]) doubleLit+ , bench "3" $ whnf (lookup ["41"]) doubleLit+ , bench "4" $ whnf (lookup ["61"]) doubleLit+ , bench "4" $ whnf (lookup ["81"]) doubleLit+ ]+ , bgroup "Pred"+ [ bench "1" $ whnf (lookup ["1"]) doubleAtto+ , bench "2" $ whnf (lookup ["21"]) doubleAtto+ , bench "3" $ whnf (lookup ["41"]) doubleAtto+ , bench "4" $ whnf (lookup ["61"]) doubleAtto+ , bench "4" $ whnf (lookup ["81"]) doubleAtto+ ]+ ]+ ]
− bench/SimpleVShort.hs
@@ -1,64 +0,0 @@-module Main where---import Data.Trie.Pred.FastUnified-import qualified Data.Trie.Pred.FastUnified as FU-import Data.Trie.Pred.NormUnified-import qualified Data.Trie.Pred.NormUnified as NU-import Criterion.Main-import Data.List.NonEmpty-import qualified Data.List.NonEmpty as NE---tdFU = FUMore "foo" Nothing $ NE.fromList- [ FURest ("bar":|["baz","qux"]) 1- , FUMore "tro" (Just 2) $ NE.fromList- [ FUMore "zja" (Just 3) $ NE.fromList- [ FURest ("hda":|["jes","kuq"]) 4 ]- , FURest ("end":|["orp","vag"]) 5- ]- , FURest ("dic":|["but","gea"]) 6- ]--tdNU = NUMore "foo" Nothing- [ NUMore "bar" Nothing- [ NUMore "baz" Nothing- [ NUMore "qux" (Just 1) []- ]- ]- , NUMore "tro" (Just 2)- [ NUMore "zja" (Just 3)- [ NUMore "hda" Nothing- [ NUMore "jes" Nothing- [ NUMore "kuq" (Just 4) []- ]- ]- ]- , NUMore "end" Nothing- [ NUMore "orp" Nothing- [ NUMore "vag" (Just 5) []- ]- ]- ]- , NUMore "dic" Nothing- [ NUMore "but" Nothing- [ NUMore "gea" (Just 6) []- ]- ]- ]----main = defaultMain- [ bgroup "Fast" [ bench "foobarbaz" $ whnf (FU.lookup $ "foo":|["bar","baz"]) tdFU- , bench "footrozjahdajes" $ whnf (FU.lookup $ "foo":|["tro","zja","hda","jes"]) tdFU- , bench "footroendorpvag" $ whnf (FU.lookup $ "foo":|["tro","end","orp","vag"]) tdFU- , bench "foodicbutgea" $ whnf (FU.lookup $ "foo":|["dic","but","gea"]) tdFU- ]-- , bgroup "Norm" [ bench "foobarbaz" $ whnf (NU.lookup $ "foo":|["bar","baz"]) tdNU- , bench "footrozjahdajes" $ whnf (NU.lookup $ "foo":|["tro","zja","hda","jes"]) tdNU- , bench "footroendorpvag" $ whnf (NU.lookup $ "foo":|["tro","end","orp","vag"]) tdNU- , bench "foodicbutgea" $ whnf (NU.lookup $ "foo":|["dic","but","gea"]) tdNU- ]- ]
pred-trie.cabal view
@@ -1,5 +1,5 @@ Name: pred-trie-Version: 0.2.4+Version: 0.3.0 Author: Athan Clark <athan.clark@gmail.com> Maintainer: Athan Clark <athan.clark@gmail.com> License: BSD3@@ -14,41 +14,52 @@ Default-Language: Haskell2010 HS-Source-Dirs: src GHC-Options: -Wall- Exposed-Modules: Data.Trie.Pred.Unified- Data.Trie.Pred.Unified.Tail- Data.Trie.Pred.Disjoint- Data.Trie.Pred.Disjoint.Tail+ Exposed-Modules: Data.Trie.Pred+ Data.Trie.Pred.Step Build-Depends: base >= 4.6 && < 5 , semigroups , mtl- , composition-extra >= 1.2+ , containers+ , composition-extra >= 2.0.0+ , tries >= 0.0.2 , QuickCheck -Test-Suite spec+-- Test-Suite spec+-- Type: exitcode-stdio-1.0+-- Default-Language: Haskell2010+-- Hs-Source-Dirs: src+-- , test+-- Ghc-Options: -Wall -threaded+-- Main-Is: Spec.hs+-- Other-Modules: Data.Trie.PredSpec+-- Build-Depends: base+-- , tasty+-- , tasty-quickcheck+-- , tasty-hunit+-- , QuickCheck+-- , quickcheck-instances+-- , semigroups+-- , mtl+-- , composition-extra++Benchmark bench Type: exitcode-stdio-1.0 Default-Language: Haskell2010- Hs-Source-Dirs: src- , test- Ghc-Options: -Wall- Main-Is: Spec.hs- Build-Depends: base- , tasty- , tasty-quickcheck- , tasty-hunit- , QuickCheck- , quickcheck-instances- , semigroups- , mtl- , composition-extra--Benchmark simplevshort- Type: exitcode-stdio-1.0- Main-Is: SimpleVShort.hs- HS-Source-Dirs: bench+ Main-Is: Bench.hs+ HS-Source-Dirs: bench , src- Build-Depends: base+ Ghc-Options: -Wall -threaded+ Build-Depends: base+ , pred-trie+ , tries+ , containers , criterion , semigroups+ , attoparsec+ , text+ , composition-extra+ , QuickCheck+ , sets Source-Repository head Type: git
+ src/Data/Trie/Pred.hs view
@@ -0,0 +1,143 @@+{-# LANGUAGE+ ExistentialQuantification+ , FlexibleContexts+ , FlexibleInstances+ , MultiParamTypeClasses+ , DeriveFunctor+ #-}++module Data.Trie.Pred where++import Prelude hiding (lookup)+import Data.Trie.Pred.Step+import Data.Trie.Class+import qualified Data.Trie.Map as MT+import qualified Data.Map as Map+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.List.NonEmpty as NE++import Data.Functor.Syntax+import Data.Monoid+import Data.Maybe (fromMaybe)++++-- * Predicated Trie++data PredTrie s a = PredTrie+ { predLits :: MT.MapStep PredTrie s a+ , predPreds :: PredSteps PredTrie s a+ } deriving (Functor)++instance Ord s => Trie NonEmpty s PredTrie where+ lookup ts (PredTrie ls ps) =+ getFirst $ First (lookup ts ls) <> First (lookup ts ps)+ delete ts (PredTrie ls ps) = PredTrie (delete ts ls) (delete ts ps)+ insert ts x (PredTrie ls ps) = PredTrie (insert ts x ls) ps -- can only insert literals++instance Ord s => Monoid (PredTrie s a) where+ mempty = PredTrie mempty mempty+ mappend (PredTrie ls1 ps1) (PredTrie ls2 ps2) =+ PredTrie (ls1 <> ls2) (ps1 <> ps2)++emptyPT :: PredTrie s a+emptyPT = PredTrie MT.empty (PredSteps [])++++-- subtrie :: Ord s => NonEmpty s -> PredTrie s a -> PredTrie s a+-- subtrie (t:|ts) (PredTrie (MapTrie (MapStep ls)) ps)+-- | null ts = getFirst $ First (lookup ts ls)++-- | Find the nearest parent node of the requested query, while returning+-- the split of the string that was matched, and what wasn't.+matchPT :: Ord s => NonEmpty s -> PredTrie s a -> Maybe (NonEmpty s, a, [s])+matchPT (t:|ts) (PredTrie ls (PredSteps ps)) = getFirst $+ First (goLit ls) <> foldMap (First . goPred) ps+ where+ goLit (MT.MapStep xs) = do+ (mx,mxs) <- Map.lookup t xs+ let mFoundHere = do x <- mx+ return (t:|[], x, [])+ if null ts then mFoundHere+ else getFirst $ First (do (pre,y,suff) <- matchPT (NE.fromList ts) =<< mxs+ return (t:|NE.toList pre, y, suff))+ <> First mFoundHere++ goPred (PredStep _ p mx xs) = do+ r <- p t+ let mFoundHere = do x <- mx <$~> r+ return (t:|[], x, [])+ if null ts then mFoundHere+ else getFirst $ First (do (pre,y,suff) <- matchPT (NE.fromList ts) xs+ return (t:|NE.toList pre, y r, suff))+ <> First mFoundHere+++matchesPT :: Ord s => NonEmpty s -> PredTrie s a -> [(NonEmpty s, a, [s])]+matchesPT (t:|ts) (PredTrie ls (PredSteps ps)) =+ fromMaybe [] $ getFirst $ First (goLit ls) <> foldMap (First . goPred) ps+ where+ goLit (MT.MapStep xs) = do+ (mx,mxs) <- Map.lookup t xs+ let mFoundHere = do x <- mx+ return [(t:|[],x,ts)]+ prependAncestry (pre,x,suff) = (t:| NE.toList pre,x,suff)+ if null ts then mFoundHere+ else do foundHere <- mFoundHere+ let rs = fromMaybe [] $ matchesPT (NE.fromList ts) <$> mxs+ return $ foundHere ++ (prependAncestry <$> rs)++ goPred (PredStep _ p mx xs) = do+ r <- p t+ let mFoundHere = do x <- mx <$~> r+ return [(t:|[],x,ts)]+ prependAncestryAndApply (pre,x,suff) = (t:| NE.toList pre,x r,suff)+ if null ts then mFoundHere+ else do foundHere <- mFoundHere+ let rs = matchesPT (NE.fromList ts) xs+ return $ foundHere ++ (prependAncestryAndApply <$> rs)++-- * Rooted Predicated Trie++data RootedPredTrie s a = RootedPredTrie+ { rootedBase :: Maybe a+ , rootedSub :: PredTrie s a+ } deriving (Functor)++instance Ord s => Trie [] s RootedPredTrie where+ lookup [] (RootedPredTrie mx _) = mx+ lookup ts (RootedPredTrie _ xs) = lookup (NE.fromList ts) xs++ delete [] (RootedPredTrie _ xs) = RootedPredTrie Nothing xs+ delete ts (RootedPredTrie mx xs) = RootedPredTrie mx $ delete (NE.fromList ts) xs++ insert [] x (RootedPredTrie _ xs) = RootedPredTrie (Just x) xs+ insert ts x (RootedPredTrie mx xs) = RootedPredTrie mx $ insert (NE.fromList ts) x xs++instance Ord s => Monoid (RootedPredTrie s a) where+ mempty = emptyRPT+ mappend (RootedPredTrie mx xs) (RootedPredTrie my ys) = RootedPredTrie+ (getLast $ Last mx <> Last my) $ xs <> ys+++emptyRPT :: RootedPredTrie s a+emptyRPT = RootedPredTrie Nothing emptyPT++matchRPT :: Ord s => [s] -> RootedPredTrie s a -> Maybe ([s], a, [s])+matchRPT [] (RootedPredTrie mx _) = do x <- mx+ return ([],x,[])+matchRPT ts (RootedPredTrie mx xs) = getFirst $+ First mFoundThere <> First (do x <- mx+ return ([],x,[]))+ where mFoundThere = do (pre,x,suff) <- matchPT (NE.fromList ts) xs+ return (NE.toList pre,x,suff)++matchesRPT :: Ord s => [s] -> RootedPredTrie s a -> [([s], a, [s])]+matchesRPT [] (RootedPredTrie mx _) = fromMaybe [] $ do x <- mx+ return [([],x,[])]+matchesRPT ts (RootedPredTrie mx xs) =+ foundHere ++ fmap allowRoot (matchesPT (NE.fromList ts) xs)+ where foundHere = fromMaybe [] $ do x <- mx+ return [([],x,[])]+ allowRoot (pre,x,suff) = (NE.toList pre,x,suff)
− src/Data/Trie/Pred/Disjoint.hs
@@ -1,64 +0,0 @@-module Data.Trie.Pred.Disjoint- ( RDPTrie (..)- , merge- , lookup- , lookupWithL- , lookupNearestParent- , litSingleton- , litExtrude- , module Data.Trie.Pred.Disjoint.Tail- ) where--import Prelude hiding (lookup)-import Data.Trie.Pred.Disjoint.Tail hiding (lookup, lookupWithL, lookupNearestParent, merge)-import qualified Data.Trie.Pred.Disjoint.Tail as ND-import Data.Monoid-import qualified Data.List.NonEmpty as NE----- | A Rooted, predicate, disjointly indexed trie-data RDPTrie p t x = Rooted (Maybe x) [DPTrie p t x]--instance (Eq p, Eq t) => Monoid (RDPTrie p t x) where- mempty = Rooted Nothing []- mappend = Data.Trie.Pred.Disjoint.merge--merge :: (Eq p, Eq t) => RDPTrie p t x -> RDPTrie p t x -> RDPTrie p t x-merge (Rooted mx xs) (Rooted my ys) =- Rooted my $ foldr go [] $ xs ++ ys- where- go :: (Eq p, Eq t) => DPTrie p t x -> [DPTrie p t x] -> [DPTrie p t x]- go a [] = [a]- go a (b:bs) | ND.areDisjoint a b = a : b : bs- | otherwise = ND.merge a b : bs--lookup :: (Eq t) => [t] -> RDPTrie p t x -> Maybe x-lookup [] (Rooted mx _) = mx-lookup ts (Rooted _ xs) = firstJust $ map (ND.lookup $ NE.fromList ts) xs--lookupWithL :: (Eq t) => (t -> t) -> [t] -> RDPTrie p t x -> Maybe x-lookupWithL _ [] (Rooted mx _) = mx-lookupWithL f ts (Rooted _ xs) = firstJust $ map (ND.lookupWithL f $ NE.fromList ts) xs--lookupNearestParent :: (Eq t) => [t] -> RDPTrie p t x -> Maybe x-lookupNearestParent [] (Rooted mx _) = mx-lookupNearestParent ts (Rooted mx xs) =- getFirst $ (First $ firstJust $ map (ND.lookupNearestParent $ NE.fromList ts) xs) <> First mx--firstJust :: [Maybe a] -> Maybe a-firstJust [] = Nothing-firstJust (Nothing:xs) = firstJust xs-firstJust (Just x :xs) = Just x---litSingleton :: [t] -> x -> RDPTrie p t x-litSingleton [] x = Rooted (Just x) []-litSingleton ts x = Rooted Nothing [ND.litSingletonTail (NE.fromList ts) x]---litExtrude :: [t] -> RDPTrie p t x -> RDPTrie p t x-litExtrude [] r = r-litExtrude [t] (Rooted mx xs) = Rooted Nothing [DMore t mx xs]-litExtrude ts (Rooted mx xs) = Rooted Nothing [ND.litExtrudeTail (init ts) $- DMore (last ts) mx xs- ]
− src/Data/Trie/Pred/Disjoint/Tail.hs
@@ -1,134 +0,0 @@-{-# LANGUAGE- GADTs- #-}--module Data.Trie.Pred.Disjoint.Tail- ( DPTrie (..)- , lookup- , lookupWithL- , lookupNearestParent- , merge- , areDisjoint- , litSingletonTail- , litExtrudeTail- , sort- ) where--import Prelude hiding (lookup)-import Data.List.NonEmpty as NE hiding (map, sort)-import Control.Applicative----data DPTrie p t x where- DMore :: t- -> Maybe x- -> [DPTrie p t x]- -> DPTrie p t x- DPred :: p- -> (t -> Maybe r)- -> Maybe (r -> x)- -> [DPTrie p t (r -> x)]- -> DPTrie p t x----- | Overwrites when similar, leaves untouched when not-merge :: (Eq p, Eq t) => DPTrie p t x -> DPTrie p t x -> DPTrie p t x-merge xx@(DMore t mx xs) yy@(DMore p my ys)- | t == p = DMore p my $ foldr go [] $ xs ++ ys- | otherwise = xx- where- go :: (Eq p, Eq t) => DPTrie p t x -> [DPTrie p t x] -> [DPTrie p t x]- go a [] = [a]- go a (b:bs) | areDisjoint a b = a : b : bs- | otherwise = merge a b : bs-merge xx@(DPred t q mrx xrs) yy@(DPred p w mry yrs)- | t == p = yy- | otherwise = xx-merge xx@(DMore t mx xs) yy@(DPred p w mrx xrs) = yy-merge xx@(DPred t q mrx xrs) yy@(DMore p my ys) = yy---areDisjoint :: (Eq p, Eq t) => DPTrie p t x -> DPTrie p t x -> Bool-areDisjoint (DMore t _ _) (DMore p _ _) = t == p-areDisjoint (DPred t _ _ _) (DPred p _ _ _) = t == p-areDisjoint _ _ = True---lookup :: Eq t => NonEmpty t -> DPTrie p t x -> Maybe x-lookup (t:|ts) (DMore t' mx xs)- | t == t' = case ts of- [] -> mx- _ -> firstJust $ map (lookup $ NE.fromList ts) xs- | otherwise = Nothing-lookup (t:|ts) (DPred _ p mrx xrs) =- p t >>=- \r -> case ts of- [] -> ($ r) <$> mrx- _ -> ($ r) <$> firstJust (map (lookup $ NE.fromList ts) xrs)--lookupWithL :: Eq t => (t -> t) -> NonEmpty t -> DPTrie p t x -> Maybe x-lookupWithL f (t:|ts) (DMore t' mx xs)- | null ts = if f t == t'- then mx- else Nothing- | otherwise = if t == t'- then firstJust $ map (lookupWithL f $ NE.fromList ts) xs- else Nothing-lookupWithL f (t:|ts) (DPred _ p mrx xrs) =- p t >>=- \r -> case ts of- [] -> ($ r) <$> mrx- _ -> ($ r) <$> firstJust (map (lookupWithL f $ NE.fromList ts) xrs)--lookupNearestParent :: Eq t => NonEmpty t -> DPTrie p t x -> Maybe x-lookupNearestParent tss@(t:|ts) trie@(DMore t' mx xs) = case lookup tss trie of- Nothing -> if t == t'- then case ts of- [] -> mx -- redundant; should have successful lookup- _ -> case firstJust $ map (lookupNearestParent $ NE.fromList ts) xs of- Nothing -> mx- justr -> justr- else Nothing- justr -> justr-lookupNearestParent tss@(t:|ts) trie@(DPred t' p mrx xrs) = case lookup tss trie of- Nothing -> p t >>=- \r -> case ts of- [] -> ($ r) <$> mrx -- redundant; should have successful lookup- _ -> case firstJust $ map (lookupNearestParent $ NE.fromList ts) xrs of- Nothing -> ($ r) <$> mrx- justr -> ($ r) <$> justr- justr -> justr---firstJust :: [Maybe a] -> Maybe a-firstJust [] = Nothing-firstJust (Nothing:xs) = firstJust xs-firstJust (Just x :xs) = Just x---litSingletonTail :: NonEmpty t -> x -> DPTrie p t x-litSingletonTail (t:|[]) x = DMore t (Just x) []-litSingletonTail (t:|ts) x = DMore t Nothing [litSingletonTail (NE.fromList ts) x]---litExtrudeTail :: [t] -> DPTrie p t x -> DPTrie p t x-litExtrudeTail [] r = r-litExtrudeTail (t:ts) r = DMore t Nothing [litExtrudeTail ts r]---sort :: (Eq p, Eq t) => [DPTrie p t x] -> [DPTrie p t x]-sort = foldr insert []- where- insert :: (Eq p, Eq t) => DPTrie p t x -> [DPTrie p t x] -> [DPTrie p t x]- insert r [] = [r]- insert x@(DMore t _ _) (y@(DMore p _ _):rs)- | t == p = x : rs- | otherwise = x : y : rs- insert x@(DMore t _ _) (y@(DPred p _ _ _):rs) =- x : y : rs- insert x@(DPred t _ _ _) (y@(DPred p _ _ _):rs)- | t == p = x : rs -- basis- | otherwise = x : y : rs- insert x@(DPred t _ _ _) (y@(DMore p _ _):rs) =- y : insert x rs
+ src/Data/Trie/Pred/Step.hs view
@@ -0,0 +1,73 @@+{-# LANGUAGE+ ExistentialQuantification+ , FlexibleContexts+ , FlexibleInstances+ , MultiParamTypeClasses+ , DeriveFunctor+ #-}++module Data.Trie.Pred.Step where++import Prelude hiding (lookup)+import Data.Trie.Class+import qualified Data.Trie.Map as MT+import qualified Data.Map as Map+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.List.NonEmpty as NE++import Data.Functor.Syntax+import Data.Monoid+import Data.Maybe (fromMaybe)+++-- * Single Predicated Step++data PredStep c s a = forall r. PredStep+ { predTag :: s -- ^ Unique identifier for the predicate - used for combination+ , predPred :: s -> Maybe r+ , predData :: Maybe (r -> a)+ , predSub :: c s (r -> a)+ }++instance Functor (c s) => Functor (PredStep c s) where+ fmap f (PredStep i p mx xs) = PredStep i p (f <.$> mx) $ f <.$> xs++-- | Lookup and delete only - can't arbitrarilly construct a predicated trie.+instance Trie NonEmpty s c => Trie NonEmpty s (PredStep c) where+ lookup (t:|ts) (PredStep _ p mx xs) = do+ r <- p t+ if null ts then mx <$~> r+ else lookup (NE.fromList ts) xs <$~> r+ delete (t:|ts) xss@(PredStep i p mx xs) =+ maybe xss+ (const $ if null ts+ then PredStep i p Nothing xs+ else PredStep i p mx $ delete (NE.fromList ts) xs)+ (p t)++singletonPred :: Monoid (c s (r -> a)) => s -> (s -> Maybe r) -> (r -> a) -> PredStep c s a+singletonPred i p x = PredStep i p (Just x) mempty+++-- * Adjacent Predicated Steps++newtype PredSteps c s a = PredSteps+ { unPredSteps :: [PredStep c s a] }+ deriving (Functor)++-- | Lookup and delete only - can't arbitrarilly construct a predicated trie.+instance Trie NonEmpty s c => Trie NonEmpty s (PredSteps c) where+ lookup ts (PredSteps ps) = getFirst $ foldMap (First . lookup ts) ps+ delete ts (PredSteps ps) = PredSteps $ fmap (delete ts) ps++instance Eq s => Monoid (PredSteps c s a) where+ mempty = PredSteps []+ mappend = unionPred++-- | @Last@-style instance+unionPred :: Eq s => PredSteps c s a -> PredSteps c s a -> PredSteps c s a+unionPred (PredSteps (xss@(PredStep i p mx xs):pxs)) (PredSteps (yss@(PredStep j q my ys):pys))+ | i == j = PredSteps $ yss : unPredSteps (unionPred (PredSteps pxs) (PredSteps pys))+ | otherwise = PredSteps $ xss : yss : unPredSteps (unionPred (PredSteps pxs) (PredSteps pys))+unionPred x (PredSteps []) = x+unionPred (PredSteps []) y = y
− src/Data/Trie/Pred/Unified.hs
@@ -1,112 +0,0 @@-module Data.Trie.Pred.Unified- ( RUPTrie (..)- , UPTrie (..)- , assignLit- , showTrie- , merge- , elem- , lookup- , lookupWithL- , lookupNearestParent- , lookupThrough- , litSingleton- , litExtrude- ) where--import Prelude hiding (lookup, map, elem)-import Data.Trie.Pred.Unified.Tail (UPTrie (..), showTail)-import qualified Data.Trie.Pred.Unified.Tail as NU-import qualified Data.List.NonEmpty as NE-import Data.Monoid-import Data.Maybe-import Data.Functor.Syntax--import Test.QuickCheck---data RUPTrie t x = Rooted { root :: Maybe x- , children :: [UPTrie t x] }- deriving (Eq)--instance Functor (RUPTrie t) where- fmap = map--map :: (a -> b) -> RUPTrie t a -> RUPTrie t b-map f (Rooted mx xs) = Rooted (f <$> mx) $ f <$$> xs--instance Foldable (RUPTrie t) where- foldMap f (Rooted mx xs) = fromMaybe (foldMap (foldMap f) xs) $ f <$> mx--showTrie :: Show t => RUPTrie t x -> String-showTrie (Rooted mx xs) =- if isNothing mx- then "(NoRoot) [" ++ concatMap showTail xs ++ "] "- else "(Root) [" ++ concatMap showTail xs ++ "] "--instance (Eq t) => Monoid (RUPTrie t x) where- mempty = Rooted Nothing []- mappend = Data.Trie.Pred.Unified.merge--merge :: (Eq t) => RUPTrie t x -> RUPTrie t x -> RUPTrie t x-merge (Rooted mx xs) (Rooted my ys) =- Rooted (getLast $ Last mx <> Last my) $ NU.sort $ foldr go [] $ xs ++ ys- where- go :: (Eq t) => UPTrie t x -> [UPTrie t x] -> [UPTrie t x]- go a [] = [a]- go a (b:bs) | NU.areDisjoint a b = a : b : bs- | otherwise = NU.merge a b : bs--instance (Show t) => Show (RUPTrie t x) where- show = showTrie--instance (Arbitrary t, Arbitrary x) => Arbitrary (RUPTrie t x) where- arbitrary = do- mx <- arbitrary- xs <- arbitrary `suchThat` (\x -> length x < 10)- return $ Rooted mx xs---assignLit :: Eq t => [t] -> Maybe x -> RUPTrie t x -> RUPTrie t x-assignLit [] mx (Rooted _ ys) = Rooted mx ys-assignLit ts mx (Rooted my ys) = Rooted my $- NU.assignLit (NE.fromList ts) mx <$> ys--elem :: (Eq t) => [t] -> RUPTrie t x -> Bool-elem ts = isJust . lookup ts--lookup :: (Eq t) => [t] -> RUPTrie t x -> Maybe x-lookup [] (Rooted mx _) = mx-lookup ts (Rooted _ xs) = firstJust $ NU.lookup (NE.fromList ts) <$> xs---- | Applies @f@ to the last chunk.-lookupWithL :: (Eq t) => (t -> t) -> [t] -> RUPTrie t x -> Maybe x-lookupWithL _ [] (Rooted mx _) = mx-lookupWithL f ts (Rooted _ xs) = firstJust $ NU.lookupWithL f (NE.fromList ts) <$> xs--lookupNearestParent :: (Eq t) => [t] -> RUPTrie t x -> Maybe x-lookupNearestParent [] (Rooted mx _) = mx-lookupNearestParent ts (Rooted mx xs) =- firstJust $ (NU.lookupNearestParent (NE.fromList ts) <$> xs) ++ [mx]---- | Append contents up-to lookup path.-lookupThrough :: (Eq t) => [t] -> RUPTrie t x -> [x]-lookupThrough [] (Rooted mx _) = maybeToList mx-lookupThrough ts (Rooted mx xs) =- maybeToList mx ++ NU.firstNonEmpty (NU.lookupThrough (NE.fromList ts) <$> xs)--litSingleton :: [t] -> x -> RUPTrie t x-litSingleton [] x = Rooted (Just x) []-litSingleton ts x = Rooted Nothing [NU.litSingletonTail (NE.fromList ts) x]---litExtrude :: [t] -> RUPTrie t x -> RUPTrie t x-litExtrude [] r = r-litExtrude [t] (Rooted mx xs) = Rooted Nothing [UMore t mx xs]-litExtrude ts (Rooted mx xs) = Rooted Nothing [NU.litExtrudeTail (init ts) $- UMore (last ts) mx xs- ]---- * Utilities--firstJust :: [Maybe a] -> Maybe a-firstJust = getFirst . foldMap First
− src/Data/Trie/Pred/Unified/Tail.hs
@@ -1,261 +0,0 @@-{-# LANGUAGE- GADTs- #-}--module Data.Trie.Pred.Unified.Tail- ( UPTrie (..)- , suppliment- , tagUPTrie- , measureDepthRelative- , minDepth- , maxDepth- , showTail- , assignLit- , elem- , lookup- , lookupWithL- , lookupNearestParent- , lookupThrough- , firstNonEmpty- , merge- , areDisjoint- , litSingletonTail- , litExtrudeTail- , sort- ) where--import Prelude hiding (lookup, elem, map)-import Data.List.NonEmpty as NE hiding (map, sort, length)-import Data.Semigroup hiding (First (..), Last (..))-import Data.Monoid hiding ((<>))-import Data.Maybe-import Data.Functor.Syntax-import Control.Applicative-import Control.Monad--import Test.QuickCheck----data UPTrie t x where- UMore :: t- -> Maybe x- -> [UPTrie t x]- -> UPTrie t x- UPred :: t- -> (t -> Maybe r)- -> Maybe (r -> x)- -> [UPTrie t (r -> x)]- -> UPTrie t x----- | Given a parser and a chunk, take a trie expecting a result, and--- possibly return a reduced trie without the expectation.-suppliment :: (t -> Maybe r) -> t -> UPTrie t (r -> x) -> Maybe (UPTrie t x)-suppliment p t xrs = (xrs <~$>) <$> p t---- | Acts as a default tag value for the node-tagUPTrie :: UPTrie t x -> t-tagUPTrie (UMore t _ _) = t-tagUPTrie (UPred t _ _ _) = t---- | Measure the depth of a trie, based on the relation of other adjacent depths-measureDepthRelative :: ([Int] -> Int) -> UPTrie t x -> Int-measureDepthRelative f = go 0- where- go :: Int -> UPTrie t x -> Int- go n (UMore _ _ xs) =- f $ go (n+1) <$> xs- go n (UPred t p _ xrs) =- f $ go (n+1) <$> mapMaybe (suppliment p t) xrs--maxDepth :: UPTrie t x -> Int-maxDepth = measureDepthRelative maximum'- where- maximum' [] = 1- maximum' xs = maximum xs--minDepth :: UPTrie t x -> Int-minDepth = measureDepthRelative minimum'- where- minimum' [] = 1- minimum' xs = minimum xs--instance Functor (UPTrie t) where- fmap = map--map :: (a -> b) -> UPTrie t a -> UPTrie t b-map f (UMore t mx xs) = UMore t (f <$> mx) $ f <$$> xs-map f (UPred t p mrx xrs) = UPred t p (f <.$> mrx) $ f <.$$> xrs--instance Foldable (UPTrie t) where- foldMap f xs = fromMaybe mempty $ unwrapMonoid $ go $ f <$> xs- where- go (UMore _ mx xs') = WrapMonoid mx <> WrapMonoid (foldMap (unwrapMonoid . go) xs')- go (UPred t p mrx xrs) = WrapMonoid (mrx <*> p t) <> WrapMonoid (mconcat- (mapMaybe (\z -> (\r -> unwrapMonoid $ go $ z <~$> r) <$> p t) xrs))--instance (Eq t, Eq x) => Eq (UPTrie t x) where- (UMore s mx xs) == (UMore t my ys) = t == s && mx == my && xs == ys- (UPred s p mx xs) == (UPred t q my ys) = s == t- && (mx <*> p s) == (my <*> q t)- && (suppliment p s <$> xs) -- same children- == (suppliment q t <$> ys)- (UMore s mx xs) == (UPred t q my ys) = s == t- && mx == (my <*> q t)- && (Just <$> xs) == (suppliment q t <$> ys)- (UPred s p mx xs) == (UMore t my ys) = s == t- && (mx <*> p s) == my- && (suppliment p s <$> xs) == (Just <$> ys)--instance Eq t => Semigroup (UPTrie t x) where- (<>) = merge---- | Overwrites when similar, leaves untouched when not-merge :: (Eq t) => UPTrie t x -> UPTrie t x -> UPTrie t x-merge xx@(UMore t mx xs) (UMore p my ys)- | t == p = UMore p (getLast $ Last mx <> Last my) $ sort $ xs ++ ys- | otherwise = xx-merge xx@(UPred t _ _ _) yy@(UPred p _ _ _)- | t == p = yy -- predicate children are incompatible- | otherwise = xx-merge xx@(UMore t _ _) yy@(UPred p _ _ _)- | t == p = yy -- rightward bias- | otherwise = xx-merge xx@(UPred t _ _ _) yy@(UMore p _ _)- | t == p = yy -- rightward bias- | otherwise = xx---- | Can only generate literal examples-instance (Arbitrary t, Arbitrary x) => Arbitrary (UPTrie t x) where- arbitrary = sized go- where- go s = do- t <- arbitrary- mx <- arbitrary- xs <- if s <= 1 then return []- else scale (subtract 1) arbitrary `suchThat` (\x -> length x < 10)- return $ UMore t mx xs--showTail :: (Show t) => UPTrie t x -> String-showTail (UMore t _ xs) = "(UMore " ++ show t ++ ") [" ++ concatMap showTail xs ++ "] "-showTail (UPred t _ _ xs) = "(UPred " ++ show t ++ ") [" ++ concatMap showTail xs ++ "] "---- | Ignores contents-instance (Show t) => Show (UPTrie t x) where- show = showTail----type Path t = NonEmpty t---- | Assigns a value to literal constructors-assignLit :: (Eq t) => Path t -> Maybe x -> UPTrie t x -> UPTrie t x-assignLit (t:|ts) mx yy@(UMore p my ys)- | t == p = if null ts- then UMore p mx ys- else UMore p my $ fmap (assignLit (NE.fromList ts) mx) ys- | otherwise = yy-assignLit _ _ yy = yy---areDisjoint :: (Eq t) => UPTrie t x -> UPTrie t x -> Bool-areDisjoint (UMore t _ _) (UMore p _ _) = t /= p-areDisjoint (UPred t _ _ _) (UPred p _ _ _) = t /= p-areDisjoint (UPred t _ _ _) (UMore p _ _) = t /= p-areDisjoint (UMore t _ _) (UPred p _ _ _) = t /= p--elem :: Eq t => Path t -> UPTrie t x -> Bool-elem ts = isJust . lookup ts--lookup :: Eq t => Path t -> UPTrie t x -> Maybe x-lookup (t:|ts) (UMore t' mx xs) = do- guard (t == t')- if null ts then mx- else firstJust $ fmap (lookup $ NE.fromList ts) xs-lookup (t:|ts) (UPred _ p mrx xrs) = do- r <- p t- if null ts then mrx <~$> r- else firstJust (fmap (lookup $ NE.fromList ts) xrs) <~$> r---- | Apply a transform @f@ to the final path chunk, when matching a literal--- cell - used for eliminating file extensions in nested-routes.-lookupWithL :: Eq t => (t -> t) -> Path t -> UPTrie t x -> Maybe x-lookupWithL f (t:|ts) (UMore t' mx xs)- | null ts = do guard (f t == t')- mx- | otherwise = do guard (t == t')- firstJust $ fmap (lookupWithL f $ NE.fromList ts) xs-lookupWithL f (t:|ts) (UPred _ p mrx xrs) = do- r <- p t- if null ts then mrx <~$> r- else firstJust (fmap (lookupWithL f $ NE.fromList ts) xrs) <~$> r--lookupNearestParent :: Eq t => Path t -> UPTrie t x -> Maybe x-lookupNearestParent tss@(t:|ts) trie@(UMore t' mx xs) = firstJust- [ lookup tss trie- , do guard (t == t')- firstJust $ fmap (lookupNearestParent $ NE.fromList ts) xs ++ [mx]- ]-lookupNearestParent tss@(t:|ts) trie@(UPred _ p mrx xrs) = firstJust- [ lookup tss trie- , do r <- p t- firstJust (fmap (lookupNearestParent $ NE.fromList ts) xrs ++ [mrx]) <~$> r- ]---- | Return all nodes passed during a lookup-lookupThrough :: Eq t => Path t -> UPTrie t x -> [x]-lookupThrough (t:|ts) (UMore t' mx xs) = do- guard $ t == t'- maybeToList mx ++ (do guard $ null ts- firstNonEmpty $ fmap (lookupThrough $ NE.fromList ts) xs)-lookupThrough (t:|ts) (UPred _ p mrx xrs) =- let (left,right) = fromMaybe (Nothing,[]) $ do- r <- p t- return $ if null ts- then ( mrx <~$> r, [])- else ( mrx <~$> r- , firstNonEmpty (fmap (lookupThrough $ NE.fromList ts) xrs) <~$> r- )- in maybeToList left ++ right--firstNonEmpty :: [[a]] -> [a]-firstNonEmpty [] = []-firstNonEmpty (x:xs) | null x = firstNonEmpty xs- | otherwise = x---- | Create a singleton trie out of literal constructors-litSingletonTail :: Path t -> x -> UPTrie t x-litSingletonTail (t:|[]) x = UMore t (Just x) []-litSingletonTail (t:|ts) x = UMore t Nothing [litSingletonTail (NE.fromList ts) x]---- | Push a trie down with literal constructors-litExtrudeTail :: [t] -> UPTrie t x -> UPTrie t x-litExtrudeTail [] r = r-litExtrudeTail (t:ts) r = UMore t Nothing [litExtrudeTail ts r]----- | also does a non-deterministic merge - make sure your nodes are disjoint & clean-sort :: (Eq t) => [UPTrie t x] -> [UPTrie t x]-sort = foldr insert' []- where- insert' :: (Eq t) => UPTrie t x -> [UPTrie t x] -> [UPTrie t x]- insert' r [] = [r]- insert' x@(UMore t _ _) (y@(UMore p _ _):rs)- | t == p = x : rs- | otherwise = x : y : rs- insert' x@(UMore t _ _) (y@(UPred p _ _ _):rs)- | t == p = x : rs- | otherwise = x : y : rs- insert' x@(UPred t _ _ _) (y@(UPred p _ _ _):rs)- | t == p = x : rs- | otherwise = x : y : rs- insert' x@(UPred t _ _ _) (y@(UMore p _ _):rs)- | t == p = insert' x rs -- Puts @UPred@ at the bottom- | otherwise = y : insert' x rs----- * Utilities--firstJust :: [Maybe a] -> Maybe a-firstJust = getFirst . foldMap First
− test/Spec.hs
@@ -1,13 +0,0 @@-module Main where--import Data.Trie.Pred.UnifiedSpec--import Test.Tasty---main :: IO ()-main = defaultMain tests--tests :: TestTree-tests = testGroup "Testing..."- [unifiedSpec]