简体   繁体   English

如何编写接受任何数字类型的通用 function?

[英]How to write a generic function that accepts any numerical type?

Coming from JS / TS, I wanted to checkout go and make a simple calculator, since there's the difference between int and float, what is the preferred way to write a functions that takes any number?来自 JS / TS,我想签出 go 并制作一个简单的计算器,因为 int 和 float 之间存在差异,编写一个接受任何数字的函数的首选方法是什么?

For example:例如:

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
}

Do I need to convert everything to float (since it "covers" the int range) or do I create a separate functions for each type like addInt(a int, b int) int and addFloat(a float64, b float64) float64 or might there be a more elegant way at all?我是否需要将所有内容都转换为浮点数(因为它“覆盖”了 int 范围),或者我是否为每种类型创建一个单独的函数,如addInt(a int, b int) intaddFloat(a float64, b float64) float64或者可能有更优雅的方式吗?

When generics are added to the language, there will be a better answer当 generics 添加到语言中时,会有更好的答案

Simplest option now is to just convert arguments at the call site.现在最简单的选择是在呼叫站点转换 arguments。

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

Go 1.18 Go 1.18

With the introduction of type parameters in Go 1.18 ( released in beta ), this will be easier to accomplish.随着 Go 1.18(测试版发布)中类型参数的引入,这将更容易实现。

You can define a function parametrized in T and use an interface constraint to restrict T to numeric types.您可以定义在T中参数化的 function 并使用接口约束T限制为数字类型。

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

The constraint Number can be defined with the new constraints package that has been added to the standard library:可以使用已添加到标准库中的新constraints package 来定义约束Number

import "constraints"

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

Where:在哪里:

  • Number is the union of the type sets of constraints.Integer and constraints.Float Numberconstraints.Integer类型集的并集。Integer 和constraints.Float 。浮点数
  • constraints.Integer is the set of all signed and unsigned integer types constraints.Integer是所有有符号和无符号 integer 类型的集合
  • contraints.Float is the set of float types contraints.Float是浮点类型的集合

This will allow you to call add with any two arguments of numeric type.这将允许您使用任意两个数字类型的 arguments 调用add Then in the function body you will be able to use any operation that is supported by all types in the constraint .然后在 function 正文中,您将能够使用约束中所有类型支持的任何操作 So in case of numbers, this includes also arithmetic ops.所以在数字的情况下,这也包括算术运算。 Then declaring similar functions is easy:然后声明类似的函数很容易:

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

Keep in mind that the arguments must have the same type .请记住,arguments必须具有相同的类型 Regardless of generics, you can't use different types;不管generics,不能使用不同的类型; from the specs Operators :从规格运营商

[...] the operand types must be identical unless the operation involves shifts or untyped constants. [...] 操作数类型必须相同,除非操作涉及移位或无类型常量。

Therefore our generic add and multiply functions are defined with only one type parameter T .因此,我们的通用addmultiply函数仅使用一个类型参数T定义。 This implies that you also can't call the add function with untyped constants whose default types are incompatible:这意味着您也不能使用默认类型不兼容的无类型常量调用add function:

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

In this case the compiler will infer the type of T from the first argument 2.5 , which defaults to float64 , and then won't be able to match the type of 2 , which defaults to int .在这种情况下,编译器将从第一个参数2.5 (默认为float64 )推断T的类型,然后无法匹配默认为int2的类型。

Full program:完整程序:

package main

import (
    "constraints"
    "fmt"
)

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://gotipplay.golang.org/p/5QOb6Rd6Ymi游乐场: https://gotipplay.golang.org/p/5QOb6Rd6Ymi

It is possible now thanks to generics, but it's extremely tedious because you have to specify every numeric type by hand in the function declaration.多亏了 generics,现在这成为可能,但它非常繁琐,因为您必须在 function 声明中手动指定每个数字类型。

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

There is a constraints package that provides a more DRY way of defining such functions, but it's experimental and could be removed from the language at some point, so I wouldn't recommend using it.有一个约束package 提供了一种更 DRY 的方式来定义此类函数,但它是实验性的,可能会在某个时候从语言中删除,所以我不建议使用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM