...
1
2
3 package spell
4
5 import (
6 "strings"
7 "unicode"
8 )
9
10
11
12
13 func Nearest(x string, candidates []string) string {
14
15 fold := func(s string) string {
16 return strings.Map(func(r rune) rune {
17 if r == '_' {
18 return -1
19 }
20 return unicode.ToLower(r)
21 }, s)
22 }
23
24 x = fold(x)
25
26 var best string
27 bestD := (len(x) + 1) / 2
28 for _, c := range candidates {
29 d := levenshtein(x, fold(c), bestD)
30 if d < bestD {
31 bestD = d
32 best = c
33 }
34 }
35 return best
36 }
37
38
39
40
41
42
43 func levenshtein(x, y string, max int) int {
44
45
46
47
48
49 if len(x) > len(y) {
50 x, y = y, x
51 }
52
53
54 for i := 0; i < len(x); i++ {
55 if x[i] != y[i] {
56 x = x[i:]
57 y = y[i:]
58 break
59 }
60 }
61 if x == "" {
62 return len(y)
63 }
64
65 if d := abs(len(x) - len(y)); d > max {
66 return d
67 }
68
69 row := make([]int, len(y)+1)
70 for i := range row {
71 row[i] = i
72 }
73
74 for i := 1; i <= len(x); i++ {
75 row[0] = i
76 best := i
77 prev := i - 1
78 for j := 1; j <= len(y); j++ {
79 a := prev + b2i(x[i-1] != y[j-1])
80 b := 1 + row[j-1]
81 c := 1 + row[j]
82 k := min(a, min(b, c))
83 prev, row[j] = row[j], k
84 best = min(best, k)
85 }
86 if best > max {
87 return best
88 }
89 }
90 return row[len(y)]
91 }
92
93 func b2i(b bool) int {
94 if b {
95 return 1
96 } else {
97 return 0
98 }
99 }
100
101 func min(x, y int) int {
102 if x < y {
103 return x
104 } else {
105 return y
106 }
107 }
108
109 func abs(x int) int {
110 if x >= 0 {
111 return x
112 } else {
113 return -x
114 }
115 }
116
View as plain text