qinghe пре 3 година
родитељ
комит
c285241ed4
7 измењених фајлова са 296 додато и 39 уклоњено
  1. 130
    0
      chapter1-why-generics.slide
  2. 47
    7
      chapter2-constraints.slide
  3. 41
    0
      codes/atomic.go
  4. 0
    32
      codes/graph.go
  5. 31
    0
      codes/print.go
  6. 17
    0
      codes/stringfy.go
  7. 30
    0
      codes/type_inference.go

+ 130
- 0
chapter1-why-generics.slide Прегледај датотеку

@@ -0,0 +1,130 @@
1
+# Go 泛型介绍
2
+
3
+清和
4
+2022-01-22
5
+chenqh721@foxmail.com
6
+
7
+
8
+## Agenda
9
+- Why generic
10
+- Type parameter
11
+- Constraint
12
+- Some cases
13
+
14
+
15
+## Why generic
16
+
17
+## Why generic
18
+
19
+.code codes/atomic.go /OMIT/,/OMIT/
20
+
21
+- 每种类型都得实现一遍
22
+
23
+## Why generic
24
+
25
+.code codes/atomic.go /INTR OMIT/,/INTR OMIT/
26
+
27
+- 无法在编译期检查出错误
28
+- 类型转换影响性能
29
+
30
+
31
+## Type parameters
32
+
33
+## Type parameters
34
+
35
+泛型代码中会用到抽象的数据类型,这个类型叫做`type parameters`. 在运行泛型代码的时候,类型形参会被替换为类型实参。
36
+
37
+.code codes/print.go /PRINT OMIT/,/PRINT OMIT/
38
+
39
+类型T该如何被定义?
40
+
41
+
42
+- 类型参数类似于传统的非类型参数
43
+- 需要区分类型参数与非类型参数
44
+
45
+==>  **需要另外一个可选参数列表来确定类型参数**
46
+
47
+
48
+## Type parameter
49
+
50
+- 在普通参数前面
51
+- 使用方括号[ ]包围
52
+- 类型参数也有「元类型」,也即constraint(约束)
53
+
54
+.code codes/print.go /GEN OMIT/,/GEN OMIT/
55
+
56
+以上示例代码中:
57
+
58
+- `T` 是一个**type parameter**
59
+- **any**是一个**constraint**,意思是T可以是任意类型
60
+- `T` 可以用来规定参数s的类型,也可以用在函数体内
61
+- 不同于普通参数,类型参数名`T` 不可省略
62
+
63
+## Type parameter
64
+
65
+调用泛型函数
66
+
67
+.play codes/print.go /CALL OMIT/,/CALL OMIT/
68
+
69
+- 需指定类型参数,类型参数写在中括号内
70
+- 有多个类型参数时,使用逗号分隔
71
+
72
+
73
+## Constraint
74
+
75
+## Constraint
76
+接下来来个稍微复杂点的例子:
77
+
78
+.play codes/stringfy.go /START OMIT/,/END OMIT/
79
+
80
+- T 可以是任意类型
81
+- 类型 T 可能并没有String方法
82
+
83
+**以上代码并不能运行**
84
+
85
+
86
+## Constraint
87
+
88
+在其他有泛型的语言中也会出现类似的情况。
89
+
90
+例如,在C++中。
91
+
92
+调用v.String()是允许的,在编译过程中,如果发现实际调用时传入的类型实参T没有String方法,那么会报错误,如果有String方法,可以正常编译。
93
+
94
+也就是说:**C++实际上对类型是没有明确限制的**
95
+
96
+
97
+## Constraint
98
+
99
+出于语言风格考虑,Go语言不会采用上述方案。
100
+
101
+- In Go we don't refer to names, such as, in this case, String, and hope that they exist. Go resolves all names to their declarations when they are seen.
102
+- Another reason is that Go is designed to support programming at scale.
103
+
104
+Details see: [Type Parameters Proposal # Constraint](https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#constraints)
105
+
106
+通常,我们希望泛型代码中的类型参数满足一定的条件,这些条件被称为约束(Constraints)。
107
+
108
+Constraints必须能够同时限制调用方传入的类型参数以及泛型函数的具体实现:
109
+1. **调用泛型函数时传入的类型实参必须满足Constraints**
110
+2. **实现泛型函数时必须用Constraints允许的方式来使用这些值(e. g. + - * / > ....)**
111
+
112
+
113
+## Constraint
114
+
115
+首先看一个特殊的constraint:**any**,表示类型参数允许是任何类型。any约束下各类型(T)允许的操作有:
116
+
117
+- 申明对应类型的变量
118
+- 给此类型变量重新赋一个相同类型的值
119
+- 将此类型变量传给其他函数或者作为返回值返回
120
+- 取此类型变量的地址
121
+- 强制类型转换为interface{}类型或者赋值给interface{}类型的变量
122
+- 将类型T强转为类型T(允许但是没啥用)
123
+- 使用类型断言将interface转换为类型T
124
+- 在type switch中使用此类型作为其中一个case
125
+- 定义及使用此类型的复合类型(例如切片)
126
+- 将此类型作为预定义类型的参数,比如new函数
127
+
128
+## Constraint
129
+
130
+

main.slide → chapter2-constraints.slide Прегледај датотеку

@@ -222,17 +222,57 @@ comparable作为一个约束,它也可以被嵌入在其他约束内。
222 222
 - 必须有Hash() uintptr方法
223 223
 
224 224
 
225
+## Type inference(类型推断)
225 226
 
226
-## Mutually referencing type parameters(相互引用类型参数)
227
+## Type inference
228
+可以使用约束类型推断来省略一些代码。在实例化泛型函数或类型时,可以省略部分或全部类型参数。当省略的类型参数可以被推断时,如果只指定部分类型实参,
229
+则这部分类型实参是类型形参的第一个参数。
227 230
 
228
-在一个泛型函数的定义中,类型参数之间可能会相互引用。
231
+举例如下:
229 232
 
230
-例如,要实现图数据结构的一些基本算法。图包括两种结构,`Node`和`Edge`,`Node`应该有个方法Egdes()[]Edge,用于返回和节点相连的边。Edge也有个方法
231
-Nodes()(Node,Node),用于返回一条边两端连接的节点。一个完整的图就可以用[]Node来表示。
233
+.code codes/type_inference.go /MAP OMIT/,/MAP OMIT/
232 234
 
235
+以下的调用方式都是正确的:
233 236
 
234
-## Mutually referencing type parameters
235 237
 
236
-代码如下:
238
+.code codes/type_inference.go /MAP CALL OMIT/,/MAP CALL OMIT/ 
237 239
 
238
-.code codes/graph.go /GRAPH OMIT/,/GRAPH OMIT/
240
+## Type inference
241
+
242
+当类型不能被推断的时候,省略类型参数会报错。
243
+
244
+//例如:
245
+
246
+//.code codes/type_inference.go /ERR OMIT/,/ERR OMIT/
247
+
248
+
249
+## Type unification (类型归并)
250
+
251
+类型推断的基础是类型归并。类型归并用于两种类型之间,描述一种类型是否包含在另一种类型中。其中至少一种是类型参数或者包含类型参数。
252
+
253
+类型归并比较类型间的结构,如果除了类型参数以外的部分结构和类型一致,则可以成功归并。
254
+
255
+
256
+举个例子:
257
+
258
+如果T1和T2是类型参数,[]map[int]bool可以被归并为以下方式:
259
+- []map[int]bool
260
+- T1 (T1 matches []map[int]bool)
261
+- []T1 (T1 matches map[int]bool)
262
+- []map[T1]T2 (T1 matches int, T2 matches bool)
263
+
264
+(上述列表并不完全,可能存在其他归并方式)
265
+
266
+## Type unification
267
+
268
+
269
+
270
+对于[]map[int]bool类型,以下方式就是错误的归并方式:
271
+- int
272
+- struct{}
273
+- []struct{}
274
+- []map[T1]string
275
+
276
+
277
+## Function argument type inference
278
+函数参数类型推断用在进行函数调用并且没有指明类型参数时。当类型参数在实例化

+ 41
- 0
codes/atomic.go Прегледај датотеку

@@ -0,0 +1,41 @@
1
+package example
2
+
3
+// OMIT
4
+func AddInt32(addr *int32, delta int32) int32 {
5
+	// some codes ...
6
+	return 0
7
+}
8
+func AddInt64(addr *int64, delta int64) int64 {
9
+	// some codes ...
10
+	return 0
11
+}
12
+func AddUint32(addr *uint32, delta uint32) uint32 {
13
+	// some codes ...
14
+	return 0
15
+}
16
+func AddUint64(addr *uint64, delta uint64) uint64 {
17
+	// some codes ...
18
+	return 0
19
+}
20
+
21
+// OMIT
22
+
23
+// INTR OMIT
24
+func Add(addr interface{}, delta interface{}) interface{} {
25
+	switch addr.(type) {
26
+	case nil:
27
+		// some codes
28
+	case *int32:
29
+		// some codes
30
+	case *int64:
31
+		// some codes
32
+	case *uint32:
33
+		// some codes
34
+	case *uint64:
35
+		// some codes
36
+	}
37
+
38
+	return nil
39
+}
40
+
41
+// INTR OMIT

+ 0
- 32
codes/graph.go Прегледај датотеку

@@ -1,32 +0,0 @@
1
-
2
-package main
3
-
4
-
5
-// GRAPH OMIT
6
-// NodeConstraint is the type constraint for graph nodes:
7
-// they must have an Edges method that returns the Edge's
8
-// that connect to this Node.
9
-type NodeConstraint[Edge any] interface {
10
-	Edges() []Edge
11
-}
12
-
13
-// EdgeConstraint is the type constraint for graph edges:
14
-// they must have a Nodes method that returns the two Nodes
15
-// that this edge connects.
16
-type EdgeConstraint[Node any] interface {
17
-	Nodes() (from, to Node)
18
-}
19
-
20
-// Graph is a graph composed of nodes and edges.
21
-type Graph[Node NodeConstraint[Edge], Edge EdgeConstraint[Node]] struct { ... }
22
-
23
-// New returns a new graph given a list of nodes.
24
-func New[Node NodeConstraint[Edge], Edge EdgeConstraint[Node]] (nodes []Node) *Graph[Node, Edge] {
25
-	...
26
-}
27
-
28
-// ShortestPath returns the shortest path between two nodes,
29
-// as a list of edges.
30
-func (g *Graph[Node, Edge]) ShortestPath(from, to Node) []Edge { ... }
31
-
32
-// GRAPH OMIT

+ 31
- 0
codes/print.go Прегледај датотеку

@@ -0,0 +1,31 @@
1
+package main
2
+
3
+import "fmt"
4
+
5
+/**
6
+// PRINT OMIT
7
+func Print(s []T) {
8
+	for _, v := range s {
9
+		fmt.Println(v)
10
+	}
11
+}
12
+
13
+// PRINT OMIT
14
+**/
15
+
16
+
17
+// GEN OMIT
18
+func Print[T any](s []T) {
19
+	for _, v := range s {
20
+		fmt.Println(v)
21
+	}
22
+}
23
+// GEN OMIT
24
+
25
+
26
+func main(){
27
+	// CALL OMIT
28
+	Print[int]([]int{1,2,3,4})
29
+	Print[int32](int32{1,2,3,4})
30
+	// CALL OMIT
31
+}

+ 17
- 0
codes/stringfy.go Прегледај датотеку

@@ -0,0 +1,17 @@
1
+package main
2
+
3
+
4
+func main(){
5
+	Stringify([]int{1,2,3})
6
+}
7
+
8
+// START OMIT
9
+// Stringify convert a slice of type T to a string slice.
10
+func Stringify[T any](s []T) (ret []string) {
11
+	for _, v := range s {
12
+		ret = append(ret, v.String())
13
+	}
14
+	return ret
15
+}
16
+
17
+// END OMIT

+ 30
- 0
codes/type_inference.go Прегледај датотеку

@@ -0,0 +1,30 @@
1
+
2
+// MAP OMIT
3
+func Map[F, T any](s []F, f func(F) T) []T { ... }
4
+// MAP OMIT
5
+
6
+
7
+// MAP CALL OMIT
8
+var s []int
9
+f := func(i int) int64 { return int64(i) }
10
+var r []int64
11
+// Specify both type arguments explicitly.
12
+r = Map[int, int64](s, f)
13
+// Specify just the first type argument, for F,
14
+// and let T be inferred.
15
+r = Map[int](s, f)
16
+// Don't specify any type arguments, and let both be inferred.
17
+r = Map(s, f)
18
+
19
+// MAP CALL OMIT
20
+
21
+
22
+// ERR OMIT
23
+
24
+func Double[T interface{ ~int }](v T) T { 
25
+	return v * 2 
26
+}
27
+
28
+Double(1) // 
29
+
30
+// ERR OMIT