提升 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(a []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(a []float64, x float64) int {
}
func IndexOfString(a []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:
-
Filter
-
Map
-
FilterMap
-
FlatMap
-
Reduce
-
ForEach
-
Times
-
Uniq
-
UniqBy
-
GroupBy
-
Chunk
-
PartitionBy
-
Flatten
-
Shuffle
-
Reverse
-
Fill
-
Repeat
-
KeyBy
-
Drop
-
DropRight
-
DropWhile
-
DropRightWhile
-
Reject
-
Count
-
CountBy
Supported helpers for maps:
-
Keys
-
Values
-
PickBy
-
PickByKeys
-
PickByValues
-
OmitBy
-
OmitByKeys
-
OmitByValues
-
Entries
-
FromEntries
-
Invert
-
Assign (merge of maps)
-
MapKeys
-
MapValues
Supported math helpers:
-
Range / RangeFrom / RangeWithSteps
-
Clamp
-
SumBy
Supported helpers for strings:
-
Substring
-
RuneLength
Supported helpers for tuples:
-
T2 -> T9
-
Unpack2 -> Unpack9
-
Zip2 -> Zip9
-
Unzip2 -> Unzip9
Supported intersection helpers:
-
Contains
-
ContainsBy
-
Every
-
EveryBy
-
Some
-
SomeBy
-
None
-
NoneBy
-
Intersect
-
Difference
-
Union
Supported search helpers:
-
IndexOf
-
LastIndexOf
-
Find
-
FindIndexOf
-
FindLastIndexOf
-
Min
-
MinBy
-
Max
-
MaxBy
-
Last
-
Nth
-
Sample
-
Samples
Conditional helpers:
-
Ternary (1 line if/else statement)
-
If / ElseIf / Else
-
Switch / Case / Default
Type manipulation helpers:
-
ToPtr
-
ToSlicePtr
-
ToAnySlice
-
FromAnySlice
-
Empty
-
Coalesce
Concurrency helpers:
-
Attempt
-
AttemptWithDelay
-
Debounce
-
Async
Error handling:
-
Must
-
Try
-
TryCatch
-
TryWithErrorValue
-
TryCatchWithErrorValue
Constraints:
- Clonable
隨着 Go 語言泛型的完善,我相信這些工具函數,會成爲 Go 標準庫的一部分,被更多開發者使用。
現在,先把 lo 用起來吧~~~~
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/zC4kZNAz3wNr_9--xcT8Fg