我实际上正在为 Emacs 构建类似于 Vim 的 Command-T 和 ctrlp 插件,只是为了好玩。我刚刚与一些聪明的同事就如何最有效地做到这一点进行了富有成效的讨论。目标是减少消除不匹配文件所需的操作次数。所以我们创建了一个嵌套映射,在顶层,每个键都是一个出现在搜索集中某处的字符,映射到搜索集中所有字符串的索引。然后,这些索引中的每一个都映射到该特定字符出现在搜索字符串中的字符偏移列表。
在伪代码中,对于字符串:
我们会像这样构建一个地图:
{
"c" => {
0 => [0]
},
"o" => {
0 => [1, 5],
1 => [1]
},
"n" => {
0 => [2]
},
"t" => {
0 => [3]
},
"r" => {
0 => [4, 9]
},
"l" => {
0 => [6, 7],
1 => [4]
},
"e" => {
0 => [9],
1 => [3],
2 => [2]
},
"m" => {
1 => [0]
},
"d" => {
1 => [2]
},
"v" => {
2 => [0]
},
"i" => {
2 => [1]
},
"w" => {
2 => [3]
}
}
所以现在你有一个这样的映射:
{
character-1 => {
word-index-1 => [occurrence-1, occurrence-2, occurrence-n, ...],
word-index-n => [ ... ],
...
},
character-n => {
...
},
...
}
现在搜索字符串“oe”:
- 初始化一个新映射,其中键是匹配字符串的索引,而值是到目前为止通过该字符串读取的偏移量。
- 使用搜索字符串“o”中的第一个字符并在查找表中进行查找。
- 由于索引 0 和 1 处的字符串与“o”匹配,请将它们放入映射
{0 => 1, 1 => 1}。
- 现在搜索使用输入字符串中的下一个字符“e”并在表中查找。
- 这里有 3 个字符串匹配,但我们知道我们只关心字符串 0 和 1。
- 检查是否有任何偏移> 当前偏移。如果不是,则从我们的地图中删除项目,否则更新偏移量:
{0 => 9, 1 => 3}。
现在通过查看我们累积的映射中的键,我们知道哪些字符串与模糊搜索匹配。
理想情况下,如果搜索是在用户键入时执行的,您将跟踪累积的结果哈希并将其传递回您的搜索函数。我认为这将比迭代所有搜索字符串并在每个字符串上执行完整的通配符搜索要快得多。
有趣的是,假设您只关心插入,而不关心替换或删除,您还可以有效地将 Levenstein 距离与每个匹配项一起存储。虽然加入这个逻辑也许并不难。