从插入排序来理解 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 这个接口就可使用了,而不用去修改插入排序原有的函数定义。这样使得咱们的代码抽象度更高也更灵活,当咱们面临相似的需求时,接口就是答案。

相关文章
相关标签/搜索