3
3
{-# HLINT ignore "Use camelCase" #-}
4
4
{-# LANGUAGE TupleSections #-}
5
5
{-# LANGUAGE DeriveGeneric #-}
6
+ {-# LANGUAGE GeneralizedNewtypeDeriving #-}
6
7
-- TODO: document
7
8
module Data.PrioTrie
8
9
( PrioTrie
@@ -12,21 +13,22 @@ module Data.PrioTrie
12
13
)
13
14
where
14
15
15
- import qualified Data.ByteString as BS
16
16
import qualified Data.List.NonEmpty as NE
17
17
import qualified Data.IntMap.Strict as IMap
18
18
import qualified Data.Map.Strict as Map
19
- import Data.Word (Word8 )
20
19
import qualified Data.Set as Set
21
20
import Data.Maybe (fromJust )
22
21
import Data.List (sortOn )
23
22
import Data.Ord (Down (Down ))
24
23
import Control.DeepSeq (NFData )
25
24
import GHC.Generics (Generic , Generic1 )
25
+ import qualified Data.Char
26
+ import Data.Bifunctor (first )
27
+ import qualified Data.Text as T
26
28
27
29
data PrioTrie prio a
28
30
= PrioTrie_Node
29
- ! (IMap. IntMap (PrioTrie prio a ))
31
+ ! (CharMap (PrioTrie prio a ))
30
32
-- ^ Children
31
33
! (NE. NonEmpty (prio , a ))
32
34
-- ^ List containing /all/ items that match the prefix reached through the 'IntMap'.
@@ -36,32 +38,32 @@ data PrioTrie prio a
36
38
37
39
instance (NFData prio , NFData a ) => NFData (PrioTrie prio a )
38
40
39
- -- | Construct a 'PrioTrie' from a list of items and their priorities, deriving each 'BS.ByteString ' key from the item
41
+ -- | Construct a 'PrioTrie' from a list of items and their priorities, deriving each 'T.Text ' key from the item
40
42
fromListDeriveKey
41
43
:: forall prio a .
42
44
Ord prio
43
45
=> (NE. NonEmpty (prio , a ) -> NE. NonEmpty (prio , a ))
44
46
-- ^ Modify the sorted list stored at each 'PrioTrie_Node'.
45
47
-- Used to e.g. limit the number of items in the list.
46
- -> (a -> BS. ByteString )
47
- -- ^ Derive a 'BS.ByteString ' from the item (@a@) stored in the 'PrioTrie'
48
+ -> (a -> T. Text )
49
+ -- ^ Derive a 'T.Text ' from the item (@a@) stored in the 'PrioTrie'
48
50
-> NE. NonEmpty (prio , a )
49
51
-> PrioTrie prio a
50
52
fromListDeriveKey modify deriveKey lst =
51
53
fromList modify $ NE. map (\ prioAndA -> (deriveKey $ snd prioAndA, prioAndA)) lst
52
54
53
- -- | Construct a 'PrioTrie' from a list of items and their priorities with the given 'BS.ByteString ' as key.
55
+ -- | Construct a 'PrioTrie' from a list of items and their priorities with the given 'T.Text ' as key.
54
56
fromList
55
57
:: forall prio a .
56
58
Ord prio
57
59
=> (NE. NonEmpty (prio , a ) -> NE. NonEmpty (prio , a ))
58
60
-- ^ Modify the sorted list stored at each 'PrioTrie_Node'.
59
61
-- Used to e.g. limit the number of items in the list.
60
- -> NE. NonEmpty (BS. ByteString , (prio , a ))
62
+ -> NE. NonEmpty (T. Text , (prio , a ))
61
63
-> PrioTrie prio a
62
64
fromList modify lst =
63
65
let
64
- initMap :: Map. Map BS. ByteString (NE. NonEmpty (prio , a )) -- unsorted!
66
+ initMap :: Map. Map T. Text (NE. NonEmpty (prio , a )) -- unsorted!
65
67
-- ByteString -> [(144, Data.Internal.ByteString), (37, Data.Internal.Lazy.ByteString)]
66
68
-- String -> [(12344, Data.String.String)]
67
69
-- Tree -> [(13, Data.Tree.Tree), (7, Data.Tree.Special.Tree)]
@@ -71,63 +73,76 @@ fromList modify lst =
71
73
(map (fmap NE. singleton) (NE. toList lst))
72
74
73
75
mkUnconsMap
74
- :: Map. Map BS. ByteString (NE. NonEmpty (prio , a ))
75
- -> Map. Map (Word8 , BS. ByteString ) (NE. NonEmpty (prio , a ))
76
+ :: Map. Map T. Text (NE. NonEmpty (prio , a ))
77
+ -> Map. Map (Char , T. Text ) (NE. NonEmpty (prio , a ))
76
78
mkUnconsMap map' =
77
79
Map. mapKeys fromJust $ -- safe because all 'Nothing' keys have been removed
78
80
Map. withoutKeys
79
- (Map. mapKeys BS . uncons map')
81
+ (Map. mapKeys T . uncons map')
80
82
(Set. singleton Nothing )
81
83
82
84
mkPrefixToList
83
- :: Map. Map (Word8 , BS. ByteString ) (NE. NonEmpty (prio , a )) -- output of 'mkUnconsMap'
84
- -> Map. Map Word8 (NE. NonEmpty (BS. ByteString , NE. NonEmpty (prio , a ))) -- input to 'fromList' for each Word8 -prefix
85
+ :: Map. Map (Char , T. Text ) (NE. NonEmpty (prio , a )) -- output of 'mkUnconsMap'
86
+ -> Map. Map Char (NE. NonEmpty (T. Text , NE. NonEmpty (prio , a ))) -- input to 'fromList' for each Char -prefix
85
87
mkPrefixToList =
86
88
Map. fromListWith (<>) -- TODO: performance
87
- . map (\ ((w8, bs ), ne) -> (w8 , NE. singleton (bs , ne)))
89
+ . map (\ ((char, txt ), ne) -> (char , NE. singleton (txt , ne)))
88
90
. Map. toList
89
91
90
- mkIntMap
91
- :: Map. Map Word8 (NE. NonEmpty (BS. ByteString , NE. NonEmpty (prio , a ))) -- output of 'mkPrefixToList'
92
- -> IMap. IntMap (PrioTrie prio a ) -- first argument to 'PrioTrie_Node'
93
- mkIntMap =
94
- IMap. fromList
92
+ mkCharMap
93
+ :: Map. Map Char (NE. NonEmpty (T. Text , NE. NonEmpty (prio , a ))) -- output of 'mkPrefixToList'
94
+ -> CharMap (PrioTrie prio a ) -- first argument to 'PrioTrie_Node'
95
+ mkCharMap =
96
+ fromListCharMap
95
97
. Map. toList
96
98
. Map. map (fromList modify)
97
- . Map. map (NE. fromList . concatMap (\ (bs, ne) -> map (bs,) (NE. toList ne)) . NE. toList)
98
- . Map. mapKeys fromIntegral
99
+ . Map. map (NE. fromList . concatMap (\ (txt, ne) -> map (txt,) (NE. toList ne)) . NE. toList)
99
100
100
- intMap :: IMap. IntMap (PrioTrie prio a )
101
- intMap =
102
- mkIntMap $ mkPrefixToList $ mkUnconsMap initMap
101
+ charMap :: CharMap (PrioTrie prio a )
102
+ charMap =
103
+ mkCharMap $ mkPrefixToList $ mkUnconsMap initMap
103
104
104
105
-- the non-empty input list sorted by priority (descending)
105
106
nonEmpty = modify $
106
107
NE. fromList . sortOn (Down . fst ) $ map snd (NE. toList lst)
107
108
108
- in PrioTrie_Node intMap nonEmpty
109
+ in PrioTrie_Node charMap nonEmpty
109
110
110
111
-- | Convert a 'PrioTrie' to a list of keys and items (along with priority)
111
112
-- given the @deriveKey@ function used to construct the 'PrioTrie' using 'fromListDeriveKey'
112
113
toListDeriveKey
113
- :: (a -> BS. ByteString )
114
- -- ^ Derive a 'BS.ByteString ' from the item (@a@) stored in the 'PrioTrie'
114
+ :: (a -> T. Text )
115
+ -- ^ Derive a 'T.Text ' from the item (@a@) stored in the 'PrioTrie'
115
116
-> PrioTrie prio a
116
- -> NE. NonEmpty (BS. ByteString , (prio , a ))
117
+ -> NE. NonEmpty (T. Text , (prio , a ))
117
118
toListDeriveKey deriveKey (PrioTrie_Node _ neList) =
118
119
NE. map (\ prioAndA -> (deriveKey $ snd prioAndA, prioAndA)) neList
119
120
120
121
prefixLookup
121
122
:: PrioTrie prio a
122
- -> BS. ByteString
123
+ -> T. Text
123
124
-> Maybe (NE. NonEmpty (prio , a ))
124
- prefixLookup initTrie initBs =
125
- let go (PrioTrie_Node intMap neList) bs =
126
- case BS . uncons bs of
127
- Nothing -> -- end of 'bs' reached
125
+ prefixLookup initTrie prefix =
126
+ let go (PrioTrie_Node charMap neList) prefix' =
127
+ case T . uncons prefix' of
128
+ Nothing -> -- end of prefix reached
128
129
Just neList
129
- Just (w8, bsRem ) -> --
130
- case IMap. lookup ( fromIntegral w8) intMap of
131
- Just trie' -> go trie' bsRem
130
+ Just (char, prefixRem ) -> --
131
+ case lookupCharMap char charMap of
132
+ Just trie' -> go trie' prefixRem
132
133
Nothing -> Nothing
133
- in go initTrie initBs
134
+ in go initTrie prefix
135
+
136
+ -- ####################
137
+ -- ## Helpers ##
138
+ -- ####################
139
+
140
+ -- | A map from a unicode character ('Char') to something
141
+ newtype CharMap a = CharMap { unCharMap :: IMap. IntMap a }
142
+ deriving (Eq , Show , Functor , Generic , Generic1 , NFData )
143
+
144
+ lookupCharMap :: Char -> CharMap a -> Maybe a
145
+ lookupCharMap c = IMap. lookup (Data.Char. ord c) . unCharMap
146
+
147
+ fromListCharMap :: [(Char , a )] -> CharMap a
148
+ fromListCharMap = CharMap . IMap. fromList . map (first Data.Char. ord)
0 commit comments