sort.Slice
sort.Slice是go 1.8版本引入的一个强大排序函数。第一个参数是待排序的任意类型slice;第二个参数是less function,用于比较 i 和 j 对应的元素大小,"较小"的排在前面。注意这里并不真的按照"大小"排序,而是根据less func的定义来决定排序。
func Slice(x any, less func(i, j int) bool)
import (
"fmt"
"sort"
)
func main() {
people := []struct {
Name string
Age int
}{
{"Gopher", 7},
{"Alice", 55},
{"Vera", 24},
{"Bob", 75},
}
sort.Slice(people, func(i, j int) bool { return people[i].Name < people[j].Name })
fmt.Println("By name:", people)
sort.Slice(people, func(i, j int) bool { return people[i].Age < people[j].Age })
fmt.Println("By age:", people)
}
上面这段代码就很好的展示了less function的强大。但同时也需要注意,这里的less function是一个匿名函数,存在的坑接下来我会细说。
匿名函数小坑
假设我们现在要对一个int slice进行排序,小的在前面。很容易写出以下代码:
func sortAll(nums []int) {
sort.Slice(nums, func(i, j int) bool {
return nums[i] < nums[j]
})
}
接下来,坑来了。如果我只想对int slice的后半部分进行排序,怎么办?以下代码可行吗:
func sortPartly(nums []int, p int) { // 从p开始排序
sort.Slice(nums[p:], func(i, j int) bool { // 这里传入的slice不再是完整的nums,而是nums[p:]
return nums[i] < nums[j]
})
} // 错误写法
乍一看是可以的,但由于匿名函数的原因,这个写法不可行。在上面那段代码中,需要排序的slice是nums,对于slice中的第i或者第j个元素,less function直接对nums[i]和nums[j]进行比较操作;而在下面那段代码中,需要排序的slice是 nums[:p] ,对于slice中的第i或者第j个元素,less function 仍然是对nums[i]和nums[j] 进行比较操作。实际上,less function应该比较的对象是 nums[i+p]和nums[j+p]。
因此,一个正确的部分排序函数应该这样写,对less function的index进行调整:
func sortPartly(nums []int, p int) {
sort.Slice(nums[p:], func(i, j int) bool {
return nums[i+p] < nums[j+p]
})
}
那么,还有另一种优雅一些的写法: 对需要排序的部分新建立一个slice (go语言中的slice都是对一个底层数组的引用),再进行排序。这样就无需调整less function的index。
func sortPartly(nums []int, p int) {
t := nums[p:]
sort.Slice(t, func(i, j int) bool {
return t[i] < t[j]
})
}