文章目录


1、Go语言支持闭包


2、Go语言能通过escape analyze识别出变量的作用域,自动

将变量在堆上分配。将闭包环境变量在堆上分配是Go实现闭包的基础。


3、返回闭包时并

不是单纯返回一个函数,而是返回了一个结构体,记录下函数返回地址和引用的环境中的变量地址。

用途

函数式编程,匿名函数。

示例一

package main

import "fmt"

func adder() func(int) int {
sum := 0
return func(x int) int {
fmt.Printf("sum addr=%p, x addr=%p\n", &sum, &x)
sum += x
return sum
}
}

func main() {
pos, neg := adder(), adder()
fmt.Printf("pos addr=%p, neg addr=%p\n", &pos, &neg)
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}

输出如下:

pos addr=0xc000006028, neg addr=0xc000006030
sum addr=0xc0000180d8, x addr=0xc0000180f8
sum addr=0xc0000180f0, x addr=0xc000018120
0 0
sum addr=0xc0000180d8, x addr=0xc000018130
sum addr=0xc0000180f0, x addr=0xc000018138
1 -2
sum addr=0xc0000180d8, x addr=0xc000018160
sum addr=0xc0000180f0, x addr=0xc000018168
3 -6
sum addr=0xc0000180d8, x addr=0xc000018190
sum addr=0xc0000180f0, x addr=0xc000018198
6 -12
sum addr=0xc0000180d8, x addr=0xc0000181c0
sum addr=0xc0000180f0, x addr=0xc0000181c8
10 -20
sum addr=0xc0000180d8, x addr=0xc0000181f0
sum addr=0xc0000180f0, x addr=0xc0000181f8
15 -30
sum addr=0xc0000180d8, x addr=0xc000018220
sum addr=0xc0000180f0, x addr=0xc000018228
21 -42
sum addr=0xc0000180d8, x addr=0xc000018250
sum addr=0xc0000180f0, x addr=0xc000018258
28 -56
sum addr=0xc0000180d8, x addr=0xc000018280
sum addr=0xc0000180f0, x addr=0xc000018288
36 -72
sum addr=0xc0000180d8, x addr=0xc0000182b0
sum addr=0xc0000180f0, x addr=0xc0000182b8
45 -90

因为pos和neg都调用了adder()参数,返回了不同的闭包,所以sum在堆上分配的地址空间也不同;

所以针对pos(i)是求sum=sum+i,即0+0=0,0+1=1, 1+2=3,3+3=6…

neg是求sum=sum+(-2i),即0+(-20)=0,0+(-21)=-2,-2+(-22)=-6…

示例二

package main

import "fmt"

func main() {
var flist []func()
for i := 0; i < 3; i++ {
fmt.Printf("A: addr=%p, value=%d\n", &i, i)
flist = append(flist, func() {
fmt.Printf("B: addr=%p, value=%d\n", &i, i)
})
}

for _, f := range flist {
f()
}
}

输出如下:

A: addr=0xc0000180d8, value=0
A: addr=0xc0000180d8, value=1
A: addr=0xc0000180d8, value=2
B: addr=0xc0000180d8, value=3
B: addr=0xc0000180d8, value=3
B: addr=0xc0000180d8, value=3

这个比较好理解,三个匿名函数闭包中的i是同一个变量i,在堆上是同一个,最后i的值为3,所以三次都输出3。

示例三

package main

import "fmt"

func main() {
var flist []func()
for i := 0; i < 3; i++ {
//给i变量重新赋值
i := i
fmt.Printf("A: addr=%p, value=%d\n", &i, i)
flist = append(flist, func() {
fmt.Printf("B: addr=%p, value=%d\n", &i, i)
})
}
for _, f := range flist {
f()
}
}

输出结果如下:

A: addr=0xc0000180d8, value=0
A: addr=0xc0000180f8, value=1
A: addr=0xc000018118, value=2
B: addr=0xc0000180d8, value=0
B: addr=0xc0000180f8, value=1
B: addr=0xc000018118, value=2

i被重新赋值,传入闭包函数的i为新的地址,所以三个闭包函数分别为1,2,3

参考

​Go语言中的闭包​


​Golang中闭包的实现原理​