-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWordTrie.hs
More file actions
148 lines (120 loc) · 4.41 KB
/
WordTrie.hs
File metadata and controls
148 lines (120 loc) · 4.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
module WordTrie (
WordTrie(..),
insert,
insert1,
insertMany,
insertMany1,
getWordFrequency,
isWord,
getWords)
where
import Data.Function (on)
import Data.List (find,findIndex, foldl', sortBy)
import Data.Maybe (isJust, fromMaybe)
newtype WordTrie = WordTrie {nodes :: [LetterNode]}
-- CAT CAT CATS
-- C -> A -> T (2) -> S (1)
data LetterNode = LetterNode {char :: !Char, children :: ![LetterNode], wordFrequency :: !(Maybe Int), parent :: !(Maybe LetterNode)}
-- func :: LetterNode -> Bool
-- func (LetterNode c cs w p) =
-- func (Letternode b) =
type WordWithFreq = (String, Int)
instance Show WordTrie where
show w = unwords $ map show (nodes w)
instance Show LetterNode where
show l = show (char l) ++ if isJust (wordFrequency l) then "*" else "" ++ unwords (map show (children l))
insert1 :: WordTrie -> String -> WordTrie
insert1 w s = insert w (s, 1)
insert :: WordTrie -> WordWithFreq -> WordTrie
insert w ([], _) = w
insert w wwf@(str, freq)
| freq <= 0 = error "Words cannot have a frequency <= 0"
| '.' `elem` str = error "Words cannot contain '.'"
| '#' `elem` str = error "Words cannot contain '#'"
| otherwise =
let ns = insertLetterNode (nodes w) wwf Nothing
in WordTrie {
nodes = ns
}
insertMany :: WordTrie -> [WordWithFreq] -> WordTrie
insertMany = foldl' insert
insertMany1 :: WordTrie -> [String] -> WordTrie
insertMany1 = foldl' insert1
insertLetterNode :: [LetterNode] -> WordWithFreq -> Maybe LetterNode -> [LetterNode]
insertLetterNode ls ([], _) _ = ls
insertLetterNode ls ((c:cs), freq) p =
-- Consider lambda case here
case findLetterIndex ls c of
Just i ->
let
(before, n:after) = splitAt i ls
newNode = LetterNode {
char = c
, children = insertLetterNode (children n) (cs, freq) (Just newNode)
, wordFrequency = if null cs then Just freq else wordFrequency n
, parent = p
}
in
before ++ newNode : after
Nothing ->
let newNode = LetterNode {
char = c
, children = insertLetterNode [] (cs, freq) (Just newNode)
, wordFrequency = if null cs then Just freq else Nothing
, parent = p
}
in newNode : ls
findLetterIndex :: [LetterNode] -> Char -> Maybe Int
findLetterIndex ns c = findIndex (\n -> char n == c) ns
findLetter :: [LetterNode] -> Char -> Maybe LetterNode
findLetter ns c = find (\n -> char n == c) ns
isWord :: String -> WordTrie -> Bool
isWord s wt = isJust $ getWordFrequency s wt
getWordFrequency :: String -> WordTrie -> Maybe Int
getWordFrequency [] _ = Nothing
getWordFrequency [c] w = do
letterNode <- findLetter (nodes w) c
wordFrequency letterNode
getWordFrequency (c:cs) w = do
letterNode <- findLetter (nodes w) c
getWordFrequency_ letterNode cs
getWordFrequency_ :: LetterNode -> String -> Maybe Int
getWordFrequency_ l [] = wordFrequency l
getWordFrequency_ l [c] = case findLetter (children l) c of
Just n -> getWordFrequency_ n []
Nothing -> Nothing
getWordFrequency_ l (c:cs) = case findLetter (children l) c of
Just n -> getWordFrequency_ n cs
Nothing -> Nothing
-- "A..." -> [ABLE, ARIA, ...]
getWords :: String -> WordTrie -> [String]
getWords s wt =
let sort = sortBy (flip compare `on` snd)
in map fst $ sort $ getWords1 s wt
getWords1 :: String -> WordTrie -> [WordWithFreq]
getWords1 [] _ = []
getWords1 ('.':cs) w =
let leaves = concatMap (getLeaves cs) (nodes w)
in produceWords leaves
getWords1 (c:cs) w = case findLetter (nodes w) c of
Just n -> produceWords $ getLeaves cs n
Nothing -> []
produceWords :: [LetterNode] -> [WordWithFreq]
produceWords = map readUp
readUp :: LetterNode -> WordWithFreq
readUp l = case parent l of
Just p -> (reverse (char l : readUp_ p), wf)
Nothing -> ([char l], wf)
where
wf = fromMaybe (error "Can't readUp nonword.") (wordFrequency l)
readUp_ :: LetterNode -> String
readUp_ l = case parent l of
Just p -> char l : readUp_ p
Nothing -> [char l]
getLeaves :: String -> LetterNode -> [LetterNode]
getLeaves [] l = [l | isJust $ wordFrequency l]
getLeaves ('.':cs) l =
concatMap (getLeaves cs) (children l)
getLeaves (c:cs) l = case findLetter (children l) c of
Just n -> getLeaves cs n
Nothing -> []