Go語言是一門非常簡單優雅的語言,其源碼更是其風格標杆。看源碼,不僅能學習Go的設計哲學,瞭解如何調用庫函數,同時幫助我們寫出更優雅的go代碼。
Go源碼位於GOROOT目錄下的src中。
本文學習1.14.1版本源碼庫的sort包。該包對外提供的主要功能是排序和搜索。其核心的函數分別是:sort.Sort()與sort.Search()。
1. sort.Sort()
函數定義如下
func Sort(data Interface) {
n := data.Len()
quickSort(data, 0, n, maxDepth(n))
}
Sort函數中調用了quickSort方法,該方法下是排序算法的具體實現,核心策略是根據待排序的數據量,相應調整不同的排序算法,這部分內容不在本文分析內容之中。
對包使用者來說,需要注意的是,Sort的入參是Interface接口,這Interface是什麼呢?以下是其在sort.go中的定義。
type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less reports whether the element with
// index i should sort before the element with index j.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}
可見,Interface是一個接口類型(interface),其需要實現的函數方法分別是:Len、Less和Swap。這意味着什麼呢?
我們知道,go中的接口實現是隱式實現,只要對象實現了接口定義的方法,即可實現該接口。因此,這種方式是一種非侵入式的實現。
那麼,如果需要某對象能調用Sort方法,即實現其參數Interface接口即可。
在Sort包中,已經實現了部分對象類型對Sort函數的調用,包括:[]int,[]float64,[]string。以[]int類型爲例,看其代碼實現。
type IntSlice []int //使用IntSlice類型對[]int類型進行封裝
//IntSlice對象實現Interface接口
func (p IntSlice) Len() int { return len(p) }
func (p IntSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p IntSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
...
//由於Intslice對象實現了Interface接口,即可以作爲參數調用Sort方法。
func Ints(a []int) { Sort(IntSlice(a)) }
這就是sort包對外提供的Ints函數:當需要對[]int數組進行排序時,直接調用sort.Ints()。
package main
import (
"fmt"
"sort"
)
func main() {
a := []int{12,45,33,78,9,14}
sort.Ints(a)
fmt.Println(a) //[9 12 14 33 45 78]
}
sort.Float64()和sort.Strings()同理。
2. 自定義對象調用Sort()
思考:假如需要排序的是自定義對象,應該如何實現。
給定對象person,其包括兩個屬性: name string, age int。那麼如何根據姓名或者age給person進行排序。完整示例如下。
package main
import (
"fmt"
"sort"
)
type person struct { //定義對象person
name string
age int
}
type personSlice []person //給[]person綁定對象
// 實現sort包定義的Interface接口
func (s personSlice) Len() int {
return len(s)
}
func (s personSlice) Less(i, j int) bool {
return s[i].age < s[j].age
}
func (s personSlice) Swap(i, j int) {
s[i].age, s[j].age = s[j].age, s[i].age
}
func main() {
p := personSlice{
person{
name: "mike",
age: 13,
}, person{
name: "jane",
age: 12,
}, person{
name: "peter",
age: 14,
}}
sort.Sort(p)
fmt.Println(p) // [{mike 12} {jane 13} {peter 14}]
}
注意:並不是所有對象都能實現排序,前提是排序依賴的屬性列是可比較的,如int,string等。
3. sort.Search()
函數原型
func Search(n int, f func(int) bool) int {}
Serarch裏面的算法細節本文不討論,只關注如何實現調用。Search函數中n參數是待搜索對象集的長度,注意此對象集必須是已排序過的;f參數是一個匿名函數,實現規則見下文[]int例子。
同樣的,sort包已爲幾個特定對象實現了調用函數,包括[]int,[]float64,[]string。以[]int爲例。
// 輸入已排序[]int類型a,和某item值x,返回其在[]int中的索引值。
// 注:小於[]int最小值返回0,大於最大值,返回len(a)
func SearchInts(a []int, x int) int {
return Search(len(a), func(i int) bool { return a[i] >= x })
}
...
// 返回IntSlice([]int)中的x所在索引值
// 注:小於[]int最小值返回0,大於最大值,返回len(a)
func (p IntSlice) Search(x int) int { return SearchInts(p, x) }
使用示例:
package main
import (
"fmt"
"sort"
)
func main() {
a := []int{3,2,4,5,1}
sort.Ints(a)
fmt.Println(a) // [1 2 3 4 5]
fmt.Println(sort.SearchInts(a,5)) // 4
fmt.Println(sort.SearchInts(a,6)) // 5
fmt.Println(sort.IntSlice(a).Search(5)) // 4
}
4. 自定義對象調用Search
同樣是person對象,如果想根據age來查找排序後的person集合(personSlice)中下標,該如何實現。示例代碼如下。
// 調用sort.Search函數,構造PersonSlice自己的Search方法
func (s personSlice) Search(age int) int{
return sort.Search(len(s), func(i int) bool {
return s[i].age>=age
})
}
func main() {
p := personSlice{
person{
name: "mike",
age: 13,
}, person{
name: "jane",
age: 12,
}, person{
name: "peter",
age: 14,
}}
sort.Sort(p)
fmt.Println(p) // [{mike 12} {jane 13} {peter 14}]
fmt.Println(p.Search(13)) // 1
}
5. 其他函數
當然,在sort包中還有其他的一些函數暴露給用戶使用,例如:sort.IsSorted()用於判斷對象是否有序;sort.Stable()提供穩定排序,即當元素類型相等時,保留原有相對位置;sort.Reverse()提供對象集合元素反轉等。有了sort.Sort()和sort.Search()的理解,這些函數都是相似的調用或實現。
本文內容同步在作者個人微信公衆號:Golang技術分享。歡迎關注!