Go 1.21.0 带来了什么新特性?min 和 max 内置函数解析
2023-6-16 08:53:42 Author: Go语言中文网(查看原文) 阅读量:13 收藏

阅读本文大概需要 5 分钟。

大家好,我是站长 polarisxu。

Go 1.21.0 是 Go 语言的最新版本,它将在 2023 年 8 月发布,会带来了一些语言和工具的变化。其中一个值得关注的变化是增加了两个新的内置函数 min 和 max,用来对任意可比较类型进行最小值和最大值的操作。这是很常见的需求,现在有内置实现了。本文将介绍这两个函数的背景、规范、实现原理和使用示例。

增加这两个函数的背景

在 Go 语言中,有很多情况需要对一组值进行最小值或最大值的操作,比如排序、统计、筛选等。然而,Go 语言没有提供直接的方法来实现这个功能,开发者需要自己编写循环来完成。(标准库 math 有对应的函数,但只支持接受 float64 类型。)

但这个确实很常见的需求。为此,Go 1.21.0 引入了两个新的内置函数 min 和 max,它们可以对任意可比较类型进行最小值或最大值的操作,无需编写循环或引入第三方库。

长什么样?

根据 Go builtin 文档,min 和 max 的函数原型如下:

// 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

从函数原型可以看出,min 和 max 的参数和返回值都是同一种类型,必须是可比较的有序类型,比如整数、浮点数、字符串等。如果只有一个参数,它就是最小值或最大值。如果有多个参数,min 和 max 会根据 < 运算符来比较大小,并返回最小值或最大值。如果有多个相同的最小值或最大值,min 会返回最左边的一个,max 会返回最右边的一个。如果没有参数,或者参数不是有序类型,min 和 max 编译不通过。

这两个函数的实现原理

min 和 max 是内置函数,它们的实现是在编译器层面完成的,而不是在运行时。具体来说,它们是在编译器的 SSA (Static Single Assignment) 阶段进行转换的,将 min 和 max 的调用转换为对应的循环代码。这样做的好处是可以避免引入新的运行时函数,也可以让编译器有更多的优化空间。

具体的实现代码在 src/cmd/compile/internal/types2/builtins.go 中:(部分代码)

for i, a := range args {
    if a.mode == invalid {
        return
    }

    if !allOrdered(a.typ) {
        check.errorf(a, InvalidMinMaxOperand, invalidArg+"%s cannot be ordered", a)
        return
    }

    // The first argument is already in x and there's nothing left to do.
    if i > 0 {
        check.matchTypes(x, a)
        if x.mode == invalid {
            return
        }

        if !Identical(x.typ, a.typ) {
            check.errorf(a, MismatchedTypes, invalidArg+"mismatched types %s (previous argument) and %s (type of %s)", x.typ, a.typ, a.expr)
            return
        }

        if x.mode == constant_ && a.mode == constant_ {
            if constant.Compare(a.val, op, x.val) {
                *x = *a
            }
        } else {
            x.mode = value
        }
    }
}

可以看到,编译器将 min 的调用转换为一个循环,从第二个参数开始遍历切片,并与第一个参数比较大小,更新最小值/最大值变量。

使用示例

min 和 max 函数的使用非常简单,只需要将要比较的值作为参数传入即可。

下面是一些使用示例:(在线运行地址 https://go.dev/play/p/AQ6HD_gfame?v=gotip)

package main

import (
 "fmt"
 "math"
)

func main() {
 // 比较整数
 fmt.Println(min(123)) // 1
 fmt.Println(max(123)) // 3

 // 比较浮点数
 fmt.Println(min(1.52.53.5)) // 1.5
 fmt.Println(max(1.52.53.5)) // 3.5

 // 浮点数包含 NaN
 fmt.Println(min(1.5, math.NaN(), 3.5)) // NaN
 fmt.Println(max(1.5, math.NaN(), 3.5)) // NaN

 // 比较字符串
 fmt.Println(min("apple""banana""cherry")) // apple
 fmt.Println(max("apple""banana""cherry")) // cherry
}

输出结果:

1
3
1.5
3.5
NaN
NaN
apple
cherry

可以看到,min 和 max 函数可以方便地对不同类型的值进行最小值和最大值的操作,无需编写额外的代码。

总结

本文介绍了 Go 1.21.0 新增的两个内置函数 min 和 max,它们可以对任意可比较类型进行最小值和最大值的操作。目前 Go1.21.0 还未发布,其中的具体实现可能会发生变化。


往期推荐

我是 polarisxu,北大硕士毕业,曾在 360 等知名互联网公司工作,10多年技术研发与架构经验!2012 年接触 Go 语言并创建了 Go 语言中文网!著有《Go语言编程之旅》、开源图书《Go语言标准库》等。

坚持输出技术(包括 Go、Rust 等技术)、职场心得和创业感悟!欢迎关注「polarisxu」一起成长!也欢迎加我微信好友交流:gopherstudio


文章来源: http://mp.weixin.qq.com/s?__biz=MzAxMTA4Njc0OQ==&mid=2651454457&idx=1&sn=83a160ff5051a013662d9ef145e4a2d7&chksm=80bb250bb7ccac1d46507e29a4b813fa207c254b9bcb1d77dabe868a8e65807c05800e36242e#rd
如有侵权请联系:admin#unsafe.sh