Skip to content

Commit 94cf616

Browse files
committed
Convert prefixtree to a generic implementation
Value types are now generic instead of relying on type assertions.
1 parent 3e5091e commit 94cf616

File tree

4 files changed

+69
-66
lines changed

4 files changed

+69
-66
lines changed

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ The following code adds strings and associated data (in this case an integer)
1616
to a prefix tree.
1717

1818
```go
19-
tree := prefixtree.New()
19+
tree := prefixtree.New[int]()
2020

2121
tree.Add("apple", 10)
2222
tree.Add("orange", 20)
@@ -61,20 +61,20 @@ Output:
6161
```
6262
prefix value error
6363
------ ----- -----
64-
a <nil> prefixtree: prefix ambiguous
65-
appl <nil> prefixtree: prefix ambiguous
64+
a 0 prefixtree: prefix ambiguous
65+
appl 0 prefixtree: prefix ambiguous
6666
apple 10 <nil>
6767
apple p 30 <nil>
6868
apple pie 30 <nil>
69-
apple pies <nil> prefixtree: prefix not found
69+
apple pies 0 prefixtree: prefix not found
7070
o 20 <nil>
7171
orang 20 <nil>
7272
orange 20 <nil>
73-
oranges <nil> prefixtree: prefix not found
74-
l <nil> prefixtree: prefix ambiguous
75-
lemo <nil> prefixtree: prefix ambiguous
73+
oranges 0 prefixtree: prefix not found
74+
l 0 prefixtree: prefix ambiguous
75+
lemo 0 prefixtree: prefix ambiguous
7676
lemon 40 <nil>
7777
lemon m 50 <nil>
7878
lemon meringue 50 <nil>
79-
pear <nil> prefixtree: prefix not found
79+
pear 0 prefixtree: prefix not found
8080
```

example_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
func ExampleTree_usage() {
1515
// Create the tree. Add 5 strings, each with an associated
1616
// integer.
17-
tree := prefixtree.New()
17+
tree := prefixtree.New[int]()
1818
for i, s := range []string{
1919
"apple",
2020
"orange",
@@ -53,19 +53,19 @@ func ExampleTree_usage() {
5353
// Output:
5454
// prefix value error
5555
// ------ ----- -----
56-
// a <nil> prefixtree: prefix ambiguous
57-
// appl <nil> prefixtree: prefix ambiguous
56+
// a 0 prefixtree: prefix ambiguous
57+
// appl 0 prefixtree: prefix ambiguous
5858
// apple 0 <nil>
5959
// apple p 2 <nil>
6060
// apple pie 2 <nil>
61-
// apple pies <nil> prefixtree: prefix not found
61+
// apple pies 0 prefixtree: prefix not found
6262
// o 1 <nil>
6363
// orang 1 <nil>
6464
// orange 1 <nil>
65-
// oranges <nil> prefixtree: prefix not found
66-
// lemo <nil> prefixtree: prefix ambiguous
65+
// oranges 0 prefixtree: prefix not found
66+
// lemo 0 prefixtree: prefix ambiguous
6767
// lemon 4 <nil>
6868
// lemon m 3 <nil>
6969
// lemon meringue 3 <nil>
70-
// lemon meringues <nil> prefixtree: prefix not found
70+
// lemon meringues 0 prefixtree: prefix not found
7171
}

prefixtree.go

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -25,43 +25,44 @@ var (
2525
ErrPrefixAmbiguous = errors.New("prefixtree: prefix ambiguous")
2626
)
2727

28-
// A KeyValue type encapsulates a key string and its associated value.
29-
type KeyValue struct {
28+
// A KeyValue type encapsulates a key string and its associated value of type
29+
// V.
30+
type KeyValue[V any] struct {
3031
Key string
31-
Value any
32+
Value V
3233
}
3334

3435
// A Tree represents a prefix tree containing strings and their associated
35-
// value data. The tree is implemented as a trie and can be searched
36+
// value data of type V. The tree is implemented as a trie and can be searched
3637
// efficiently for unique prefix matches.
37-
type Tree struct {
38+
type Tree[V any] struct {
3839
key string
39-
value any
40-
links []link
40+
value V
41+
links []link[V]
4142
descendants int
4243
}
4344

44-
type link struct {
45+
type link[V any] struct {
4546
keyseg string
46-
tree *Tree
47+
tree *Tree[V]
4748
}
4849

49-
// New returns an empty prefix tree.
50-
func New() *Tree {
51-
return new(Tree)
50+
// New returns an empty prefix tree with a value type of V.
51+
func New[V any]() *Tree[V] {
52+
return new(Tree[V])
5253
}
5354

5455
// isTerminal returns true if the tree is a terminal subtree in the
5556
// prefix tree.
56-
func (t *Tree) isTerminal() bool {
57+
func (t *Tree[V]) isTerminal() bool {
5758
return t.key != ""
5859
}
5960

6061
// FindKey searches the prefix tree for a key string that uniquely matches the
6162
// prefix. If found, the full matching key is returned. If not found,
6263
// ErrPrefixNotFound is returned. If the prefix matches more than one key in
6364
// the tree, ErrPrefixAmbiguous is returned.
64-
func (t *Tree) FindKey(prefix string) (key string, err error) {
65+
func (t *Tree[V]) FindKey(prefix string) (key string, err error) {
6566
st, err := t.findSubtree(prefix)
6667
if err != nil {
6768
return "", err
@@ -74,17 +75,17 @@ func (t *Tree) FindKey(prefix string) (key string, err error) {
7475
// value is returned. If not found, ErrPrefixNotFound is returned. If the
7576
// prefix matches more than one key in the tree, ErrPrefixAmbiguous is
7677
// returned.
77-
func (t *Tree) FindKeyValue(prefix string) (kv KeyValue, err error) {
78+
func (t *Tree[V]) FindKeyValue(prefix string) (kv KeyValue[V], err error) {
7879
st, err := t.findSubtree(prefix)
7980
if err != nil {
80-
return KeyValue{}, err
81+
return KeyValue[V]{}, err
8182
}
82-
return KeyValue{st.key, st.value}, nil
83+
return KeyValue[V]{st.key, st.value}, nil
8384
}
8485

8586
// FindKeys searches the prefix tree for all key strings prefixed by the
8687
// provided prefix and returns them.
87-
func (t *Tree) FindKeys(prefix string) (keys []string) {
88+
func (t *Tree[V]) FindKeys(prefix string) (keys []string) {
8889
st, err := t.findSubtree(prefix)
8990
if err == ErrPrefixNotFound {
9091
return []string{}
@@ -99,43 +100,44 @@ func (t *Tree) FindKeys(prefix string) (keys []string) {
99100
// the prefix. If found, the value associated with the key is returned. If not
100101
// found, ErrPrefixNotFound is returned. If the prefix matches more than one
101102
// key in the tree, ErrPrefixAmbiguous is returned.
102-
func (t *Tree) FindValue(prefix string) (value any, err error) {
103+
func (t *Tree[V]) FindValue(prefix string) (value V, err error) {
103104
st, err := t.findSubtree(prefix)
104105
if err != nil {
105-
return nil, err
106+
var empty V
107+
return empty, err
106108
}
107109
return st.value, nil
108110
}
109111

110112
// FindKeyValues searches the prefix tree for all key strings prefixed by the
111113
// provided prefix. All discovered keys and their values are returned.
112-
func (t *Tree) FindKeyValues(prefix string) (values []KeyValue) {
114+
func (t *Tree[V]) FindKeyValues(prefix string) (values []KeyValue[V]) {
113115
st, err := t.findSubtree(prefix)
114116
if err == ErrPrefixNotFound {
115-
return []KeyValue{}
117+
return []KeyValue[V]{}
116118
}
117119
if st.isTerminal() && err != ErrPrefixAmbiguous {
118-
return []KeyValue{{st.key, st.value}}
120+
return []KeyValue[V]{{st.key, st.value}}
119121
}
120122
return appendDescendantKeyValues(st, nil)
121123
}
122124

123125
// FindValues searches the prefix tree for all key strings prefixed by the
124126
// provided prefix. All associated values are returned.
125-
func (t *Tree) FindValues(prefix string) (values []any) {
127+
func (t *Tree[V]) FindValues(prefix string) (values []V) {
126128
st, err := t.findSubtree(prefix)
127129
if err == ErrPrefixNotFound {
128-
return []any{}
130+
return []V{}
129131
}
130132
if st.isTerminal() && err != ErrPrefixAmbiguous {
131-
return []any{st.value}
133+
return []V{st.value}
132134
}
133135
return appendDescendantValues(st, nil)
134136
}
135137

136138
// findSubtree searches the prefix tree for the deepest subtree matching
137139
// the prefix.
138-
func (t *Tree) findSubtree(prefix string) (*Tree, error) {
140+
func (t *Tree[V]) findSubtree(prefix string) (*Tree[V], error) {
139141
outerLoop:
140142
for {
141143
// Ran out of prefix?
@@ -201,7 +203,7 @@ func matchingChars(s1, s2 string) int {
201203

202204
// appendDescendantKeys recursively appends a tree's descendant keys
203205
// to an array of keys.
204-
func appendDescendantKeys(t *Tree, keys []string) []string {
206+
func appendDescendantKeys[V any](t *Tree[V], keys []string) []string {
205207
if t.isTerminal() {
206208
keys = append(keys, t.key)
207209
}
@@ -213,9 +215,9 @@ func appendDescendantKeys(t *Tree, keys []string) []string {
213215

214216
// appendDescendantKeyValues recursively appends a tree's descendant keys
215217
// to an array of key/value pairs.
216-
func appendDescendantKeyValues(t *Tree, kv []KeyValue) []KeyValue {
218+
func appendDescendantKeyValues[V any](t *Tree[V], kv []KeyValue[V]) []KeyValue[V] {
217219
if t.isTerminal() {
218-
kv = append(kv, KeyValue{t.key, t.value})
220+
kv = append(kv, KeyValue[V]{t.key, t.value})
219221
}
220222
for i := 0; i < len(t.links); i++ {
221223
kv = appendDescendantKeyValues(t.links[i].tree, kv)
@@ -225,7 +227,7 @@ func appendDescendantKeyValues(t *Tree, kv []KeyValue) []KeyValue {
225227

226228
// appendDescendantValues recursively appends a tree's descendant values
227229
// to an array of values.
228-
func appendDescendantValues(t *Tree, values []any) []any {
230+
func appendDescendantValues[V any](t *Tree[V], values []V) []V {
229231
if t.isTerminal() {
230232
values = append(values, t.value)
231233
}
@@ -236,7 +238,7 @@ func appendDescendantValues(t *Tree, values []any) []any {
236238
}
237239

238240
// Add a key string and its associated value data to the prefix tree.
239-
func (t *Tree) Add(key string, value any) {
241+
func (t *Tree[V]) Add(key string, value V) {
240242
k := key
241243
outerLoop:
242244
for {
@@ -255,7 +257,7 @@ outerLoop:
255257

256258
// Check the links before and after the insertion point for a matching
257259
// prefix to see if we need to split one of them.
258-
var splitLink *link
260+
var splitLink *link[V]
259261
var splitIndex int
260262
innerLoop:
261263
for li, lm := max(ix-1, 0), min(ix, len(t.links)-1); li <= lm; li++ {
@@ -275,24 +277,25 @@ outerLoop:
275277

276278
// No split necessary, so insert a new link and subtree.
277279
if splitLink == nil {
278-
child := &Tree{
280+
child := &Tree[V]{
279281
key: key,
280282
value: value,
281283
links: nil,
282284
descendants: 1,
283285
}
284286
t.links = append(t.links[:ix],
285-
append([]link{{k, child}}, t.links[ix:]...)...)
287+
append([]link[V]{{k, child}}, t.links[ix:]...)...)
286288
break outerLoop
287289
}
288290

289291
// A split is necessary, so split the current link's string and insert
290292
// a child tree.
291293
k1, k2 := splitLink.keyseg[:splitIndex], splitLink.keyseg[splitIndex:]
292-
child := &Tree{
294+
var empty V
295+
child := &Tree[V]{
293296
key: "",
294-
value: nil,
295-
links: []link{{k2, splitLink.tree}},
297+
value: empty,
298+
links: []link[V]{{k2, splitLink.tree}},
296299
descendants: splitLink.tree.descendants,
297300
}
298301
splitLink.keyseg, splitLink.tree = k1, child
@@ -302,11 +305,11 @@ outerLoop:
302305

303306
// Output the structure of the tree to stdout. This function exists for
304307
// debugging purposes.
305-
func (t *Tree) Output() {
308+
func (t *Tree[V]) Output() {
306309
t.outputNode(0)
307310
}
308311

309-
func (t *Tree) outputNode(level int) {
312+
func (t *Tree[V]) outputNode(level int) {
310313
fmt.Printf("%sNode: key=\"%s\" term=%v desc=%d value=%v\n",
311314
strings.Repeat(" ", level), t.key, t.isTerminal(), t.descendants, t.value)
312315
for i, l := range t.links {

prefixtree_test.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,21 @@ import (
1313

1414
type entry struct {
1515
key string
16-
value any
16+
value int
1717
}
1818

1919
type testcase struct {
2020
key string
21-
value any
21+
value int
2222
err error
2323
}
2424

25-
func test(t *testing.T, entries []entry, cases []testcase) *Tree {
25+
func test(t *testing.T, entries []entry, cases []testcase) *Tree[int] {
2626
// Run 256 iterations of build/find using random tree entry
2727
// insertion orders.
28-
var tree *Tree
28+
var tree *Tree[int]
2929
for i := 0; i < 256; i++ {
30-
tree = New()
30+
tree = New[int]()
3131
for _, i := range rand.Perm(len(entries)) {
3232
tree.Add(entries[i].key, entries[i].value)
3333
}
@@ -189,7 +189,7 @@ func TestFindKeys(t *testing.T) {
189189
{"bog", 6},
190190
}
191191

192-
tree := New()
192+
tree := New[int]()
193193
for _, entry := range entries {
194194
tree.Add(entry.key, entry.value)
195195
}
@@ -250,7 +250,7 @@ func TestFindValues(t *testing.T) {
250250
{"bee", 5},
251251
}
252252

253-
tree := New()
253+
tree := New[int]()
254254
for _, entry := range entries {
255255
tree.Add(entry.key, entry.value)
256256
}
@@ -332,10 +332,10 @@ func TestDictionary(t *testing.T) {
332332
}
333333

334334
// Scan all words from the dictionary into the tree.
335-
tree := New()
335+
tree := New[bool]()
336336
scanner := bufio.NewScanner(file)
337337
for scanner.Scan() {
338-
tree.Add(scanner.Text(), nil)
338+
tree.Add(scanner.Text(), true)
339339
}
340340
file.Close()
341341

@@ -382,10 +382,10 @@ func BenchmarkDictionary(b *testing.B) {
382382
}
383383

384384
// Scan all words from the dictionary into the tree.
385-
tree := New()
385+
tree := New[bool]()
386386
scanner := bufio.NewScanner(file)
387387
for scanner.Scan() {
388-
tree.Add(scanner.Text(), nil)
388+
tree.Add(scanner.Text(), true)
389389
}
390390
file.Close()
391391

0 commit comments

Comments
 (0)