Go语言 如何编写一个接受任何数值类型的泛型函数?

v8wbuo2f  于 2023-08-01  发布在  Go
关注(0)|答案(3)|浏览(114)

从JavaScript和TypeScript开始,我想看看Go并制作一个简单的计算器。既然int和float之间有区别,那么写一个接受任何数字的函数的首选方法是什么?
举例来说:

package main

func add(a float64, b float64) float64 {
  return a + b;
}

func main() {
  a := 1;
  b := 2;
  fmt.Println(add(1, 2)); // 3
  fmt.Println(add(a, b)); // Cannot use a (type int) as type float64 in argument to add
  fmt.Println(add(1.5, 3.2)); // 4.7
  fmt.Println(add(2.5, 2)); // 4.5
}

字符串
我是否需要将所有内容都转换为float(因为它“覆盖”了int范围),或者我是否需要为每种类型创建一个单独的函数,如addInt(a int, b int) intaddFloat(a float64, b float64) float64?或者有更优雅的方式吗?

cidc1ykv

cidc1ykv1#

Go 1.18及以上版本

随着Go 1.18中类型参数的引入,这更容易实现。
您可以在T中定义一个参数化函数,并使用接口约束将T限制为数值类型。

func add[T Number](a, b T) T {
    return a + b
}

字符串
约束Number可以使用golang.org/x/exp/constraints package定义(仍处于实验阶段):

import "golang.org/x/exp/constraints"

type Number interface {
    constraints.Integer | constraints.Float
}


地点:

  • Numberconstraints.Integerconstraints.Float的类型集的并集
  • constraints.Integer是所有有符号和无符号整数类型的集合
  • contraints.Float是float类型的集合

这将允许您使用任意两个数值类型的参数调用add。然后在函数体中就可以使用任何约束中 * 所有 * 类型支持的操作。所以在数字的情况下,这也包括算术运算符。然后声明类似的函数很容易:

func multiply[T Number](a, b T) T {
    return a * b
}


请记住,参数必须具有相同的类型。不管泛型,你不能使用不同的类型;从规格Operators:
[...]操作数类型必须相同,除非操作涉及移位或无类型常量。
因此,我们的泛型addmultiply函数只定义了一个类型参数T。这意味着你也不能用默认类型不兼容的无类型常量调用add函数:

add(2.5, 2) // won't compile


在这种情况下,编译器将从第一个参数2.5推断T的类型,默认为float64,然后将无法匹配2的类型,默认为int
完整程序:

package main

import (
    "fmt"

    "golang.org/x/exp/constraints"
)

type Number interface {
    constraints.Integer | constraints.Float
}

func main() {
    a := 1
    b := 2
    
    fmt.Println(add(1, 2))     // 3
    fmt.Println(add(a, b))     // 3
    fmt.Println(add(1.5, 3.2)) // 4.7
    // fmt.Println(add(2.5, 2)) // default type int of 2 does not match inferred type float64 for T
}

func add[T Number](a, b T) T {
    return a + b
}


Playground:https://go.dev/play/p/rdqi3_-EdHp

**警告:**由于这些函数也处理浮点数,请记住浮点数可以容纳NaN值和无穷大。
关于复数

Go语言有complex64complex128预声明类型。您也可以在Number约束中使用它们:

import "golang.org/x/exp/constraints"

type Number interface {
    constraints.Integer | constraints.Float | constraints.Complex
}


这并不限制这些通用函数的功能:整数和浮点数支持的算术运算符(仅+-*/)以及所有顺序运算符也支持复杂类型。余数运算符%和按位运算符仅受整数支持,因此受限制为constraints.Integer的类型参数支持。

rbl8hiat

rbl8hiat2#

**直到Go 1.17(pre-generics)。**查看其他答案以获得更新的解决方案

最简单的选择是在调用点转换参数。

add(float64(a), float64(b))

字符串

7gyucuyw

7gyucuyw3#

由于泛型的出现,现在这是可能的,但这非常繁琐,因为你必须在函数声明中手工指定每个数值类型。

// Adds two integers and returns the result together with a boolean
// which indicates whether an overflow has occurred
func AddInt[I int | uint | int8 | uint8 | int16 | uint16 | int32 | uint32 | int64 | uint64](a, b I) (I, bool) {
    c := a + b
    if (c > a) == (b > 0) {
        return c, true
    }
    return c, false
}

字符串
有一个constraints包提供了一种更DRY的方式来定义这样的函数,但它是实验性的,可能会在某个时候从语言中删除,所以我不建议使用它。

相关问题