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

1 回复

更多关于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
	*/
}

性能优化建议

  1. 批量处理:如果需要处理大量文本,建议批量处理而非单个单词
func BatchStem(words []string) []string {
	result := make([]string, len(words))
	for i, word := range words {
		result[i] = porter2.Stem(word)
	}
	return result
}
  1. 并发处理:对于大规模文本处理,可以使用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
}
  1. 预处理:对于重复出现的单词,可以使用缓存
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)
	}
}

注意事项

  1. Porter2算法只适用于英文文本
  2. 算法会将所有输入转换为小写后再处理
  3. 对于专有名词或特殊词汇可能不会得到理想结果
  4. 词干提取不同于词形还原(lemmatization),结果可能不完全相同

性能对比

Porter2实现通常比原始Porter算法慢约10-20%,但准确率更高。surgebase/porter2库经过优化,性能表现良好,每秒可处理数十万单词。

对于需要更高性能的场景,可以考虑预编译词干映射表或使用基于机器学习的现代词干提取器。

回到顶部