前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go 1.21.0 新增 3 个内置函数详解

Go 1.21.0 新增 3 个内置函数详解

作者头像
frank.
发布2023-09-08 16:20:11
4300
发布2023-09-08 16:20:11
举报

01

介绍

Go 1.21.0 新增 3 个内置函数,minmax 函数,返回 N 个入参中最小/最大的参数,参数类型为 Ordered(有序类型,即支持比较运算符的类型)。

clear 函数,删除 map 中的所有元素,将切片中的所有元素改为切片类型的零值。

本文我们详细介绍 minmaxclear 的使用方式。

02

Min and max

源码/usr/local/go/src/builtin/builtin.go

代码语言:javascript
复制
// The max built-in function returns the largest value of a fixed number of
// arguments of [cmp.Ordered] types. There must be at least one argument.
// If T is a floating-point type and any of the arguments are NaNs,
// max will return NaN.
func max[T cmp.Ordered](x T, y ...T) T

// The min built-in function returns the smallest value of a fixed number of
// arguments of [cmp.Ordered] types. There must be at least one argument.
// If T is a floating-point type and any of the arguments are NaNs,
// min will return NaN.
func min[T cmp.Ordered](x T, y ...T) T

阅读源码,我们可以发现其是泛型函数,入参是 [cmp.Ordered]cmp 包也是 Go 1.21.0 新增的 package,它提供 3 个函数,分别是 LessCompareisNaN,感兴趣的读者朋友们可以阅读源码,本文将不展开介绍 cmp 包提供的函数。

如果读者朋友们还不熟悉泛型,建议先阅读之前的一篇介绍泛型的文章「Go 1.18 新增三大功能之一“泛型”怎么使用?」。

[cmp.Ordered]类型源码:

代码语言:javascript
复制
// Ordered is a constraint that permits any ordered type: any type
// that supports the operators < <= >= >.
// If future releases of Go add new ordered types,
// this constraint will be modified to include them.
//
// Note that floating-point types may contain NaN ("not-a-number") values.
// An operator such as == or < will always report false when
// comparing a NaN value with any other value, NaN or not.
// See the [Compare] function for a consistent way to compare NaN values.
type Ordered interface {
 ~int | ~int8 | ~int16 | ~int32 | ~int64 |
  ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
  ~float32 | ~float64 |
  ~string
}

内置函数 minmax 分别计算任意数量(至少有一个参数)的可比较类型参数的最小值和最大值。

所谓可比较类型参数,即可以使用运算符比较的参数,比如整型、浮点型、字符串。

如果泛型 T 是浮点数类型,并且任意参数是 NaN("not-a-number"),则函数的返回结果是 NaN

所谓不可比较类型参数,即不可以使用运算符比较的参数,比如 slicemapfunction,它们不可以作为 minmax 的参数。

注意:虽然 slicemapfunction 三种类型不可比较,但是,有个特例,即它们都可以和 nil 比较。

接下来,我们使用 minmax 分别计算整型、浮点型、字符串,代码如下:

示例一

代码语言:javascript
复制
func main() {
    m := min()
    fmt.Println(m) // invalid operation: not enough arguments for min() (expected 1, found 0)
}

阅读上面这段代码,我们没有传入参数,执行代码,返回错误信息。

示例二

代码语言:javascript
复制
func main() {
    s := []int{1, 2, 3}
    m := min(s...)
    fmt.Println(m) // invalid operation: invalid use of ... with built-in min
}

阅读上面这段代码,我们传入切片类型参数,执行代码,返回错误信息。

示例三

代码语言:javascript
复制
func main() {
    var x int
    m := min(x)
    fmt.Println(m) // 0
}

阅读上面这段代码,我们定义一个整型参数 x,并将其作为 min 函数的入参,返回值赋值给参数 m,打印 m 输出的值为 0,即参数 x 的值。

根据打印输出结果,我们可以得出结论,当我们给 min 函数仅传入一个参数时,返回结果和入参的值相等,即 m == x

示例四

代码语言:javascript
复制
func main() {
    var x, y int = 1, 2
    m := min(x, y)
    fmt.Println(m) // 1
}

阅读上面这段代码,我们定义整型参数 xy,分别赋值 12,并将 xy 作为 min 函数的入参,返回值赋值给参数 m,打印 m 输出的值为 1,即参数 xy 中值最小的参数的值。

示例五

代码语言:javascript
复制
func main() {
    c := min(1, 2.0, 3)
    fmt.Printf("%T\t%v\n", c, c) // float64 1
}

阅读上面这段代码,我们给 min 函数传入三个参数,分别是整型参数 1、浮点型参数 2.0 和整型参数 3,返回值赋值给参数 c,打印 c 的类型和值,输出结果为 float64 1,即三个参数中值最小的参数的值。

但是,值最小的参数的类型发生了变化,由整型转换为浮点型,这是因为 min 函数中的参数,如果有浮点型参数,则所有参数都会转换为浮点型参数作比较,因为运行该示例代码的电脑是 64 位操作系统,所以参数 2.0 的类型为 float64

如果我们指定浮点数类型,则参数都会转换为指定的浮点数类型参数作比较。

示例六

代码语言:javascript
复制
func main() {
    t := min("foo", "bar")
    fmt.Println(t) // bar
    t1 := min("", "foo", "bar")
    fmt.Println(t) // ""
}

阅读上面这段代码,我们给 min 函数传入两个参数,分别是字符串 foo 和字符串 bar,返回值赋值为参数 t,打印 t 输出的值为 bar

我们给 min 函数传入三个参数,分别是空字符串、字符串 foo 和字符串 bar,返回值赋值给参数 t1,打印 t1 输出的值为空字符串。

根据 tt1 的值,我们可以得出结论,即如果 min 函数的入参为字符串类型的参数,则按照字典序返回最小的字符串,如果有空字符串,则返回空字符串。

示例七

代码语言:javascript
复制
func main() {
    m := min(3.14, math.NaN(), 1.0)
    fmt.Println(m) // NaN
}

阅读上面这段代码,参数为浮点数类型,包含 NaN,返回结果则是 NaN

函数 max 和函数 min 的使用方式相同,返回结果相反。

在项目开发中,我们可以使用 minmax 直接比较一组数据,得出最小/最大的结果值,而不再需要循环遍历。

特别提示:

  1. 整型参数,minmax 的参数可以交换和组合。
  2. 字符串类型参数,minmax 的参数逐个字节比较,得出最小/最大的字符串,参数可以交换和组合。
  3. 浮点型参数-0.00.0 作为参数,-0.0 小于 0.0;负无穷大,小于任意其它数值;正无穷大,大于任意其它数值。
  4. minmax 的任意参数是 NaN[1],则返回结果是 NaN ("not-a-number") 值。

03

Clear

代码语言:javascript
复制
// The clear built-in function clears maps and slices.
// For maps, clear deletes all entries, resulting in an empty map.
// For slices, clear sets all elements up to the length of the slice
// to the zero value of the respective element type. If the argument
// type is a type parameter, the type parameter's type set must
// contain only map or slice types, and clear performs the operation
// implied by the type argument.
func clear[T ~[]Type | ~map[Type]Type1](t T)

阅读源码,我们可以发现 clear 也是泛型函数,入参为 mapslicetype parameter (类型参数),如果是 map,则删除 map 中的所有元素,返回一个空 map;如果是 slice,则将 slice 中的所有元素改为切片类型的零值。

需要注意的是,如果函数 clear 的入参是 type parameter (类型参数),则类型参数的集合必须仅包含 mapslice,函数 clear 则按照类型参数集合中的字段类型,执行相应的操作。

注意:如果 mapslicenil,函数 clear 的执行则是无效操作。

示例一

代码语言:javascript
复制
func main() {
    s := []int{1, 2, 3}
    fmt.Printf("len=%d\t s=%+v\n", len(s), s) // len=3      s=[1 2 3]
    clear(s)
    fmt.Printf("len=%d\t s=%+v\n", len(s), s) // len=3      s=[0 0 0]
}

阅读上面这段代码,我们可以发现使用 clear 执行的切片,其长度不变,所有元素变为切片类型的零值。

示例二

代码语言:javascript
复制
func main() {
    m := map[string]int{"go": 100, "php": 80}
    fmt.Printf("len=%d\tm=%+v\n", len(m), m) // len=2   m=map[go:100 php:80]
    clear(m)
    fmt.Printf("len=%d\tm=%+v\n", len(m), m) // len=0   m=map[]
}

阅读上面这段代码,我们可以发现使用 clear 执行的 map,其长度变为 0,所有元素被删除。

示例三

代码语言:javascript
复制
func main() {
    d := []Data{
        {
            User:   map[int]string{1: "frank", 2: "lucy"},
            Salary: map[string]int{"frank": 1000, "lucy": 2000},
        },
    }
    fmt.Printf("d=%+v\n", d) // d=[{User:map[1:frank 2:lucy] Salary:map[frank:1000 lucy:2000]}]
    clear(d)
    fmt.Printf("d=%+v\n", d) // d=[{User:map[] Salary:map[]}]
    
    d1 := []Data1{
        {
            User:   "frank",
            Salary: 1000,
        },
    }
    fmt.Printf("d1=%+v\n", d1) // d1=[{User:frank Salary:1000}]
    clear(d1)
    fmt.Printf("d1=%+v\n", d1) // d1=[{User: Salary:0}]
}

type Data struct {
    User   map[int]string
    Salary map[string]int
}

type Data1 struct {
    User   string
    Salary int
}

阅读上面这段代码,我们可以发现使用 clear 执行类型参数集合参数 d 的类型是 sliceslice类型的参数类型是 structstruct 的字段类型为 map,返回结果是按照 struct 的字段类型做相应处理,该示例是删除 map 中的所有元素;参数 d1 同理。

关于类型参数的示例,阅读起来比较烧脑,建议读者朋友们运行代码加深理解。

在项目开发中,我们可以使用函数 clear 删除 map 中的元素,替代通过循环遍历调用 delete 删除 map 中的元素;使用函数 clearslice 中的元素的值修改为切片类型的零值,替代通过循环遍历修改切片中的元素的值为切片类型的零值。

04

总结

本文我们介绍 Go 1.21.0 新增的 3 个内置函数,通过示例代码介绍函数的使用方式和注意事项。

读者朋友们可以尝试在项目开发中使用新增的 3 个内置函数,逐步加深对这 3 个新增内置函数的理解。

关于其它内置函数,我们在之前的文章「Go 语言 15 个内置函数详解」中已经介绍,建议还没有阅读的读者朋友们一起阅读。

推荐阅读

  1. Go 语言怎么使用类型转换和类型断言?
  2. Go 配置管理库 Viper 怎么读取结构体嵌套的配置信息?
  3. Go 语言实现创建型设计模式 - 工厂模式
  4. Go 语言各个版本支持 Go Modules 的演进史
  5. Go 1.18 新增三大功能之一“泛型”怎么使用?

参考资料

[1]

NaN: https://en.wikipedia.org/wiki/NaN

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-08-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Golang语言开发栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 推荐阅读
  • 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档