golang sort.Slice踩坑记录

2022-09-14  本文已影响0人  夕午wuw

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]
    })
}
上一篇 下一篇

猜你喜欢

热点阅读