go泛型实践

之前go在1.18 dev版本中支持了泛型,当时大致的看了一下,但是考虑到是dev版本可能功能并没有完全确定,所以并没有进行练习。今天更新到go1.18 darwin/arm64发现可以直接用泛型了,写篇文章记下笔记。

泛型程序设计(generic programming)是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。各种程序设计语言和其编译器、运行环境对泛型的支持均不一样。Ada、Delphi、Eiffel、Java、C#、F#、Swift 和 Visual Basic .NET 称之为泛型(generics);ML、Scala 和 Haskell 称之为参数多态(parametric polymorphism);C++ 和 D称之为模板。具有广泛影响的1994年版的《Design Patterns》一书称之为参数化类型(parameterized type)。

之前在使用RUST时经常用到泛型,泛型重要特征之一就是泛型约束(类型约束)。 通俗的讲,就是我们怎么用看起来像一种类型的表现方式来表示对多种数据类型的支持。笔者认为泛型是针对于强类型语言,提供一种类似弱类型语言的使用体验,泛型可以让开发者书写更少的代码片段,能够提升开发效率,但是并不能提升语言的执行效率,甚至会降低一部分执行效率(取决于泛型支持是在编译时的支持还是运行时的支持),这也是go迟迟没有支持泛型的原因。

下面的python代码中实现了一个sum函数,对于参数a,bpython中我们是可以传递整数,浮点数,字符串甚至与数组等支持+预算的数据类型。

1
2
def sum(a,b):
    return a+b

go之前不支持泛型的版本,我们需要针对每一种类型都做一次函数实现以支撑不同类型的调用。

先看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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package main

import "fmt"

type Number interface {
    int64 | float64
}

func main() {
    // Initialize a map for the integer values
    ints := map[string]int64{
        "first": 34,
        "second": 12,
    }

    // Initialize a map for the float values
    floats := map[string]float64{
        "first": 35.98,
        "second": 26.99,
    }

    fmt.Printf("Non-Generic Sums: %v and %v\n",
        SumInts(ints),
        SumFloats(floats))

    fmt.Printf("Generic Sums: %v and %v\n",
        SumIntsOrFloats[string, int64](ints),
        SumIntsOrFloats[string, float64](floats))

    fmt.Printf("Generic Sums, type parameters inferred: %v and %v\n",
        SumIntsOrFloats(ints),
        SumIntsOrFloats(floats))

    fmt.Printf("Generic Sums with Constraint: %v and %v\n",
        SumNumbers(ints),
        SumNumbers(floats))
}

// SumInts adds together the values of m.
func SumInts(m map[string]int64) int64 {
    var s int64
    for _, v := range m {
        s += v
    }
    return s
}

// SumFloats adds together the values of m.
func SumFloats(m map[string]float64) float64 {
    var s float64
    for _, v := range m {
        s += v
    }
    return s
}

// SumIntsOrFloats sums the values of map m. It supports both floats and integers
// as map values.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}

// SumNumbers sums the values of map m. Its supports both integers
// and floats as map values.
func SumNumbers[K comparable, V Number](m map[K]V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}

从上面的代码我们可以看到,go通过|运算符来实现类型约束,并且可以通过interface对其类型进行包装.官方示例为对map中的value进行求和.这里再实现一个类似前面提到的python sum函数的go泛型实现:

1
2
3
4
5
6
7
8
type Number interface {
	int64 | float64 | int
}

func sum[V Number](a, b V) V {

	return a + b
}
Licensed under CC BY-NC-SA 4.0
Built with Hugo
主题 StackJimmy 设计