go sort.Interface 排序介面

miss201發表於2018-08-07

在很多語言中,排序演算法都是和序列資料型別關聯,同時排序函式和具體型別元素關聯。而 Go 語言的 sort.Sort() 函式不會對具體的序列和它的元素做任何假設。相反,它使用了一個介面型別 sort.Interface 來指定通用的排序演算法和可能被排序到的序列型別之間的約定。
這個介面的實現由序列的具體表示和它希望排序的元素決定,序列的表示經常是一個切片。
一個內建的排序演算法需要知道三個東西:序列的長度,表示兩個元素比較的結果,一種交換兩個元素的
方式
;這就是 sort.Interface 的三個方法:

package sort

// A type, typically a collection, that satisfies sort.Interface can be
// sorted by the routines in this package. The methods require that the
// elements of the collection be enumerated by an integer index.
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)
}

為了對序列進行排序,我們需要定義一個實現了這三個方法的型別,然後對這個型別的一個例項應用
sort.Sort() 函式。
例子:對一個表格中的音樂播放列表進行排序

package main

import (
    "fmt"
    "os"
    "sort"
    "text/tabwriter"
    "time"
)

//定義音樂播放列表的結構體
type Track struct {
    Title  string
    Artist string
    Album  string
    Year   int
    Length time.Duration
}

//例項化幾條測試資料
var tracks = []*Track{
    {"GO", "Delilah", "From the Roots Up", 2012, length("3m38s")},
    {"GO", "Moby", "Moby", 2012, length("13m38s")},
    {"GOaaaa", "Alicic keys", "As I am", 2012, length("31m38s")},
    {"GO", "Martin solveig", "Smash", 2012, length("34m38s")},
}

/**
獲取時間長度
*/
func length(s string) time.Duration {
    d, err := time.ParseDuration(s)
    if err != nil {
        panic(err)
    }
    return d
}

/**
將播放列表列印成一個表格,
使用text/tabwriter包來生成一個列是整齊對齊和隔開的表格
*/
func printTracks(track []*Track) {
    const format = "%v\t%v\t%v\t%v\t%v\t\n"
    tw := new(tabwriter.Writer).Init(os.Stdout, 0, 8, 2, ' ', 0)
    fmt.Fprintf(tw, format, "Title", "Artist", "Album", "Year", "Length")
    for _, t := range track {
        fmt.Fprintf(tw, format, t.Title, t.Artist, t.Album, t.Year, t.Length)
    }

    tw.Flush()
}

/**
為了能按照Artist欄位對播放列表進行排序,
我們要按照sort.Interface定義實現三個方法:
Len() int
Less(i,j int) bool
Swap(i,j)

*/
type byArtist []*Track

func (x byArtist) Len() int {
    return len(x)
}

//按照Artilst排序
func (x byArtist) Less(i, j int) bool {
    return x[i].Artist < x[j].Artist
}

func (x byArtist) Swap(i, j int) {
    x[i], x[j] = x[j], x[i]
}

//程式入口
func main() {
    sort.Sort(byArtist(tracks)) //將tracks轉換為新的byArtist型別
    printTracks(tracks)
}

輸出結果:
file

假設使用者第二次請求 「按照 artist 排序」,我們會對 tracks 進行逆向排序。然而我們不需要定義一個有
顛倒 Less 方法的新型別 byReverseArtist ,因為sort包中提供了 Reverse 函式將排序順序轉換成逆序。

func main() {
    sort.Sort(sort.Reverse(byArtist(tracks)))//逆向排序
    printTracks(tracks)
}

逆向排序的結果:
file

sort 包定義了一個不公開的 struct 型別 reverse,它嵌入了一個 sort.Interface。reverse的Less方法調
用了內嵌的sort.Interface值的Less方法,但是通過交換索引的方式使排序結果變成逆序:

package sort

type reverse struct {
    // This embedded Interface permits Reverse to use the methods of
    // another Interface implementation.
    Interface
}

// Less returns the opposite of the embedded implementation's Less method.
func (r reverse) Less(i, j int) bool {
    return r.Interface.Less(j, i)
}

// Reverse returns the reverse order for data.
func Reverse(data Interface) Interface {
    return &reverse{data}
}

reverse 的另外兩個方法 Len 和 Swap 隱式地由原有內嵌的 sort.Interface 提供。因為reverse是一個不公開的型別,所以匯出函式 Reverse 函式返回一個包含原有sort.Interface 值的 reverse 型別例項。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

不卑不亢,不慌不忙,這才是生活的模樣。

相關文章