Go语言的sort.Sort函数不会对具体的序列和它的元素做任何假设。相反,它使用了一个接口类型sort.Interface来指定通用的排序算法和可能被排序到的序列类型之间的约定。与c++中标准库的函数类似,sort包提供的函数也属于泛型函数。
接口定义
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
与c++和java等语言不通的是,golang的继承不需要显示指明基类或接口类。只要类型实现了接口的方法,类型和接口自动就关联起来。这样做带来的另一个不同点是,我们可以先实现类型,再提供接口。
主函数
func Sort(data Interface) {
n := data.Len()
quickSort(data, 0, n, maxDepth(n))
}
从C++语言过来的程序员可能会比较不适应,因为c++提供的标准库是可以直接操作像vector,list这样的数组模版的。Golang的slice没有Interface所需的方法,因此必须对其进行处理后才能使用Sort函数。
比如
type StringSlice []string
func (p StringSlice) Len() int { return len(p) }
func (p StringSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p StringSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
这里有个思想上的转变。
str_list = {"aa","bb","cc"}
sort.Sort(StringSlice(str_list))
虽然对于str_list进行了一次类型转换,从[]string转换到了新定义的StringSlice,但转换后的变量仍与之前的变量共享底层数据,它们都是指向同一个array数组。因此对于转换后的变量进行排序后,原先的slice也有序了。
看另一个例子
type customSort struct {
t []*Track
less func(x, y *Track) bool
}
func (x customSort) Len() int
func (x customSort) Less(i, j int) bool { return x.less(x.t[i], x.t[j]) }
func (x customSort) Swap(i, j int) { x.t[i], x.t[j] = x.t[j], x.t[i] }
我们看到customSort
类型提供
sort.
Interface所需要的方法,因此可以被sort.Sort调用。customSort
是一个
struct
类型,需要排序的数据接口是其内部的成员
t
,它是一个
slice
。因此对于
customSort
类型的变量进行排序,其结果是对成员
t
进行的排序。这里的另一个细节是使用
slice pointer
,移动指针比对象的效率更高,个人认为这也是一个语法糖,可以这么进行初始化:
var tracks = []*Track{
{"Go", "Delilah", "From the Roots Up", 2012, length("3m38s")},
{"Go", "Moby", "Moby", 1992, length("3m37s")},
{"Go Ahead", "Alicia Keys", "As I Am", 2007, length("4m36s")},
{"Ready 2 Go", "Martin Solveig", "Smash", 2011, length("4m24s")},
}
Reverse函数
sort包中提供了Reverse函数将排序顺序转换成逆序,他是这么使用的:
sort.Sort(sort.Reverse(byArtist(tracks)))
我们看一下和Reverse相关的代码:
ype reverse struct{ Interface } // that is, sort.Interface
func (r reverse) Less(i, j int) bool { return r.Interface.Less(j, i) }
func Reverse(data Interface) Interface { return reverse{data} }
Reverse
函数接受
一个
Interface
实例,这里有一个隐式的
struct
到
interface
的转换。在函数的
body
里,它被赋值到
reverse
结构体中的成员里,返回时又有一次
reverse
结构体到
Interface
接口的转换。之所以能这样转换,是因为
reverse
结构体也(隐式的)实现了
Interface
所需的方法。所有的拷贝转换之所以成行(排序结果生效),是因为
slice
的拷贝属于引用拷贝。
常用函数
考虑到[]int, []float64,[]string在排序中经常会被使用到,sort包提供了相应的类型对齐进行转化(IntSlice,Float64Slice,StringSlice),这样我们可以直接使用sort.Ints,sort.Float64s,sort.Strings进行排序处理。