在 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的例子,假如不用反射,要想支持更多的类型,就得定义很多相似名称的函数,如下所示。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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语言的泛型支持之前,要解决这个问题,只能通过反射。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 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函数,在该库中是这么实现的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 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

upported 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:

Function helpers:

Concurrency helpers:

Error handling:

Constraints:

  • Clonable

随着Go语言泛型的完善,我相信这些工具函数,会成为Go标准库的一部分,被更多开发者使用。

现在,先把lo用起来吧~~~~

本文为原创文章,转载注明出处,欢迎扫码关注公众号flysnow_org或者网站 https://www.flysnow.org/ ,第一时间看后续精彩文章。觉得好的话,请猛击文章右下角「在看」,感谢支持。

扫码关注