定义
First Class Function
是一种编程语言的术语, 指当一个函数可以赋值给变量, 可以当作参数传参, 可以作为返回值返回时, 称为First Class Function
. 而Go
语言拥有First Class Function
特性;
解释
这一部分将结合Go
语言语法与各种使用场景阐述First Class Function
;
Scenario 1: Anonymous function
Anonymous function
(匿名函数)指的是没有函数名称的函数, 其在项目中往往充当简单的功能函数, 其使用方式很灵活, 例如:
赋值
Anonymous function
给某变量;1
2
3
4
5
6
7
8
9
10
11
12
13package main
import (
"fmt"
)
func main() {
a := func() {
fmt.Println("Hello First Class Function")
}
a()
fmt.Printf("Type of variable a is %T", a)
}输出为:
1
2Hello First Class Function
Type of variable a is func()上面定义了一个
Anonymous function
,其会输出Hello First Class Function
,并赋值给a
;
随后调用a
, 并打印出了a
的类型, 发现其为func()
;Anonymous function
创建的同时运行;1
2
3
4
5
6
7
8
9
10
11
12package main
import (
"fmt"
)
func main() {
func() {
fmt.Println("Hello First Class Function")
}()
}输出:
1
Hello First Class Function
对于
Anonymous function
也可以不赋值, 例如上面定义了Anonymous function
的同时立马运行, 输出了Hello First Class Function
;Anonymous function
也支持传参;1
2
3
4
5
6
7
8
9
10
11package main
import (
"fmt"
)
func main() {
func(n string) {
fmt.Println("Hello", n)
}("Gophers")
}对于
Anonymous function
, 自然也可以像普通函数一样通过传参的方式使用; 例如上面定义了一个接受string
入参的Anonymous function
.
Scenario 2: User defined function types
Go
语言支持用户自定义函数类型, 例如:
1 | package main |
输出:
1 | Min 5 |
上面定义了一个自定义函数类型Min
, 随后通过Anonymous function
的方式赋值给Min
类型的变量m
; 随后调用函数m
获得两数中的小数;
Scenario 3: Higher-order functions
Higher-order functions
高维函数指的是符合以下两者之一的函数:
- 函数传参: 入参中至少包含一个函数入参
- 返回函数: 返回值中包含有函数
函数传参:
1 | package main |
输出:
1 | Func result: 5 |
上面的例子中, 我定义了一个higher
高维函数, 其接受一个函数入参f
, 并在内部逻辑中输出f(5, 6)
的结果; 在main
函数中, 定义了一个Anonymous function
并赋值给变量min
; 最后将min
传递给higher
函数执行.
返回函数:
1 | package main |
输出为:
1 | Result is: 5 |
在上面的例子中, 我定义了一个higher
高维函数, 其会返回一个实现了Min
逻辑的函数; 在main
函数中, 我通过变量f
接收了higher
的返回值, 并在最后输出f
的执行结果;
Scenario 4: Closures
Closures
闭包是anonymous functions
的特殊用例, 指的是在Function Body
内使用了外部的变量, 导致外部变量在函数使用期间无法被GC
回收;例如:
1 | package main |
输出为:
1 | Hello World |
在上面的例子中appendStr
函数返回了一个closure
, 其绑定了变量t
; 在main
函数中将closure
赋值给变量a
, 并在随后调用了两次; 可以发现, 第二次调用时的输出为Hello World Gopher
而不是Hello Gopher
. 这说明变量t
在closure
使用过程中一直被引用, 并且其值会随着多次调用,产生无法预期的变化. 在项目中, 一般避免使用Closure
, 以免产生不必要的误解;
用例
这一部分将通过一个简单的用例, 说明First Class Function
的用于Dependency Injection
的用例场景;
定义一个
student
结构体:1
2
3
4
5
6type student struct {
firstName string
lastName string
grade string
country string
}student
结构体中包含有学生基础信息,姓, 名, 分数, 国家
;定义一个
filter
函数:1
2
3
4
5
6
7
8
9func filter(s []student, f func(student) bool) []student {
var r []student
for _, v := range s {
if f(v) == true {
r = append(r, v)
}
}
return r
}定义了一个过滤函数
filter
, 其接受一个student
列表与filter logic: f
, 函数中会遍历student
列表并返回符合f
的学生列表;
定义了一个过滤函数filter
, 其接受一个student
列表与filter logic: f
, 函数中会遍历student
列表并返回符合f
的学生列表;
函数主逻辑如下:
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
54package main
import "fmt"
type student struct {
firstName string
lastName string
grade string
country string
}
func filter(s []student, f func(student) bool) []student {
var r []student
for _, v := range s {
if f(v) == true {
r = append(r, v)
}
}
return r
}
func studentList() []student {
s1 := student{
firstName: "Naveen",
lastName: "Ramanathan",
grade: "A",
country: "India",
}
s2 := student{
firstName: "Samuel",
lastName: "Johnson",
grade: "B",
country: "USA",
}
s3 := student{
firstName: "Sun",
lastName: "Qingzhi",
grade: "C",
country: "China",
}
return []student{s1, s2, s3}
}
func main() {
s := studentList()
f := filter(s, func(s student) bool {
if s.grade == "C" {
return true
}
return false
})
fmt.Println(f)
}在
main
函数逻辑中, 首先通过studentList
函数获得了初始化学生列表, 随后通过调用filter
函数, 并注入了一个匿名函数1
2
3
4
5
6
7// 找到成绩为C的学生
func(s student) bool {
if s.grade == "C" {
return true
}
return false
}随后获得了符合匿名函数的学生列表;
输出为:
1
[{Sun Qingzhi C China}]
这么做的好处在于将
filter logic
与Caller
拆分开来, 是一种松耦合的编码方式;如果发现目前有业务调整, 需要得到来自于
India
的学生列表, 那么只需要将filter logic
逻辑更换为:1
2
3
4
5
6
7// 找到来自于印度的学生
func(s student) bool {
if s.country == "India" {
return true
}
return false
}输出为:
1
[{Naveen Ramanathan A India}]
总结
本文介绍的核心为First Class Function
, 分别介绍了定义, Go
中的使用场景与用例实战. 相信认真读到这里, 可以发现First Class Function
是一个简单的术语, 在项目中或多或少的都有涉及. 期望这一篇文章可以对你有所帮助;