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

一個很好的例子是來自標準庫的 sort 包,要對一組數字或字符串排序,只需要實現三個方法:反映元素個數的 Len()方法、比較第 ij 個元素的 Less(i, j) 方法以及交換第 ij 個元素的 Swap(i, j) 方法。

排序函數的算法只會使用到這三個方法(可以使用任何排序算法來實現,此處我們使用冒泡排序):

func Sort(data Sorter) {
    for pass := 1; pass < data.Len(); pass++ {
        for i := 0;i < data.Len() - pass; i++ {
            if data.Less(i+1, i) {
                data.Swap(i, i + 1)
            }
        }
    }
}

Sort 函數接收一個接口類型參數:Sorter ,它聲明瞭這些方法:

type Sorter interface {
    Len() int
    Less(i, j int) bool
    Swap(i, j int)
}

參數中的 int 不是說要排序的對象一定要是一組 intij 表示元素的整型索引,長度也是整型的。

現在如果我們想對一個 int 數組進行排序,所有必須做的事情就是:爲數組定一個類型並在它上面實現 Sorter 接口的方法:

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

下面是調用排序函數的一個具體例子:

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

完整的、可運行的代碼可以在 sort.gosortmain.go 裏找到。

同樣的原理,排序函數可以用於一個浮點型數組,一個字符串數組,或者一個表示每週各天的結構體 dayArray.

示例 11.6 sort.go

package sort

type Sorter interface {
    Len() int
    Less(i, j int) bool
    Swap(i, j int)
}

func Sort(data Sorter) {
    for pass := 1; pass < data.Len(); pass++ {
        for i := 0; i < data.Len()-pass; i++ {
            if data.Less(i+1, i) {
                data.Swap(i, i+1)
            }
        }
    }
}

func IsSorted(data Sorter) bool {
    n := data.Len()
    for i := n - 1; i > 0; i-- {
        if data.Less(i, i-1) {
            return false
        }
    }
    return true
}

// Convenience types for common cases
type IntArray []int

func (p IntArray) Len() int           { return len(p) }
func (p IntArray) Less(i, j int) bool { return p[i] < p[j] }
func (p IntArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

type StringArray []string

func (p StringArray) Len() int           { return len(p) }
func (p StringArray) Less(i, j int) bool { return p[i] < p[j] }
func (p StringArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

// Convenience wrappers for common cases
func SortInts(a []int)       { Sort(IntArray(a)) }
func SortStrings(a []string) { Sort(StringArray(a)) }

func IntsAreSorted(a []int) bool       { return IsSorted(IntArray(a)) }
func StringsAreSorted(a []string) bool { return IsSorted(StringArray(a)) }

示例 11.7 sortmain.go

package main

import (
    "./sort"
    "fmt"
)

func ints() {
    data := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
    a := sort.IntArray(data) //conversion to type IntArray
    sort.Sort(a)
    if !sort.IsSorted(a) {
        panic("fails")
    }
    fmt.Printf("The sorted array is: %v\n", a)
}

func strings() {
    data := []string{"monday", "friday", "tuesday", "wednesday", "sunday", "thursday", "", "saturday"}
    a := sort.StringArray(data)
    sort.Sort(a)
    if !sort.IsSorted(a) {
        panic("fail")
    }
    fmt.Printf("The sorted array is: %v\n", a)
}

type day struct {
    num       int
    shortName string
    longName  string
}

type dayArray struct {
    data []*day
}

func (p *dayArray) Len() int           { return len(p.data) }
func (p *dayArray) Less(i, j int) bool { return p.data[i].num < p.data[j].num }
func (p *dayArray) Swap(i, j int)      { p.data[i], p.data[j] = p.data[j], p.data[i] }

func days() {
    Sunday    := day{0, "SUN", "Sunday"}
    Monday    := day{1, "MON", "Monday"}
    Tuesday   := day{2, "TUE", "Tuesday"}
    Wednesday := day{3, "WED", "Wednesday"}
    Thursday  := day{4, "THU", "Thursday"}
    Friday    := day{5, "FRI", "Friday"}
    Saturday  := day{6, "SAT", "Saturday"}
    data := []*day{&Tuesday, &Thursday, &Wednesday, &Sunday, &Monday, &Friday, &Saturday}
    a := dayArray{data}
    sort.Sort(&a)
    if !sort.IsSorted(&a) {
        panic("fail")
    }
    for _, d := range data {
        fmt.Printf("%s ", d.longName)
    }
    fmt.Printf("\n")
}

func main() {
    ints()
    strings()
    days()
}

輸出:

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

備註

panic("fail") 用於停止處於在非正常情況下的程序(詳細請參考 第13章),當然也可以先打印一條信息,然後調用 os.Exit(1) 來停止程序。

上面的例子幫助我們進一步瞭解了接口的意義和使用方式。對於基本類型的排序,標準庫已經提供了相關的排序函數,所以不需要我們再重複造輪子了。對於一般性的排序,sort 包定義了一個接口:

type Interface interface {
    Len() int
    Less(i, j int) bool
    Swap(i, j int)
}

這個接口總結了需要用於排序的抽象方法,函數 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 接口並進行測試。

鏈接

results matching ""

    No results matching ""