Skip to content

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=1

GroupBy — 分组 ​

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 ...}]
}

性能提示 ​

  1. Map 中预分配容量 make([]U, len(slice)) — 避免 append 引发多次扩容
  2. Filter 中无法预知结果大小,可以用 make([]T, 0, len(slice)) 作为初始 hint
  3. Reduce 适合聚合操作,但不要滥用 — Go 的 for 循环更直观
  4. 大量数据用 slices.SortFunc,它背后是 pdqsort(比标准快排更快)

要点总结 ​

  1. 泛型算法 = 一次编写,所有类型复用
  2. Filter/Map/Reduce 是最常用的三种模式
  3. GroupBy 需要 comparable 约束(key 用于 map)
  4. 多字段排序用 cmp.Compare 链式比较
  5. 算法可以组合使用,形成数据处理管道
  6. 注意预分配容量,减少内存分配