golang高性能Porter2词干提取器插件porter2的使用
golang高性能Porter2词干提取器插件porter2的使用
Porter2实现了英语Porter2词干提取算法。它完全使用有限状态机来进行后缀比较,而不是基于字符串或树的方法。因此,它比基于字符串比较的方法快660%。
基本使用示例
package main
import (
"fmt"
"github.com/surgebase/porter2"
)
func main() {
// 简单词干提取示例
fmt.Println(porter2.Stem("seaweed")) // 输出: seawe
// 更多示例
words := []string{"running", "happily", "jumps", "cats", "fishing"}
for _, word := range words {
fmt.Printf("%s → %s\n", word, porter2.Stem(word))
}
/*
输出:
running → run
happily → happili
jumps → jump
cats → cat
fishing → fish
*/
}
性能对比
这个实现在各种基于Go的实现中性能最高。以下是一些测试结果:
实现 | 时间 | 算法 |
---|---|---|
surgebase | 319.009358ms | Porter2 |
dchest | 2.106912401s | Porter2 |
kljensen | 5.725917198s | Porter2 |
实现原理
大多数实现完全依赖于后缀字符串比较。基本上有一个后缀列表,代码会循环遍历列表以查看是否有匹配项。考虑到大多数情况下您正在寻找最长匹配项,因此您对列表进行排序,使最长的匹配项排在第一位。
这个实现完全基于有限状态机来执行后缀比较。您从最后一个字符开始向后比较字符串的每个字符。状态机将在每个步骤确定最长的后缀是什么。您可以将状态机视为展开的树。
高级使用示例
package main
import (
"fmt"
"github.com/surgebase/porter2"
"strings"
)
func stemSentence(sentence string) string {
words := strings.Fields(sentence)
for i, word := range words {
words[i] = porter2.Stem(word)
}
return strings.Join(words, " ")
}
func main() {
// 处理整个句子
sentence := "The quick brown foxes are jumping over the lazy dogs"
fmt.Println("原句:", sentence)
fmt.Println("词干提取后:", stemSentence(sentence))
/*
输出:
原句: The quick brown foxes are jumping over the lazy dogs
词干提取后: the quick brown fox ar jump over the lazi dog
*/
}
许可证
Copyright © 2014 Dataence, LLC. All rights reserved.
根据Apache License 2.0版本授权。
更多关于golang高性能Porter2词干提取器插件porter2的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang高性能Porter2词干提取器插件porter2的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang高性能Porter2词干提取器使用指南
Porter2词干提取算法是经典的英文词干提取算法,比原始Porter算法更准确。在Golang中,我们可以使用github.com/surgebase/porter2
库来实现高效的词干提取功能。
安装
首先安装porter2库:
go get github.com/surgebase/porter2
基本使用
package main
import (
"fmt"
"github.com/surgebase/porter2"
)
func main() {
// 简单示例
word := "running"
stem := porter2.Stem(word)
fmt.Printf("Original: %s, Stem: %s\n", word, stem)
// 输出: Original: running, Stem: run
// 更多示例
words := []string{"cats", "running", "jumped", "happily", "fairly"}
for _, w := range words {
fmt.Printf("%s → %s\n", w, porter2.Stem(w))
}
/*
输出:
cats → cat
running → run
jumped → jump
happily → happili
fairly → fair
*/
}
性能优化建议
- 批量处理:如果需要处理大量文本,建议批量处理而非单个单词
func BatchStem(words []string) []string {
result := make([]string, len(words))
for i, word := range words {
result[i] = porter2.Stem(word)
}
return result
}
- 并发处理:对于大规模文本处理,可以使用goroutine提高性能
func ConcurrentStem(words []string) []string {
var wg sync.WaitGroup
result := make([]string, len(words))
for i, word := range words {
wg.Add(1)
go func(idx int, w string) {
defer wg.Done()
result[idx] = porter2.Stem(w)
}(i, word)
}
wg.Wait()
return result
}
- 预处理:对于重复出现的单词,可以使用缓存
var stemCache sync.Map
func CachedStem(word string) string {
if val, ok := stemCache.Load(word); ok {
return val.(string)
}
stem := porter2.Stem(word)
stemCache.Store(word, stem)
return stem
}
实际应用示例
package main
import (
"bufio"
"fmt"
"github.com/surgebase/porter2"
"os"
"strings"
)
func main() {
// 从标准输入读取并提取词干
scanner := bufio.NewScanner(os.Stdin)
fmt.Println("Enter text (Ctrl+D to end):")
for scanner.Scan() {
text := scanner.Text()
if text == "" {
continue
}
words := strings.Fields(text)
stems := make([]string, len(words))
for i, word := range words {
stems[i] = porter2.Stem(strings.ToLower(word))
}
fmt.Println("Original:", words)
fmt.Println("Stems: ", stems)
fmt.Println()
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading standard input:", err)
}
}
注意事项
- Porter2算法只适用于英文文本
- 算法会将所有输入转换为小写后再处理
- 对于专有名词或特殊词汇可能不会得到理想结果
- 词干提取不同于词形还原(lemmatization),结果可能不完全相同
性能对比
Porter2实现通常比原始Porter算法慢约10-20%,但准确率更高。surgebase/porter2
库经过优化,性能表现良好,每秒可处理数十万单词。
对于需要更高性能的场景,可以考虑预编译词干映射表或使用基于机器学习的现代词干提取器。