Bladeren bron

2022-04-01 23:19

陈超 3 jaren geleden
bovenliggende
commit
c772e9774d

+ 8
- 20
chapter1-why-generics.slide Bestand weergeven

@@ -41,7 +41,7 @@ https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-param
41 41
 
42 42
 .code codes/atomic.go /INTR OMIT/,/INTR OMIT/
43 43
 
44
-- 需要写很多”无用“代码
44
+- 需要写很多代码
45 45
 - 无法在编译期检查出错误
46 46
 - 类型转换影响性能
47 47
 
@@ -65,7 +65,7 @@ https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-param
65 65
 - 使用方括号[ ]包围
66 66
 - 类型参数也有「元类型」,也即constraint(约束)
67 67
 
68
-.code codes/print.go /GEN OMIT/,/GEN OMIT/
68
+.code -edit codes/print.go /GEN OMIT/,/GEN OMIT/
69 69
 
70 70
 以上示例代码中:
71 71
 
@@ -106,7 +106,7 @@ https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-param
106 106
 
107 107
 调用v.String()是允许的,在编译过程中,如果发现实际调用时传入的类型实参T没有String方法,那么会报错误,如果有String方法,可以正常编译。
108 108
 
109
-也就是说:**C++实际上对类型是没有明确限制的**
109
+也就是说:**C++实际上对传入类型是没有明确限制的**
110 110
 
111 111
 
112 112
 ## Constraint
@@ -125,21 +125,9 @@ Constraints必须能够同时限制调用方传入的类型参数以及泛型函
125 125
 2. **实现泛型函数时必须用Constraints允许的方式来使用这些值(e. g. + - * / > ....)**
126 126
 
127 127
 
128
-## Constraint
129
-
130
-在继续深入讨论约束之前,首先看一个特殊的constraint:**any**,表示类型参数允许是任何类型。any约束下各类型(T)允许的操作有:
131
-
132
-- 申明对应类型的变量
133
-- 给此类型变量重新赋一个相同类型的值
134
-- 将此类型变量传给其他函数或者作为返回值返回
135
-- 取此类型变量的地址
136
-- 强制类型转换为interface{}类型或者赋值给interface{}类型的变量
137
-- 将类型T强转为类型T(允许但是没啥用)
138
-- 使用类型断言将interface转换为类型T
139
-- 在type switch中使用此类型作为其中一个case
140
-- 定义及使用此类型的复合类型(例如切片)
141
-- 将此类型作为预定义类型的参数,比如new函数
142
-
143
-## Constraint
144
-
128
+## Summary
129
+综上:
130
+- 函数除了普通参数外,可以额外的类型参数,类型参数使用`[]`包围
131
+- 这个类型参数可以作为普通参数的类型,也可以直接在函数体内使用
132
+- 每个参数类型都有个约束,就像普通参数都对应一个类型
145 133
 

+ 138
- 14
chapter2-constraints.slide Bestand weergeven

@@ -1,6 +1,27 @@
1 1
 # Go generic
2
-Qinghe
3
-21 Jan 2022
2
+
3
+清和
4
+2022-03-31
5
+
6
+
7
+## Constraint
8
+
9
+
10
+## Constraint
11
+
12
+在继续深入讨论约束之前,首先看一个特殊的constraint:**any**,表示类型参数允许是任何类型。any约束下各类型(T)允许的操作有:
13
+
14
+- 申明对应类型的变量
15
+- 给此类型变量重新赋一个相同类型的值
16
+- 将此类型变量传给其他函数或者作为返回值返回
17
+- 取此类型变量的地址
18
+- 强制类型转换为interface{}类型或者赋值给interface{}类型的变量
19
+- 将类型T强转为类型T(允许但是没啥用)
20
+- 使用类型断言将interface转换为类型T
21
+- 在type switch中使用此类型作为其中一个case
22
+- 定义及使用此类型的复合类型(例如切片)
23
+- 将此类型作为预定义函数的参数,比如new函数
24
+
4 25
 
5 26
 ## Defining constraints
6 27
 constraints的作用:
@@ -103,15 +124,35 @@ interface即可满足上面的需求,因此继续沿用interface类型来定
103 124
 
104 125
 ## Operators
105 126
 
106
-- 除了两种例外情况(后面会将),所有的四则运算、大小比较、逻辑运算只允许被用在预定义类型或者底层为预定义类型的自定义类型中。
107
-- instead of saying which operators a constraint should support, we can say which types a constraint should accept. We do this by defining a type set for a constraint.
127
+- 除了两种例外情况(后面会讲),所有的四则运算、大小比较、逻辑运算只允许被用在预定义类型或者底层为预定义类型的自定义类型中。
128
+- instead of saying which operators a constraint should support, we can say which types a constraint should accept.
129
+(与其规定一个约束能够使用哪些操作,不如规定一个约束可以接受哪些类型)
108 130
 
131
+这一想法可以通过定义一个约束的类型集来实现。
109 132
 
110 133
 ## Type sets
111 134
 
112 135
 ## Type sets
113 136
 
114
-todo
137
+每种类型都有相关联的类型集:
138
+- 对于非接口类型T,其对应的类型集为{T},集合中仅包含类型T自己
139
+- 对于接口类型,其对应的类型集为所有实现了接口的类型(无限个)
140
+
141
+接口内包含元素:
142
+1. 函数签名
143
+2. 其他接口
144
+
145
+.code codes/interface.go /INTERFACE OMIT/,/INTERFACE OMIT/
146
+
147
+
148
+
149
+## Type sets
150
+
151
+.image type_set.png  400 600
152
+
153
+**接口类型的类型集为接口类型内各元素的类型集的交集**
154
+
155
+## Constraint elements
115 156
 
116 157
 ## Constraint elements
117 158
 在interface类型中额外定义一些元素作为约束,这些元素包括:
@@ -194,7 +235,7 @@ todo
194 235
 
195 236
 现在,下面的代码就可以运行了
196 237
 
197
-.code codes/operator.go /OPERATOR MIN OMIT/,/OPERATOR MIN OMIT/
238
+.code codes/operator.go /OPERATOR MIN VALID OMIT/,/OPERATOR MIN VALID OMIT/
198 239
 
199 240
 ## Comparable types in constraints
200 241
 
@@ -222,13 +263,12 @@ comparable作为一个约束,它也可以被嵌入在其他约束内。
222 263
 - 必须有Hash() uintptr方法
223 264
 
224 265
 
225
-## Type inference(类型推断)
266
+## Type inference
226 267
 
227 268
 ## Type inference
228
-可以使用约束类型推断来省略一些代码。在实例化泛型函数或类型时,可以省略部分或全部类型参数。当省略的类型参数可以被推断时,如果只指定部分类型实参,
229
-则这部分类型实参是类型形参的第一个参数。
269
+在很多情况下,可以使用类型推断来避免显示指定一个或多个类型参数。可以使用**函数参数类型推断**来根据非类型参数的类型来确定类型参数的值。也可以使用**约束类型推断**来根据已知的参数类型确定其他约束的类型参数值。
230 270
 
231
-举例如下:
271
+当省略的类型参数可以被推断时,如果只指定部分类型实参,则这部分类型实参依次作为类型形参的前面部分参数值。
232 272
 
233 273
 .code codes/type_inference.go /MAP OMIT/,/MAP OMIT/
234 274
 
@@ -330,15 +370,99 @@ comparable作为一个约束,它也可以被嵌入在其他约束内。
330 370
 - NewPair(2, 2.5) => `compilation error`
331 371
 
332 372
 
373
+## Constraint type inference
374
+
375
+## Constraint type inference
376
+
377
+约束类型推断允许基于类型参数约束,根据一个类型参数实参推断出另外一个类型参数的值。
333 378
 
334
-## Constraint type inference (约束类型推断)
379
+约束类型推断有两种适用场景:
380
+1. 某个类型参数的约束中用到了其他类型参数
381
+2. 某个类型参数的约束是基于其他类型参数构成的
335 382
 
336 383
 
384
+约束类型推断的过程描述起来可能会比较复杂,但是通过几个典型的详细的例子,推断的过程会比较清晰。
337 385
 
338
-## Constraint type inference
339 386
 
340
-约束类型推断可以基于类型参数约束来从一个类型参数中推断另外一个类型参数的值。
387
+## Element constraint example
388
+
389
+例:实现一个函数,将切片内的所有元素都乘以2,返回新的切片。
390
+
391
+.code codes/constraint_inference.go /DOUBLE DEFINATION OMIT/,/DOUBLE DEFINATION OMIT/
392
+
393
+但是如果我们定义一个命名类型,然后再去调用呢?
394
+
395
+
396
+.code codes/constraint_inference.go /CALL DOUBLE ERROR OMIT/,/CALL DOUBLE ERROR OMIT/
397
+
398
+
399
+## Element constraint example
400
+引入一个新的类型参数:
401
+
402
+.code codes/constraint_inference.go /DOUBLE DEFINED OMIT/,/DOUBLE DEFINED OMIT/
403
+
404
+通过显式指定类型参数,可以得到想要的返回值类型:
405
+
406
+.code codes/constraint_inference.go /CALL DOUBLE OMIT/,/CALL DOUBLE OMIT/
407
+
408
+
409
+## Element constraint example
410
+
411
+那么这个函数调用是否可以简化为以下形式呢?
412
+
413
+
414
+.code codes/constraint_inference.go /CALL DOUBLE INFERENCE OMIT/,/CALL DOUBLE INFERENCE OMIT/
415
+
416
+答案是:**可以的**
417
+
418
+虽然是用函数参数类型推断无法完全推断出来,因为类型参数E没有入参与之进行归并,但是函数参数类型推断和约束类型推断结合起来,就可以了。
419
+
420
+1. 应用函数类型推断,可以得到{ S -> MySlice}
421
+2. 应用约束类型推断,归并[]E与MySlice,得到 {S -> MySlice, E -> int}
422
+3. 完成
423
+
424
+可以推断出完整调用:
425
+
426
+.code codes/constraint_inference.go /CALL DOUBLE INFERENCE WHOLE OMIT/,/CALL DOUBLE INFERENCE WHOLE OMIT/
427
+
428
+
429
+
430
+## Pointer method example
431
+
432
+再看另外一个例子:
433
+
434
+.code codes/constraint_inference.go /POINTER METHOD OMIT/,/POINTER METHOD OMIT/
435
+
436
+## Pointer method example
437
+
438
+调用示例:
439
+
440
+.play -edit -numbers codes/constraint_inference2.go /POINTER METHOD CALL OMIT/,/POINTER METHOD CALL OMIT/
441
+
442
+
443
+类型`Settable`并没有方法`Set(string)`,类型`*Settable`才有。
444
+
445
+`*Settable`类型零值为nil,调用Set方法会造成panic。
446
+
447
+
448
+## Pointer method example
449
+
450
+我们可以将两种类型都传:
451
+
452
+.code codes/constraint_inference3.go /SETTER2 OMIT/,/SETTER2 OMIT/
453
+
454
+调用 :
455
+
456
+.play codes/constraint_inference3.go /SETTER2 CALL OMIT/,/SETTER2 CALL OMIT/
457
+
458
+## Pointer method example
459
+
460
+前面那样调用看起来会比较奇怪,因为需要重复传`Settable`参数,不过可以通过约束类型推断来减少重复工作。
341 461
 
342
-略
462
+.code codes/constraint_inference3.go /SETTER3 CALL OMIT/,/SETTER3 CALL OMIT/
343 463
 
464
+类型推断过程:
344 465
 
466
+1. {T -> Settable}
467
+2. {T -> Settable, PT -> *T}
468
+3. {T -> Settable, PT -> *Settable}

+ 3
- 2
chapter3-others-about-generics.slide Bestand weergeven

@@ -1,7 +1,8 @@
1 1
 
2 2
 # More on type sets
3
-Qinghe
4
-21 Jan 2022
3
+
4
+清和
5
+2022-03-31
5 6
 
6 7
 
7 8
 ## Both elements and methods in constraints

+ 83
- 0
codes/constraint_inference.go Bestand weergeven

@@ -0,0 +1,83 @@
1
+package main 
2
+
3
+
4
+// DOUBLE DEFINATION OMIT
5
+// Double returns a new slice that contains all the elements of s, doubled.
6
+func Double[E constraints.Integer](s []E) []E {
7
+	r := make([]E, len(s))
8
+	for i, v := range s {
9
+		r[i] = v + v
10
+	}
11
+	return r
12
+}
13
+
14
+var s1 = Double[int]([]int{1,2,3}) // s1 is an int slice with value {2,4,6}
15
+
16
+// DOUBLE DEFINATION OMIT
17
+
18
+
19
+// CALL DOUBLE ERROR OMIT
20
+// MySlice is a slice of ints.
21
+type MySlice []int
22
+
23
+// The type of V1 will be []int, not MySlice.
24
+// Here we are using function argument type inference,
25
+// but not constraint type inference.
26
+var V1 = Double(MySlice{1})
27
+
28
+// CALL DOUBLE ERROR OMIT
29
+
30
+
31
+// DOUBLE DEFINED OMIT
32
+// DoubleDefined returns a new slice that contains the elements of s,
33
+// doubled, and also has the same type as s.
34
+func DoubleDefined[S ~[]E, E constraints.Integer](s S) S {
35
+	// Note that here we pass S to make, where above we passed []E.
36
+	r := make(S, len(s))
37
+	for i, v := range s {
38
+		r[i] = v + v
39
+	}
40
+	return r
41
+}
42
+// DOUBLE DEFINED OMIT
43
+
44
+
45
+// CALL DOUBLE OMIT
46
+// The type of V2 will be MySlice.
47
+var V2 = DoubleDefined[MySlice, int](MySlice{1})
48
+
49
+// CALL DOUBLE OMIT
50
+
51
+
52
+// CALL DOUBLE INFERENCE OMIT
53
+var V3 = DoubleDefined(MySlice{1})
54
+// CALL DOUBLE INFERENCE OMIT
55
+
56
+// CALL DOUBLE INFERENCE WHOLE OMIT
57
+var V3 = DoubleDefined[MySlice,int](MySlice{1})
58
+// CALL DOUBLE INFERENCE WHOLE OMIT
59
+
60
+
61
+// POINTER METHOD OMIT
62
+// Setter is a type constraint that requires that the type
63
+// implement a Set method that sets the value from a string.
64
+type Setter interface {
65
+	Set(string)
66
+}
67
+
68
+// FromStrings takes a slice of strings and returns a slice of T,
69
+// calling the Set method to set each returned value.
70
+//
71
+// Note that because T is only used for a result parameter,
72
+// function argument type inference does not work when calling
73
+// this function.
74
+func FromStrings[T Setter](s []string) []T {
75
+	result := make([]T, len(s))
76
+	for i, v := range s {
77
+		result[i].Set(v)
78
+	}
79
+	return result
80
+}
81
+// POINTER METHOD OMIT
82
+
83
+

+ 44
- 0
codes/constraint_inference2.go Bestand weergeven

@@ -0,0 +1,44 @@
1
+package main
2
+
3
+import (
4
+	"strconv"
5
+)
6
+
7
+// Setter is a type constraint that requires that the type
8
+// implement a Set method that sets the value from a string.
9
+type Setter interface {
10
+    Set(string)
11
+}
12
+
13
+// FromStrings takes a slice of strings and returns a slice of T,
14
+// calling the Set method to set each returned value.
15
+//
16
+// Note that because T is only used for a result parameter,
17
+// function argument type inference does not work when calling
18
+// this function.
19
+func FromStrings[T Setter](s []string) []T {
20
+    result := make([]T, len(s))
21
+    for i, v := range s {
22
+        result[i].Set(v)
23
+    }
24
+    return result
25
+}
26
+
27
+// POINTER METHOD CALL OMIT
28
+// Settable is an integer type that can be set from a string.
29
+type Settable int
30
+
31
+// Set sets the value of *p from a string.
32
+func (p *Settable) Set(s string) {
33
+	i, _ := strconv.Atoi(s) // real code should not ignore the error
34
+	*p = Settable(i)
35
+}
36
+
37
+func main() {
38
+	// INVALID
39
+	nums := FromStrings[Settable]([]string{"1", "2"})
40
+	_ = nums
41
+	// Here we want nums to be []Settable{1, 2}.
42
+	// ...
43
+}
44
+// POINTER METHOD CALL OMIT

+ 52
- 0
codes/constraint_inference3.go Bestand weergeven

@@ -0,0 +1,52 @@
1
+package main 
2
+
3
+import (
4
+	"fmt"
5
+	"strconv"
6
+)
7
+
8
+
9
+type Settable int
10
+
11
+func (p *Settable) Set(s string) {
12
+	i, _ := strconv.Atoi(s) // real code should not ignore the error
13
+	*p = Settable(i)
14
+}
15
+
16
+// SETTER2 OMIT
17
+type Setter2[B any] interface {
18
+	Set(string)
19
+	*B // non-interface type constraint element
20
+}
21
+
22
+func FromStrings2[T any, PT Setter2[T]](s []string) []T {
23
+	result := make([]T, len(s))
24
+	for i, v := range s {
25
+		// The type of &result[i] is *T which is in the type set
26
+		// of Setter2, so we can convert it to PT.
27
+		p := PT(&result[i]) 
28
+		p.Set(v) // PT has a Set method.
29
+	}
30
+	return result
31
+}
32
+// SETTER2 OMIT
33
+
34
+
35
+func main(){
36
+// FromStrings2 takes two type parameters.
37
+	// The second parameter must be a pointer to the first.
38
+	// Settable is as above.
39
+// SETTER2 CALL OMIT
40
+	nums := FromStrings2[Settable, *Settable]([]string{"1", "2"})
41
+	// Now nums is []Settable{1, 2}.
42
+	fmt.Println(nums)
43
+// SETTER2 CALL OMIT
44
+
45
+
46
+/**
47
+// SETTER3 CALL OMIT
48
+nums := FromStrings2[Settable]([]string{"1", "2"})
49
+// SETTER3 CALL OMIT
50
+*/
51
+
52
+}

+ 12
- 0
codes/interface.go Bestand weergeven

@@ -0,0 +1,12 @@
1
+package main
2
+
3
+
4
+
5
+// INTERFACE OMIT
6
+
7
+type ReadWriteCloser interface{
8
+	ReadWriter 
9
+	Close() error
10
+}
11
+
12
+// INTERFACE OMIT

+ 15
- 1
codes/operator.go Bestand weergeven

@@ -39,4 +39,18 @@ func Min[T Ordered](s []T) T {
39 39
 	return r
40 40
 }
41 41
 
42
-// OPERATOR MIN OMIT
42
+// OPERATOR MIN OMIT
43
+
44
+
45
+// OPERATOR MIN VALID OMIT
46
+func Min[T Ordered](s []T) T {
47
+	r := s[0] // assume that s is not empty
48
+	for _, v := range s {
49
+		if v < r { // VALID!
50
+			r = v
51
+		}
52
+	}
53
+	return r
54
+}
55
+
56
+// OPERATOR MIN VALID OMIT

BIN
type_set.png Bestand weergeven