# 11.7 第一个例子:使用 Sorter 接口排序

    一个很好的例子是来自标准库的 sort 包,要对一组数字或字符串排序,只需要实现三个方法:反映元素个数的 Len()方法、比较第 ij 个元素的 Less(i, j) 方法以及交换第 ij 个元素的 Swap(i, j) 方法。

    排序函数的算法只会使用到这三个方法(可以使用任何排序算法来实现,此处我们使用冒泡排序):

    1. func Sort(data Sorter) {
    2. for pass := 1; pass < data.Len(); pass++ {
    3. for i := 0;i < data.Len() - pass; i++ {
    4. if data.Less(i+1, i) {
    5. data.Swap(i, i + 1)
    6. }
    7. }
    8. }
    9. }

    Sort 函数接收一个接口类型参数:Sorter ,它声明了这些方法:

    1. type Sorter interface {
    2. Len() int
    3. Less(i, j int) bool
    4. Swap(i, j int)
    5. }

    参数中的 int 是待排序序列长度的类型,而不是说要排序的对象一定要是一组 intij 表示元素的整型索引,长度也是整型的。

    现在如果我们想对一个 int 数组进行排序,所有必须做的事情就是:为数组定一个类型并在它上面实现 Sorter 接口的方法:

    1. type IntArray []int
    2. func (p IntArray) Len() int { return len(p) }
    3. func (p IntArray) Less(i, j int) bool { return p[i] < p[j] }
    4. func (p IntArray) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

    下面是调用排序函数的一个具体例子:

    1. data := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
    2. a := sort.IntArray(data) //conversion to type IntArray from package sort
    3. sort.Sort(a)

    完整的、可运行的代码可以在 sort.gosortmain.go 里找到。

    同样的原理,排序函数可以用于一个浮点型数组,一个字符串数组,或者一个表示每周各天的结构体 dayArray.

    示例 11.6 sort.go:

    1. package sort
    2. type Sorter interface {
    3. Len() int
    4. Less(i, j int) bool
    5. Swap(i, j int)
    6. }
    7. func Sort(data Sorter) {
    8. for pass := 1; pass < data.Len(); pass++ {
    9. for i := 0; i < data.Len()-pass; i++ {
    10. if data.Less(i+1, i) {
    11. data.Swap(i, i+1)
    12. }
    13. }
    14. }
    15. }
    16. func IsSorted(data Sorter) bool {
    17. n := data.Len()
    18. for i := n - 1; i > 0; i-- {
    19. if data.Less(i, i-1) {
    20. return false
    21. }
    22. }
    23. return true
    24. }
    25. // Convenience types for common cases
    26. type IntArray []int
    27. func (p IntArray) Len() int { return len(p) }
    28. func (p IntArray) Less(i, j int) bool { return p[i] < p[j] }
    29. func (p IntArray) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
    30. type StringArray []string
    31. func (p StringArray) Len() int { return len(p) }
    32. func (p StringArray) Less(i, j int) bool { return p[i] < p[j] }
    33. func (p StringArray) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
    34. // Convenience wrappers for common cases
    35. func SortInts(a []int) { Sort(IntArray(a)) }
    36. func SortStrings(a []string) { Sort(StringArray(a)) }
    37. func IntsAreSorted(a []int) bool { return IsSorted(IntArray(a)) }
    38. func StringsAreSorted(a []string) bool { return IsSorted(StringArray(a)) }

    示例 11.7 sortmain.go:

    1. package main
    2. import (
    3. "./sort"
    4. "fmt"
    5. )
    6. func ints() {
    7. data := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
    8. a := sort.IntArray(data) //conversion to type IntArray
    9. sort.Sort(a)
    10. if !sort.IsSorted(a) {
    11. panic("fails")
    12. }
    13. fmt.Printf("The sorted array is: %v\n", a)
    14. }
    15. func strings() {
    16. data := []string{"monday", "friday", "tuesday", "wednesday", "sunday", "thursday", "", "saturday"}
    17. a := sort.StringArray(data)
    18. sort.Sort(a)
    19. if !sort.IsSorted(a) {
    20. panic("fail")
    21. }
    22. fmt.Printf("The sorted array is: %v\n", a)
    23. }
    24. type day struct {
    25. num int
    26. shortName string
    27. longName string
    28. }
    29. type dayArray struct {
    30. data []*day
    31. }
    32. func (p *dayArray) Len() int { return len(p.data) }
    33. func (p *dayArray) Less(i, j int) bool { return p.data[i].num < p.data[j].num }
    34. func (p *dayArray) Swap(i, j int) { p.data[i], p.data[j] = p.data[j], p.data[i] }
    35. func days() {
    36. Sunday := day{0, "SUN", "Sunday"}
    37. Monday := day{1, "MON", "Monday"}
    38. Tuesday := day{2, "TUE", "Tuesday"}
    39. Wednesday := day{3, "WED", "Wednesday"}
    40. Thursday := day{4, "THU", "Thursday"}
    41. Friday := day{5, "FRI", "Friday"}
    42. Saturday := day{6, "SAT", "Saturday"}
    43. data := []*day{&Tuesday, &Thursday, &Wednesday, &Sunday, &Monday, &Friday, &Saturday}
    44. a := dayArray{data}
    45. sort.Sort(&a)
    46. if !sort.IsSorted(&a) {
    47. panic("fail")
    48. }
    49. for _, d := range data {
    50. fmt.Printf("%s ", d.longName)
    51. }
    52. fmt.Printf("\n")
    53. }
    54. func main() {
    55. ints()
    56. strings()
    57. days()
    58. }

    输出:

    1. The sorted array is: [-5467984 -784 0 0 42 59 74 238 905 959 7586 7586 9845]
    2. The sorted array is: [ friday monday saturday sunday thursday tuesday wednesday]
    3. Sunday Monday Tuesday Wednesday Thursday Friday Saturday

    备注

    panic("fail") 用于停止处于在非正常情况下的程序(详细请参考 第13章),当然也可以先打印一条信息,然后调用 os.Exit(1) 来停止程序。

    上面的例子帮助我们进一步了解了接口的意义和使用方式。对于基本类型的排序,标准库已经提供了相关的排序函数,所以不需要我们再重复造轮子了。对于一般性的排序,sort 包定义了一个接口:

    1. type Interface interface {
    2. Len() int
    3. Less(i, j int) bool
    4. Swap(i, j int)
    5. }

    这个接口总结了需要用于排序的抽象方法,函数 Sort(data Interface) 用来对此类对象进行排序,可以用它们来实现对其他数据(非基本类型)进行排序。在上面的例子中,我们也是这么做的,不仅可以对 intstring 序列进行排序,也可以对用户自定义类型 dayArray 进行排序。

    练习 11.5 interfaces_ext.go:

    a). 继续扩展程序,定义类型 Triangle,让它实现 AreaInterface 接口。通过计算一个特定三角形的面积来进行测试(三角形面积=0.5 (底 高))

    b). 定义一个新接口 PeriInterface,它有一个 Perimeter 方法。让 Square 实现这个接口,并通过一个 Square 示例来测试它。

    练习 11.6 point_interfaces.go:

    继续 10.3 中的练习 point_methods.go,定义接口 Magnitude,它有一个方法 Abs()。让 PointPoint3Polar 实现此接口。通过接口类型变量使用方法做point.go中同样的事情。

    练习 11.7 float_sort.go / float_sortmain.go:

    类似11.7和示例11.3/4,定义一个包 float64,并在包里定义类型 Float64Array,然后让它实现 Sorter 接口用来对 float64 数组进行排序。

    另外提供如下方法:

    • NewFloat64Array():创建一个包含25个元素的数组变量(参考10.2)
    • List():返回数组格式化后的字符串,并在 String() 方法中调用它,这样就不用显式地调用 List() 来打印数组(参考10.7)
    • Fill():创建一个包含10个随机浮点数的数组(参考4.5.2.6)

    在主程序中新建一个此类型的变量,然后对它排序并进行测试。

    练习 11.8 sort.go/sort_persons.go:

    定义一个结构体 Person,它有两个字段:firstNamelastName,为 []Person 定义类型 Persons 。让 Persons 实现 Sorter 接口并进行测试。