從插入排序來理解 Go 的接口

插入排序

根據插入排序的思想,咱們很容易就能夠完成插入排序的代碼以下。bash

func insertionSort(data []int) {
    lo, hi := 0, len(data) - 1
    for i := lo + 1; i < hi; i++ {
        for j := i; j > lo && data[j] < data[j-1]; j-- {
            data[j], data[j-1] = data[j-1], data[j]
        }
    }
}
複製代碼

咱們能夠驗證一下,確實沒有問題。函數

package main

import (
    "fmt"
)

func main()  {
    nums := []int{2, 3, 4, 1, 7, 9, 10, 21, 17}
    insertionSort(nums)
    fmt.Println(nums)
}
複製代碼

代碼輸出爲,結果正確ui

[1 2 3 4 7 9 10 17 21]
複製代碼

問題

好,如今問題來了,都知道 Go 是靜態語言,那麼就意味着不一樣的數據類型可能致使上述的插入排序不可用。好比說,某天產品想要支持 uint32 的插入排序。嗯,很簡單,直接 Ctrl+c + Ctrl+c 稍微修改一下。spa

func insertionSortUint32(data []uint32) {
    lo, hi := 0, len(data) - 1
    for i := lo + 1; i < hi; i++ {
        for j := i; j > lo && data[j] < data[j-1]; j-- {
            data[j], data[j-1] = data[j-1], data[j]
        }
    }
}
複製代碼

誰知道哪天產品腦子又抽風,他想要支持 float32 類型的插入排序,代碼可能又得加幾行。code

func insertionSortFloat32(data []float32) {
    lo, hi := 0, len(data) - 1
    for i := lo + 1; i < hi; i++ {
        for j := i; j > lo && data[j] < data[j-1]; j-- {
            data[j], data[j-1] = data[j-1], data[j]
        }
    }
}
複製代碼

好像還看得下去,咱們知道 Go 中的類型可不止這 3 種,再這麼被搞下去是否是要爆炸了?不要緊,咱們有強大的 IDE 能夠快速實現。😏😏😏cdn

2332

好了,開個玩笑。若是咱們是提供一個庫的形式,使用者須要一個類型,咱們就得加一個類型支持,這樣就無法搞事了😂blog

解決

首先,回到上訴的三個類型的排序中來,咱們能夠發現這幾個排序除了數據類型是基本一致的。若是咱們想用一個函數來支持全部的數據類型,咱們是否是隻能使用 interface來實現這個功能?可是 interface 又不支持運算操做,若是斷言出來,仍是跟之前同樣麻煩。咱們看看代碼中須要對數據進行運算操做的地方。排序

發現排序中只有len(data)data[j] < data[j-1]data[j], data[j-1] = data[j-1], data[j]這三種操做 interface 不支持。若是咱們讓 interface 實現這三個方法不就解決了個人問題了嗎?接下來咱們經過這種思路修改一下咱們的插入排序 。代碼以下,接口

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

func insertionSort(data Data) {
    lo, hi := 0, data.Len() - 1
    for i := lo + 1; i < hi; i++ {
        for j := i; j > lo && data.Less(j, j-1); j-- {
            data.Swap(j, j-1)
        }
    }
}
複製代碼

咱們使用了interface來替代寫死的數據類型。若是調用方使用,只要實現 Data 接口就好了。string

package main

import (
    "fmt"
)

type Uint32Slice []uint32

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

type Float32Slice []float32

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


func main()  {
    nums := Uint32Slice{2, 3, 4, 1, 7, 9, 10, 21, 17}
    insertionSort(nums)
    fmt.Println(nums)

    float32Nums := Float32Slice{2, 3, 4, 1, 7, 9, 10, 21, 17}
    insertionSort(float32Nums)
    fmt.Println(float32Nums)
}
複製代碼

能夠驗證,結果沒有問題。

[1 2 3 4 7 9 10 21 17]
[1 2 3 4 7 9 10 21 17]
複製代碼

總結

咱們經過接口實現了一個支持多種數據類型的插入排序,調用者只須要實現 Data 這個接口就可使用了,而不用去修改插入排序原有的函數定義。這樣使得咱們的代碼抽象度更高也更靈活,當咱們面臨相似的需求時,接口就是答案。

相關文章
相關標籤/搜索