rambling-trie 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -1
- data/README.md +23 -7
- data/Rakefile +4 -0
- data/lib/rambling/trie.rb +27 -21
- data/lib/rambling/trie/comparable.rb +3 -3
- data/lib/rambling/trie/compressible.rb +14 -0
- data/lib/rambling/trie/compressor.rb +37 -24
- data/lib/rambling/trie/configuration/properties.rb +8 -6
- data/lib/rambling/trie/configuration/provider_collection.rb +34 -16
- data/lib/rambling/trie/container.rb +156 -36
- data/lib/rambling/trie/enumerable.rb +4 -4
- data/lib/rambling/trie/nodes.rb +11 -0
- data/lib/rambling/trie/nodes/compressed.rb +115 -0
- data/lib/rambling/trie/nodes/missing.rb +10 -0
- data/lib/rambling/trie/nodes/node.rb +151 -0
- data/lib/rambling/trie/nodes/raw.rb +89 -0
- data/lib/rambling/trie/readers/plain_text.rb +1 -11
- data/lib/rambling/trie/serializers/marshal.rb +4 -4
- data/lib/rambling/trie/serializers/yaml.rb +4 -4
- data/lib/rambling/trie/serializers/zip.rb +9 -8
- data/lib/rambling/trie/version.rb +1 -1
- data/spec/assets/test_words.es_DO.txt +1 -0
- data/spec/integration/rambling/trie_spec.rb +40 -35
- data/spec/lib/rambling/trie/comparable_spec.rb +6 -15
- data/spec/lib/rambling/trie/compressor_spec.rb +88 -13
- data/spec/lib/rambling/trie/configuration/properties_spec.rb +7 -7
- data/spec/lib/rambling/trie/configuration/provider_collection_spec.rb +8 -20
- data/spec/lib/rambling/trie/container_spec.rb +159 -168
- data/spec/lib/rambling/trie/enumerable_spec.rb +12 -9
- data/spec/lib/rambling/trie/inspectable_spec.rb +11 -11
- data/spec/lib/rambling/trie/nodes/compressed_spec.rb +35 -0
- data/spec/lib/rambling/trie/nodes/node_spec.rb +7 -0
- data/spec/lib/rambling/trie/nodes/raw_spec.rb +177 -0
- data/spec/lib/rambling/trie/serializers/file_spec.rb +4 -4
- data/spec/lib/rambling/trie/serializers/marshal_spec.rb +3 -7
- data/spec/lib/rambling/trie/serializers/yaml_spec.rb +3 -7
- data/spec/lib/rambling/trie/serializers/zip_spec.rb +16 -20
- data/spec/lib/rambling/trie/stringifyable_spec.rb +7 -8
- data/spec/lib/rambling/trie_spec.rb +2 -2
- data/spec/spec_helper.rb +3 -1
- data/spec/support/config.rb +4 -0
- data/spec/support/helpers/add_word.rb +18 -0
- data/spec/support/shared_examples/{a_compressable_trie.rb → a_compressible_trie.rb} +13 -3
- data/spec/support/shared_examples/a_serializable_trie.rb +8 -6
- data/spec/support/shared_examples/a_serializer.rb +6 -0
- data/spec/{lib/rambling/trie/node_spec.rb → support/shared_examples/a_trie_node.rb} +61 -30
- data/spec/{lib/rambling/trie/compressed_node_spec.rb → support/shared_examples/a_trie_node_implementation.rb} +18 -69
- metadata +22 -15
- data/lib/rambling/trie/compressable.rb +0 -14
- data/lib/rambling/trie/compressed_node.rb +0 -120
- data/lib/rambling/trie/missing_node.rb +0 -8
- data/lib/rambling/trie/node.rb +0 -97
- data/lib/rambling/trie/raw_node.rb +0 -96
- data/spec/lib/rambling/trie/raw_node_spec.rb +0 -389
@@ -2,31 +2,14 @@ module Rambling
|
|
2
2
|
module Trie
|
3
3
|
# Wrapper on top of trie data structure.
|
4
4
|
class Container
|
5
|
-
extend ::Forwardable
|
6
5
|
include ::Enumerable
|
7
6
|
|
8
|
-
delegate [
|
9
|
-
:[],
|
10
|
-
:as_word,
|
11
|
-
:children,
|
12
|
-
:children_tree,
|
13
|
-
:compressed?,
|
14
|
-
:each,
|
15
|
-
:to_a,
|
16
|
-
:has_key?,
|
17
|
-
:inspect,
|
18
|
-
:letter,
|
19
|
-
:parent,
|
20
|
-
:size,
|
21
|
-
:to_s
|
22
|
-
] => :root
|
23
|
-
|
24
7
|
# The root node of this trie.
|
25
|
-
# @return [Node] the root node of this trie.
|
8
|
+
# @return [Nodes::Node] the root node of this trie.
|
26
9
|
attr_reader :root
|
27
10
|
|
28
11
|
# Creates a new trie.
|
29
|
-
# @param [Node] root the root node for the trie
|
12
|
+
# @param [Nodes::Node] root the root node for the trie
|
30
13
|
# @param [Compressor] compressor responsible for compressing the trie
|
31
14
|
# @yield [Container] the trie just created.
|
32
15
|
def initialize root, compressor
|
@@ -36,32 +19,53 @@ module Rambling
|
|
36
19
|
yield self if block_given?
|
37
20
|
end
|
38
21
|
|
39
|
-
# Adds a word to the trie
|
22
|
+
# Adds a word to the trie.
|
40
23
|
# @param [String] word the word to add the branch from.
|
41
|
-
# @return [Node] the just added branch's root node.
|
24
|
+
# @return [Nodes::Node] the just added branch's root node.
|
42
25
|
# @raise [InvalidOperation] if the trie is already compressed.
|
43
|
-
# @see
|
44
|
-
# @see
|
45
|
-
# @note Avoids altering the contents of the word variable.
|
26
|
+
# @see Nodes::Raw#add
|
27
|
+
# @see Nodes::Compressed#add
|
46
28
|
def add word
|
47
|
-
root.add word
|
29
|
+
root.add char_symbols word
|
48
30
|
end
|
49
31
|
|
50
|
-
#
|
51
|
-
# the
|
32
|
+
# Adds all provided words to the trie.
|
33
|
+
# @param [Array<String>] words the words to add the branch from.
|
34
|
+
# @return [Array<Nodes::Node>] the collection of nodes added.
|
35
|
+
# @raise [InvalidOperation] if the trie is already compressed.
|
36
|
+
# @see Nodes::Raw#add
|
37
|
+
# @see Nodes::Compressed#add
|
38
|
+
def concat words
|
39
|
+
words.map { |word| add word }
|
40
|
+
end
|
41
|
+
|
42
|
+
# Compresses the existing trie using redundant node elimination. Marks
|
43
|
+
# the trie as compressed. Does nothing if the trie has already been
|
44
|
+
# compressed.
|
52
45
|
# @return [Container] self
|
53
|
-
# @note
|
46
|
+
# @note This method replaces the root {Nodes::Raw Raw} node with a
|
47
|
+
# {Nodes::Compressed Compressed} version of it.
|
54
48
|
def compress!
|
55
|
-
self.root =
|
49
|
+
self.root = compress_root unless root.compressed?
|
56
50
|
self
|
57
51
|
end
|
58
52
|
|
53
|
+
# Compresses the existing trie using redundant node elimination. Returns
|
54
|
+
# a new trie with the compressed root.
|
55
|
+
# @return [Container] A new {Container} with the {Nodes::Compressed
|
56
|
+
# Compressed} root node or self if the trie has already been
|
57
|
+
# compressed.
|
58
|
+
def compress
|
59
|
+
return self if root.compressed?
|
60
|
+
Rambling::Trie::Container.new compress_root, compressor
|
61
|
+
end
|
62
|
+
|
59
63
|
# Checks if a path for a word or partial word exists in the trie.
|
60
64
|
# @param [String] word the word or partial word to look for in the trie.
|
61
65
|
# @return [Boolean] `true` if the word or partial word is found, `false`
|
62
66
|
# otherwise.
|
63
|
-
# @see
|
64
|
-
# @see
|
67
|
+
# @see Nodes::Raw#partial_word?
|
68
|
+
# @see Nodes::Compressed#partial_word?
|
65
69
|
def partial_word? word = ''
|
66
70
|
root.partial_word? word.chars
|
67
71
|
end
|
@@ -70,8 +74,8 @@ module Rambling
|
|
70
74
|
# @param [String] word the word to look for in the trie.
|
71
75
|
# @return [Boolean] `true` only if the word is found and the last
|
72
76
|
# character corresponds to a terminal node, `false` otherwise.
|
73
|
-
# @see
|
74
|
-
# @see
|
77
|
+
# @see Nodes::Raw#word?
|
78
|
+
# @see Nodes::Compressed#word?
|
75
79
|
def word? word = ''
|
76
80
|
root.word? word.chars
|
77
81
|
end
|
@@ -80,8 +84,8 @@ module Rambling
|
|
80
84
|
# @param [String] word the word to look for in the trie.
|
81
85
|
# @return [Array<String>] all the words contained in the trie that start
|
82
86
|
# with the specified characters.
|
83
|
-
# @see
|
84
|
-
# @see
|
87
|
+
# @see Nodes::Raw#scan
|
88
|
+
# @see Nodes::Compressed#scan
|
85
89
|
def scan word = ''
|
86
90
|
root.scan(word.chars).to_a
|
87
91
|
end
|
@@ -92,7 +96,7 @@ module Rambling
|
|
92
96
|
# @return [Enumerator<String>] all the words in the given string that
|
93
97
|
# match a word in the trie.
|
94
98
|
# @yield [String] each word found in phrase.
|
95
|
-
# @see Node#words_within
|
99
|
+
# @see Nodes::Node#words_within
|
96
100
|
def words_within phrase
|
97
101
|
words_within_root(phrase).to_a
|
98
102
|
end
|
@@ -113,10 +117,116 @@ module Rambling
|
|
113
117
|
root == other.root
|
114
118
|
end
|
115
119
|
|
120
|
+
# Iterates over the words contained in the trie.
|
121
|
+
# @yield [String] the words contained in this trie node.
|
122
|
+
def each
|
123
|
+
return enum_for :each unless block_given?
|
124
|
+
|
125
|
+
root.each do |word|
|
126
|
+
yield word
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# @return [String] a string representation of the container.
|
131
|
+
def inspect
|
132
|
+
"#<#{self.class.name} root: #{root.inspect}>"
|
133
|
+
end
|
134
|
+
|
135
|
+
# Get {Nodes::Node Node} corresponding to a given letter.
|
136
|
+
# @param [Symbol] letter the letter to search for in the root node.
|
137
|
+
# @return [Nodes::Node] the node corresponding to that letter.
|
138
|
+
# @see Nodes::Node#[]
|
139
|
+
def [] letter
|
140
|
+
root[letter]
|
141
|
+
end
|
142
|
+
|
143
|
+
# Root node's child nodes.
|
144
|
+
# @return [Array<Nodes::Node>] the array of children nodes contained in
|
145
|
+
# the root node.
|
146
|
+
# @see Nodes::Node#children
|
147
|
+
def children
|
148
|
+
root.children
|
149
|
+
end
|
150
|
+
|
151
|
+
# Root node's children tree.
|
152
|
+
# @return [Array<Nodes::Node>] the array of children nodes contained in
|
153
|
+
# the root node.
|
154
|
+
# @see Nodes::Node#children_tree
|
155
|
+
def children_tree
|
156
|
+
root.children_tree
|
157
|
+
end
|
158
|
+
|
159
|
+
# Indicates if the root {Nodes::Node Node} can be
|
160
|
+
# compressed or not.
|
161
|
+
# @return [Boolean] `true` for non-{Nodes::Node#terminal? terminal}
|
162
|
+
# nodes with one child, `false` otherwise.
|
163
|
+
def compressed?
|
164
|
+
root.compressed?
|
165
|
+
end
|
166
|
+
|
167
|
+
# Array of words contained in the root {Nodes::Node Node}.
|
168
|
+
# @return [Array<String>] all words contained in this trie.
|
169
|
+
# @see https://ruby-doc.org/core-2.5.0/Enumerable.html#method-i-to_a
|
170
|
+
# Enumerable#to_a
|
171
|
+
def to_a
|
172
|
+
root.to_a
|
173
|
+
end
|
174
|
+
|
175
|
+
# Check if a letter is part of the root {Nodes::Node}'s children tree.
|
176
|
+
# @param [Symbol] letter the letter to search for in the root node.
|
177
|
+
# @return [Boolean] whether the letter is contained or not.
|
178
|
+
# @see Nodes::Node#has_key?
|
179
|
+
def has_key? letter
|
180
|
+
root.has_key? letter
|
181
|
+
end
|
182
|
+
|
183
|
+
# Size of the Root {Nodes::Node Node}'s children tree.
|
184
|
+
# @return [Integer] the number of letters in the root node.
|
185
|
+
def size
|
186
|
+
root.size
|
187
|
+
end
|
188
|
+
|
189
|
+
# String representation of the current node, if it is a terminal node.
|
190
|
+
# @return [String] the string representation of the current node.
|
191
|
+
# @raise [InvalidOperation] if node is not terminal or is root.
|
192
|
+
# @deprecated This will always raise an {InvalidOperation} exception.
|
193
|
+
def as_word
|
194
|
+
warn '[DEPRECATION WARNING] `#as_word` is deprecated. Please use `#root#as_word` instead.'
|
195
|
+
root.as_word
|
196
|
+
end
|
197
|
+
|
198
|
+
# Root {Nodes::Node Node}'s letter.
|
199
|
+
# @return [Symbol] the root node's letter
|
200
|
+
# @see Nodes::Node#letter
|
201
|
+
# @deprecated This will always return `nil`.
|
202
|
+
def letter
|
203
|
+
warn '[DEPRECATION WARNING] `#letter` is deprecated. Please use `#root#letter` instead.'
|
204
|
+
root.letter
|
205
|
+
end
|
206
|
+
|
207
|
+
# Root {Nodes::Node Node}'s parent.
|
208
|
+
# @return [Symbol] the root node's parent
|
209
|
+
# @see Nodes::Node#parent
|
210
|
+
# @deprecated This will always return `nil`.
|
211
|
+
def parent
|
212
|
+
warn '[DEPRECATION WARNING] `#parent` is deprecated. Please use `#root#parent` instead.'
|
213
|
+
root.parent
|
214
|
+
end
|
215
|
+
|
216
|
+
# String representation of root {Nodes::Node Node}.
|
217
|
+
# @return [String] the root node's string representation.
|
218
|
+
# @see Stringifyable#to_s
|
219
|
+
# @deprecated This will always return an empty string (`''`).
|
220
|
+
def to_s
|
221
|
+
warn '[DEPRECATION WARNING] `#to_s` is deprecated. Please use `#root#to_s` instead.'
|
222
|
+
root.to_s
|
223
|
+
end
|
224
|
+
|
116
225
|
alias_method :include?, :word?
|
117
226
|
alias_method :match?, :partial_word?
|
118
227
|
alias_method :words, :scan
|
119
228
|
alias_method :<<, :add
|
229
|
+
alias_method :has_letter?, :has_key?
|
120
230
|
|
121
231
|
private
|
122
232
|
|
@@ -134,6 +244,16 @@ module Rambling
|
|
134
244
|
end
|
135
245
|
end
|
136
246
|
end
|
247
|
+
|
248
|
+
def compress_root
|
249
|
+
compressor.compress root
|
250
|
+
end
|
251
|
+
|
252
|
+
def char_symbols word
|
253
|
+
symbols = []
|
254
|
+
word.reverse.each_char { |c| symbols << c.to_sym }
|
255
|
+
symbols
|
256
|
+
end
|
137
257
|
end
|
138
258
|
end
|
139
259
|
end
|
@@ -4,9 +4,9 @@ module Rambling
|
|
4
4
|
module Enumerable
|
5
5
|
include ::Enumerable
|
6
6
|
|
7
|
-
# Returns number of words contained in the trie
|
8
|
-
#
|
9
|
-
#
|
7
|
+
# Returns number of words contained in the trie
|
8
|
+
# @see https://ruby-doc.org/core-2.5.0/Enumerable.html#method-i-count
|
9
|
+
# Enumerable#count
|
10
10
|
alias_method :size, :count
|
11
11
|
|
12
12
|
# Iterates over the words contained in the trie.
|
@@ -16,7 +16,7 @@ module Rambling
|
|
16
16
|
|
17
17
|
yield as_word if terminal?
|
18
18
|
|
19
|
-
|
19
|
+
children_tree.each_value do |child|
|
20
20
|
child.each do |word|
|
21
21
|
yield word
|
22
22
|
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Rambling
|
2
|
+
module Trie
|
3
|
+
module Nodes
|
4
|
+
# A representation of a node in an compressed trie data structure.
|
5
|
+
class Compressed < Rambling::Trie::Nodes::Node
|
6
|
+
# Always raises {Rambling::Trie::InvalidOperation InvalidOperation} when
|
7
|
+
# trying to add a word to the current compressed trie node
|
8
|
+
# @param [String] word the word to add to the trie.
|
9
|
+
# @raise [InvalidOperation] if the trie is already compressed.
|
10
|
+
# @return [nil] this never returns as it always raises an exception.
|
11
|
+
def add word
|
12
|
+
raise Rambling::Trie::InvalidOperation, 'Cannot add word to compressed trie'
|
13
|
+
end
|
14
|
+
|
15
|
+
# Checks if a path for set a of characters exists in the trie.
|
16
|
+
# @param [Array<String>] chars the characters to look for in the trie.
|
17
|
+
# @return [Boolean] `true` if the characters are found, `false` otherwise.
|
18
|
+
def partial_word? chars
|
19
|
+
chars.empty? || has_partial_word?(chars)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Checks if a path for set of characters represents a word in the trie.
|
23
|
+
# @param [Array<String>] chars the characters to look for in the trie.
|
24
|
+
# @return [Boolean] `true` if the characters are found and form a word,
|
25
|
+
# `false` otherwise.
|
26
|
+
def word? chars
|
27
|
+
chars.empty? ? terminal? : has_word?(chars)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Always return `true` for a compressed node.
|
31
|
+
# @return [Boolean] always `true` for a compressed node.
|
32
|
+
def compressed?
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def has_partial_word? chars
|
39
|
+
recursive_get(:partial_word?, chars) || false
|
40
|
+
end
|
41
|
+
|
42
|
+
def has_word? chars
|
43
|
+
current_key = nil
|
44
|
+
|
45
|
+
while !chars.empty?
|
46
|
+
if current_key
|
47
|
+
current_key << chars.slice!(0)
|
48
|
+
else
|
49
|
+
current_key = chars.slice!(0)
|
50
|
+
end
|
51
|
+
|
52
|
+
child = children_tree[current_key.to_sym]
|
53
|
+
return child.word? chars if child
|
54
|
+
end
|
55
|
+
|
56
|
+
false
|
57
|
+
end
|
58
|
+
|
59
|
+
def closest_node chars
|
60
|
+
recursive_get(:scan, chars) || Rambling::Trie::Nodes::Missing.new
|
61
|
+
end
|
62
|
+
|
63
|
+
def children_match_prefix chars
|
64
|
+
return enum_for :children_match_prefix, chars unless block_given?
|
65
|
+
|
66
|
+
current_key = nil
|
67
|
+
|
68
|
+
while !chars.empty?
|
69
|
+
if current_key
|
70
|
+
current_key << chars.slice!(0)
|
71
|
+
else
|
72
|
+
current_key = chars.slice!(0)
|
73
|
+
end
|
74
|
+
|
75
|
+
child = children_tree[current_key.to_sym]
|
76
|
+
|
77
|
+
next unless child
|
78
|
+
|
79
|
+
child.match_prefix chars do |word|
|
80
|
+
yield word
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def recursive_get method, chars
|
86
|
+
current_length = 0
|
87
|
+
current_key = current_key chars.slice!(0)
|
88
|
+
|
89
|
+
begin
|
90
|
+
current_length += 1
|
91
|
+
|
92
|
+
if current_key && (current_key.length == current_length || chars.empty?)
|
93
|
+
return children_tree[current_key.to_sym].send method, chars
|
94
|
+
end
|
95
|
+
end while current_key && current_key[current_length] == chars.slice!(0)
|
96
|
+
end
|
97
|
+
|
98
|
+
def current_key letter
|
99
|
+
current_key = nil
|
100
|
+
|
101
|
+
children_tree.each_key do |letters|
|
102
|
+
letters_string = letters.to_s
|
103
|
+
|
104
|
+
if letters_string.start_with? letter
|
105
|
+
current_key = letters_string
|
106
|
+
break
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
current_key
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module Rambling
|
2
|
+
module Trie
|
3
|
+
module Nodes
|
4
|
+
# A representation of a node in the trie data structure.
|
5
|
+
class Node
|
6
|
+
include Rambling::Trie::Compressible
|
7
|
+
include Rambling::Trie::Enumerable
|
8
|
+
include Rambling::Trie::Comparable
|
9
|
+
include Rambling::Trie::Stringifyable
|
10
|
+
include Rambling::Trie::Inspectable
|
11
|
+
|
12
|
+
# @overload letter
|
13
|
+
# Letter(s) corresponding to the current node.
|
14
|
+
# @overload letter=(letter)
|
15
|
+
# Sets the letter(s) corresponding to the current node. Ensures the
|
16
|
+
# {Node#letter #letter} in the {Node#parent #parent}'s
|
17
|
+
# {Node#children_tree #children_tree} is updated.
|
18
|
+
# @param [String, Symbol, nil] letter the letter value.
|
19
|
+
# @return [Symbol, nil] the corresponding letter(s).
|
20
|
+
attr_reader :letter
|
21
|
+
|
22
|
+
# Child nodes tree.
|
23
|
+
# @return [Hash] the children_tree hash, consisting of `:letter => node`.
|
24
|
+
attr_accessor :children_tree
|
25
|
+
|
26
|
+
# Parent node.
|
27
|
+
# @return [Node, nil] the parent of the current node.
|
28
|
+
attr_accessor :parent
|
29
|
+
|
30
|
+
# Creates a new node.
|
31
|
+
# @param [Symbol, nil] letter the Node's letter value
|
32
|
+
# @param [Node, nil] parent the parent of the current node.
|
33
|
+
def initialize letter = nil, parent = nil, children_tree = {}
|
34
|
+
@letter = letter
|
35
|
+
@parent = parent
|
36
|
+
@children_tree = children_tree
|
37
|
+
end
|
38
|
+
|
39
|
+
# Child nodes.
|
40
|
+
# @return [Array<Node>] the array of children nodes contained
|
41
|
+
# in the current node.
|
42
|
+
def children
|
43
|
+
children_tree.values
|
44
|
+
end
|
45
|
+
|
46
|
+
# First child node.
|
47
|
+
# @return [Node, nil] the first child contained in the current node.
|
48
|
+
def first_child
|
49
|
+
return if children_tree.empty?
|
50
|
+
|
51
|
+
children_tree.each_value do |child|
|
52
|
+
return child
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Indicates if the current node is the root node.
|
57
|
+
# @return [Boolean] `true` if the node does not have a parent, `false`
|
58
|
+
# otherwise.
|
59
|
+
def root?
|
60
|
+
!parent
|
61
|
+
end
|
62
|
+
|
63
|
+
# Indicates if a {Node Node} is terminal or not.
|
64
|
+
# @return [Boolean] `true` for terminal nodes, `false` otherwise.
|
65
|
+
def terminal?
|
66
|
+
!!terminal
|
67
|
+
end
|
68
|
+
|
69
|
+
# Mark {Node Node} as terminal.
|
70
|
+
# @return [Node] the modified node.
|
71
|
+
def terminal!
|
72
|
+
self.terminal = true
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
def letter= letter
|
77
|
+
@letter = letter.to_sym if letter
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns the node that starts with the specified characters.
|
81
|
+
# @param [Array<String>] chars the characters to look for in the trie.
|
82
|
+
# @return [Node] the node that matches the specified characters.
|
83
|
+
# {Missing Missing} when not found.
|
84
|
+
def scan chars
|
85
|
+
return self if chars.empty?
|
86
|
+
|
87
|
+
closest_node chars
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns all words that match a prefix of any length within chars.
|
91
|
+
# @param [String] chars the chars to base the prefix on.
|
92
|
+
# @return [Enumerator<String>] all the words that match a prefix given by
|
93
|
+
# chars.
|
94
|
+
# @yield [String] each word found.
|
95
|
+
def match_prefix chars
|
96
|
+
return enum_for :match_prefix, chars unless block_given?
|
97
|
+
|
98
|
+
yield as_word if terminal?
|
99
|
+
children_match_prefix chars do |word|
|
100
|
+
yield word
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Get {Node Node} corresponding to a given letter.
|
105
|
+
# @param [Symbol] letter the letter to search for in the node.
|
106
|
+
# @return [Node] the node corresponding to that letter.
|
107
|
+
# @see https://ruby-doc.org/core-2.5.0/Hash.html#method-i-5B-5D
|
108
|
+
# Hash#[]
|
109
|
+
def [] letter
|
110
|
+
children_tree[letter]
|
111
|
+
end
|
112
|
+
|
113
|
+
# Set the {Node Node} that corresponds to a given letter.
|
114
|
+
# @param [Symbol] letter the letter to insert or update in the node's
|
115
|
+
# @param [Node] node the {Node Node} to assign to that letter.
|
116
|
+
# @return [Node] the node corresponding to the inserted or
|
117
|
+
# updated letter.
|
118
|
+
# @see https://ruby-doc.org/core-2.5.0/Hash.html#method-i-5B-5D
|
119
|
+
# Hash#[]
|
120
|
+
def []= letter, node
|
121
|
+
children_tree[letter] = node
|
122
|
+
end
|
123
|
+
|
124
|
+
# Check if a {Node Node}'s children tree contains a given
|
125
|
+
# letter.
|
126
|
+
# @param [Symbol] letter the letter to search for in the node.
|
127
|
+
# @return [Boolean] `true` if the letter is present, `false` otherwise
|
128
|
+
# @see https://ruby-doc.org/core-2.5.0/Hash.html#method-i-has_key-3F
|
129
|
+
# Hash#has_key?
|
130
|
+
def has_key? letter
|
131
|
+
children_tree.has_key? letter
|
132
|
+
end
|
133
|
+
|
134
|
+
# Delete a given letter and its corresponding {Node Node} from
|
135
|
+
# this {Node Node}'s children tree.
|
136
|
+
# @param [Symbol] letter the letter to delete from the node's children
|
137
|
+
# tree.
|
138
|
+
# @return [Node] the node corresponding to the deleted letter.
|
139
|
+
# @see https://ruby-doc.org/core-2.5.0/Hash.html#method-i-delete
|
140
|
+
# Hash#delete
|
141
|
+
def delete letter
|
142
|
+
children_tree.delete letter
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
attr_accessor :terminal
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|