D 的个人博客

但行好事莫问前程

  menu
417 文章
3446695 浏览
8 当前访客
ღゝ◡╹)ノ❤️

Go 边看边练 -《Go 学习笔记》系列(六)

上一篇: [1438070631857]


ToC


3.1 函数定义

不支持 嵌套 (nested)、重载 (overload) 和 默认参数 (default parameter)。

  • 无需声明原型。
  • 支持不定⻓长变参。
  • 支持多返回值。
  • 支持命名返回参数。
  • 支持匿名函数和闭包。

使用关键字 func 定义函数,左大括号依旧不能另起一行。

1func test(x, y int, s string) (int, string) { // 类型相同的相邻参数可合并。
2	n := x + y // 多返回值必须用括号。
3	return n, fmt.Sprintf(s, n)
4}

函数是第一类对象,可作为参数传递。建议将复杂签名定义为函数类型,以便于阅读。

有返回值的函数,必须有明确的终止语句,否则会引发编译错误。

3.2 变参

变参本质上就是 slice。只能有一个,且必须是最后一个。

使用 slice 对象做变参时,必须展开。

1func main() {
2	s := []int{1, 2, 3}
3	println(test("sum: %d", s...))
4}

3.3 返回值

不能用容器对象接收多返回值。只能用多个变量,或 "_" 忽略。

 1func test() (int, int) {
 2	return 1, 2
 3}
 4
 5func main() {
 6	// s := make([]int, 2)
 7  	// s = test() // Error: multiple-value test() in single-value context
 8  
 9  	x, _ := test()
10    println(x)
11}

多返回值可直接作为其他函数调用实参。

 1func test() (int, int) {
 2	return 1, 2
 3}
 4
 5func add(x, y int) int {
 6	return x + y
 7}
 8
 9func sum(n ...int) int {
10	var x int
11	for _, i := range n {
12		x += i
13	}
14    
15	return x
16}
17
18func main() {
19	println(add(test()))
20	println(sum(test()))
21}

命名返回参数可看做与形参类似的局部变量,最后由 return 隐式返回。

1func add(x, y int) (z int) {
2	z = x + y
3
4	return
5}
6
7func main() {
8	println(add(1, 2))
9}

命名返回参数可被同名局部变量遮蔽,此时需要显式返回。

1func add(x, y int) (z int) {
2    { // 不能在一个级别,引发 "z redeclared in this block" 错误。
3    	var z = x + y
4    	// return // Error: z is shadowed during return
5    	return z // 必须显式返回。
6    }
7}

命名返回参数允许 defer 延迟调用通过闭包读取和修改。

 1func add(x, y int) (z int) {
 2	defer func() {
 3		z += 100
 4	}()
 5
 6	z = x + y
 7	return
 8}
 9
10func main() {
11	println(add(1, 2)) // 输出: 103
12}

显式 return 返回前,会先修改命名返回参数。

 1func add(x, y int) (z int) {
 2	defer func() {
 3		println(z) // 输出: 203
 4	}()
 5
 6	z = x + y
 7	return z + 200 // 执⾏行顺序: (z = z + 200) -> (call defer) -> (ret)
 8}
 9
10func main() {
11	println(add(1, 2)) // 输出: 203
12}

3.4 匿名函数

匿名函数可赋值给变量,做为结构字段,或者在 channel 里传送。

 1// --- function variable ---
 2
 3fn := func() { println("Hello, World!") }
 4fn()
 5
 6// --- function collection ---
 7
 8fns := [](func(x int) int){
 9	func(x int) int { return x + 1 },
10	func(x int) int { return x + 2 },
11}
12
13println(fns[0](100))
14
15// --- function as field ---
16
17d := struct {
18	fn func() string
19}{
20	fn: func() string { return "Hello, World!" },
21}
22
23println(d.fn())
24
25// --- channel of function ---
26
27fc := make(chan func() string, 2)
28fc <- func() string { return "Hello, World!" }
29println((<-fc)())

闭包复制的是原对象指针,这就很容易解释延迟引用现象。

在汇编层面,test 实际返回的是 FuncVal 对象,其中包含了匿名函数地址、闭包对象指针。当调用匿名函数时,只需以某个寄存器传递该对象即可。

1FuncVal { func_address, closure_var_pointer ... }

下一篇: [1438260619759]



社区小贴士

  • 关注标签 [golang] 可以方便查看 Go 相关帖子
  • 关注标签 [Go 学习笔记] 可以方便查看本系列
  • 关注作者后如有新帖将会收到通知

该文章同步自 黑客派