2.4 泛型算法
泛型让函数式编程模式在 Go 中变得实用。Filter、Map、Reduce 等操作现在是类型安全的。
Filter — 过滤
go
func Filter[T any](slice []T, pred func(T) bool) []T {
var result []T
for _, v := range slice {
if pred(v) {
result = append(result, v)
}
}
return result
}
// 使用示例
nums := []int{1, 2, 3, 4, 5, 6}
evens := Filter(nums, func(n int) bool { return n%2 == 0 })
// [2 4 6]
type User struct { Name string; Active bool }
users := []User{{"A", true}, {"B", false}, {"C", true}}
active := Filter(users, func(u User) bool { return u.Active })
// [{A true} {C true}]Map — 映射变换
go
func Map[T, U any](slice []T, fn func(T) U) []U {
result := make([]U, len(slice))
for i, v := range slice {
result[i] = fn(v)
}
return result
}
// 提取字段
names := Map(users, func(u User) string { return u.Name })
// ["A", "B", "C"]
// 类型转换
strs := Map(nums, func(n int) string { return fmt.Sprintf("%d", n) })
// ["1", "2", "3", "4", "5", "6"]Reduce — 归约
go
func Reduce[T, U any](slice []T, init U, fn func(U, T) U) U {
result := init
for _, v := range slice {
result = fn(result, v)
}
return result
}
// 求和
sum := Reduce(nums, 0, func(acc, n int) int { return acc + n })
// 21
// 连接字符串
joined := Reduce([]string{"a", "b", "c"}, "", func(acc, s string) string {
if acc == "" {
return s
}
return acc + "-" + s
})
// "a-b-c"
// 分组(复杂归约)
groups := Reduce(users, make(map[bool][]User), func(acc map[bool][]User, u User) map[bool][]User {
acc[u.Active] = append(acc[u.Active], u)
return acc
})
// map[true:[{A true} {C true}] false:[{B false}]]Find / FindIndex — 查找
go
func Find[T any](slice []T, pred func(T) bool) (T, bool) {
for _, v := range slice {
if pred(v) {
return v, true
}
}
var zero T
return zero, false
}
func FindIndex[T any](slice []T, pred func(T) bool) int {
for i, v := range slice {
if pred(v) {
return i
}
}
return -1
}
// 使用
item, found := Find(nums, func(n int) bool { return n > 4 })
// item=5, found=true
idx := FindIndex(users, func(u User) bool { return u.Name == "B" })
// idx=1GroupBy — 分组
go
func GroupBy[T any, K comparable](slice []T, keyFn func(T) K) map[K][]T {
result := make(map[K][]T)
for _, v := range slice {
key := keyFn(v)
result[key] = append(result[key], v)
}
return result
}
// 按字段分组
byActive := GroupBy(users, func(u User) bool { return u.Active })
// map[true:[{A true} {C true}] false:[{B false}]]
// 按年龄分组
type Person struct { Name string; Age int }
people := []Person{{"A", 20}, {"B", 30}, {"C", 20}}
byAge := GroupBy(people, func(p Person) int { return p.Age })
// map[20:[{A 20} {C 20}] 30:[{B 30}]]Partition — 分区
go
func Partition[T any](slice []T, pred func(T) bool) (matched, unmatched []T) {
for _, v := range slice {
if pred(v) {
matched = append(matched, v)
} else {
unmatched = append(unmatched, v)
}
}
return
}
// 使用
matched, unmatched := Partition(nums, func(n int) bool { return n < 4 })
// matched=[1 2 3] unmatched=[4 5 6]FlatMap — 扁平映射
go
func FlatMap[T, U any](slice []T, fn func(T) []U) []U {
var result []U
for _, v := range slice {
result = append(result, fn(v)...)
}
return result
}
// 展开嵌套
words := []string{"hello world", "go generics"}
allWords := FlatMap(words, func(s string) []string {
return strings.Fields(s)
})
// ["hello", "world", "go", "generics"]SortFunc — 自定义排序
go
import (
"cmp"
"slices"
)
// 使用 slices.SortFunc + cmp.Compare
slices.SortFunc(people, func(a, b Person) int {
return cmp.Compare(a.Age, b.Age)
})
// 多字段排序
slices.SortFunc(people, func(a, b Person) int {
if c := cmp.Compare(a.Age, b.Age); c != 0 {
return c
}
return cmp.Compare(a.Name, b.Name)
})
// 降序
slices.SortFunc(nums, func(a, b int) int {
return cmp.Compare(b, a) // 反转 a, b
})Some / Every / None
go
func Some[T any](slice []T, pred func(T) bool) bool {
for _, v := range slice {
if pred(v) {
return true
}
}
return false
}
func Every[T any](slice []T, pred func(T) bool) bool {
for _, v := range slice {
if !pred(v) {
return false
}
}
return true
}
func None[T any](slice []T, pred func(T) bool) bool {
return !Some(slice, pred)
}
// 使用
hasNegative := Some(nums, func(n int) bool { return n < 0 }) // false
allPositive := Every(nums, func(n int) bool { return n > 0 }) // true
noZero := None(nums, func(n int) bool { return n == 0 }) // true算法组合实战
go
func main() {
orders := []Order{
{ID: 1, UserID: 10, Amount: 99.9, Status: "paid"},
{ID: 2, UserID: 20, Amount: 49.5, Status: "pending"},
{ID: 3, UserID: 10, Amount: 199.0, Status: "paid"},
{ID: 4, UserID: 30, Amount: 29.9, Status: "cancelled"},
}
// 链式操作:筛选已付款 → 按用户分组 → 计算每人总金额
paid := Filter(orders, func(o Order) bool { return o.Status == "paid" })
byUser := GroupBy(paid, func(o Order) int { return o.UserID })
totals := Map(byUser, func(g []Order) UserTotal {
sum := Reduce(g, 0.0, func(acc float64, o Order) float64 { return acc + o.Amount })
return UserTotal{UserID: g[0].UserID, Total: sum}
})
// [{UserID:10 Total:298.9} {UserID:20 ...}]
}性能提示
- Map 中预分配容量
make([]U, len(slice))— 避免 append 引发多次扩容 - Filter 中无法预知结果大小,可以用
make([]T, 0, len(slice))作为初始 hint - Reduce 适合聚合操作,但不要滥用 — Go 的 for 循环更直观
- 大量数据用
slices.SortFunc,它背后是 pdqsort(比标准快排更快)
要点总结
- 泛型算法 = 一次编写,所有类型复用
- Filter/Map/Reduce 是最常用的三种模式
- GroupBy 需要
comparable约束(key 用于 map) - 多字段排序用
cmp.Compare链式比较 - 算法可以组合使用,形成数据处理管道
- 注意预分配容量,减少内存分配