提升 Go 編碼效率,拒絕加班,真香

在 Go 語言中,slice、map 都是我們常用的基礎類型,通過它們,我們可以很容易的使用數據。但是你有沒有發現,爲了對這兩種數據進行處理,你不得不編寫很多工具函數?

比如,從 slice 切片中查找一個元素的位置?這種查找又分爲從前查找、從後查找。

又比如,獲取 map 的所有 keys?或者所有的 value?

再比如,JS 語言數組的 map、reduce、filter 函數,這在編程中非常好用,但是遺憾的是 Go 標準庫沒有提供。

這些例子,還有很多,而且都是我們編程中常用的工具函數,但是我們的 Go SDK 卻沒有。

但是我們又需要怎麼辦?一種辦法,是我們自己編寫一個工具包,用於給項目使用,但是這個工具包的維護,又是一個問題,需要人力。

第二種方式,就是用開源的庫,經過充分的測試、驗證,並且經常更新,所以可以保障。

因爲我們遇到的是常見的問題,所以有人做了開源庫,以供大家使用。這種工具庫以 Go 1.18 爲分界線,Go 1.18 之前有一款比較出名的是 go-funk。

很有意思的名字,Repo 地址是 https://github.com/thoas/go-funk。官方介紹就是一個 Go 語言工具庫:

A modern Go utility library which provides helpers (map, find, contains, filter, ...)

它提供了很多好用的函數:Contains、Difference、IndexOf、LastIndexOf 等等,具體更多的可以參考它的網站。

但是它有一個致命的問題,就是用到了反射。這也是沒辦法的事情,因爲在 Go 泛型沒有支持之前,只能通過反射才能寫出滿足不同類型的函數。

舉個 IndexOf 的例子,假如不用反射,要想支持更多的類型,就得定義很多相似名稱的函數,如下所示。

func IndexOfBool([]bool, x bool) int {
}
func IndexOfInt(a []int, x int) int {
}
func IndexOfInt32(a []int32, x int32) int {
}
func IndexOfInt64(a []int64, x int64) int {
}
func IndexOfUInt(a []uint, x uint) int {
}
func IndexOfUInt32(a []uint32, x uint32) int {
}
func IndexOfUInt64(a []uint64, x uint64) int {
}
func IndexOfFloat64([]float64, x float64) int {
}
func IndexOfString([]string, x string) int {
}

以上函數行不行?當然行,但是會寫很多重複的代碼,並且看着也怪怪的。

在 Go 語言的泛型支持之前,要解決這個問題,只能通過反射。

// IndexOf gets the index at which the first occurrence of value is found in array or return -1
// if the value cannot be found
func IndexOf(in interface{}, elem interface{}) int {
  inValue := reflect.ValueOf(in)
  elemValue := reflect.ValueOf(elem)
  inType := inValue.Type()
  if inType.Kind() == reflect.String {
    return strings.Index(inValue.String(), elemValue.String())
  }
  if inType.Kind() == reflect.Slice {
    equalTo := equal(elem)
    for i := 0; i < inValue.Len(); i++ {
      if equalTo(reflect.Value{}, inValue.Index(i)) {
        return i
      }
    }
  }
  return -1
}

泛型代碼複雜,並且效率低。。。

那麼 Go 1.18 之後已經支持了泛型,能不能用泛型來重寫呢?

答案是:當然可以,並且已經有人這麼做了。這個庫就是 https://github.com/samber/lo ,也是我們今天要介紹的主角,開源 3 個月,6000 多 star!!!

它是基於 Go 泛型實現,沒有用到反射,效率高,代碼簡潔。比如剛剛的 IndexOf 函數,在該庫中是這麼實現的:

// IndexOf returns the index at which the first occurrence of a value is found in an array or return -1
// if the value cannot be found.
func IndexOf[T comparable](collection []T, element T) int {
  for i, item := range collection {
    if item == element {
      return i
    }
  }
  return -1
}

只需要 T 被約束爲 comparable 的,就可以使用 == 符號進行比較了,整體代碼非常簡單,並且沒有反射。

IndexOf 只是 lo 幾十個函數中的一個,這些函數基本上覆蓋了 slice、map、string 等方方面面,涉及查找、比較大小、生成、map、reduce、過濾、填充、反轉、分組等等,使用方法和示例,可以參考 go doc 文檔。https://pkg.go.dev/github.com/samber/lo

Supported helpers for slices:

Supported helpers for maps:

Supported math helpers:

Supported helpers for strings:

Supported helpers for tuples:

Supported intersection helpers:

Supported search helpers:

Conditional helpers:

Type manipulation helpers:

Concurrency helpers:

Error handling:

Constraints:

隨着 Go 語言泛型的完善,我相信這些工具函數,會成爲 Go 標準庫的一部分,被更多開發者使用。

現在,先把 lo 用起來吧~~~~

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/zC4kZNAz3wNr_9--xcT8Fg