参考
http://c.biancheng.net/view/59.html
https://www.jianshu.com/p/faf7ef7fbcf8
https://www.calhoun.io/5-useful-ways-to-use-closures-in-go/
关键点
希望通过下面的关键词
,实现目的:能够快速
的回忆
,理解
,复习
知识点:
1、闭包 = 匿名函数 + 引用环境
2、函数,是
编译器静态
的概念3、闭包,是
运行期动态
的概念4、闭包的
本质
什么?要解决
什么问题
?
可以从以下几个方面去理解:
将函数增加记忆功能,
从
函数的视角
去看,将函数运行结束后
的状态
能够保存
下来从
引用环境的视角
去看,对同一块内存,用同一个函数操作后的结果
,可以操作一次,也可以操作多次闭包很适合
迭代场景
下的使用,可以迭代一次,也可以迭代多次引用环境,可以认为是
中间态
,或者说,运行态
, 或者说 一个变量
,经过匿名函数
处理后,会产生
不同的值
,就是不同的状态还可以从另外一个角度去分析:一个函数内部修改了某个变量(内存),等这个函数执行完毕后,这个变量还在,下次再次执行这个函数的时候,可以继续获取到当前的变量,即执行这个函数的时候,都是在上次的执行结果之上进行的;那么,这个变量,肯定是在函数外部定义的,然后,对这个变量和函数进行封装成一个函数,那么整体就是闭包了。递归操作,就有这种特性
含有以上特征时,可以考虑
使用闭包
1、什么闭包?
简单的说,就是
闭包 = 匿名函数 + 引用环境
同一个函数与不同引用环境的组合,可以形成不同的实例,如下图所示:
2、函数是否可以存储状态
,或者说,存储信息?
一个函数类型就像结构体一样,可以被实例化,
函数本身不存储任何信息,只有与引用环境结合后的闭包,才具有"记忆性";
调用函数时会创建
内存,
但是,当函数调用结束后,内存
就释放
掉了,
因此, 函数本身不具备
存储任何信息
的能力
3、什么是 引用
环境
由于闭包把函数和运行时的引用环境打包成为一个新的整体, 所以就解决了函数编程中的前嵌套所引发的问题。
当每次调用包含闭包的函数时都将返回一个新的闭包实例,这些实例之间是隔离的, 分别包含调用时不同的引用环境现场。
不同于函数,闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。
4、闭包的本质
是什么呢?
可以从以下几个方面去理解:
1、将函数增加记忆功能,
2、从函数的视角去看,将函数运行结束后的状态能够保存下来
3、从引用环境的视角去看,对同一块内存,用同一个函数 操作后的结果,可以操作一次,也可以操作多次
4、闭包很适合迭代场景下的使用,可以迭代一次,也可以迭代多次
5、引用环境,可以认为是中间态,或者说,运行态
含有以上特征时,可以考虑使用闭包
5、什么场景
下使用闭包
呢?
5.1、例子 1:计算一个切片的和
需求分析:
一个切片是有至少一个元素以上构成的,那么切片的和是一个累加的过程,
既然是累加的过程就会有 中间过程,中间态,
把中间过程(中间态)看做是一个引用环境,
package main
import "fmt"
func sum() func(int) int {
var num int
return func(temp int) int {
num += temp
return num
}
}
func main() {
data := []int{
2,3,4,1,5,1,
}
s := sum()
var result int
for _, value := range data{
result = s(value)
}
fmt.Printf("result = %d\n", result)
}
image
5.2、例子 2:计算一下某个函数
的调用次数
package main
import "fmt"
func createApi(str string) {
fmt.Printf("createApi recevice str =\t%s\n", str)
}
func countFunc(f func(string)) func(string) int {
var count int
return func(str string) int {
f(str)
count++
return count
}
}
func main() {
cf := countFunc(createApi)
cf("hello")
cf("world")
result := cf("Golang")
fmt.Printf("call createApi num %d", result)
}
image
5.3、例子 3: 斐波那契数列
如:
1,2,3,5,8,13,21,34,……
package main
import "fmt"
func makeFibGen() func() int {
f0 := 0
f1 := 1
return func() int {
f1, f0 = (f0 + f1), f1
return f0
}
}
func main() {
gen := makeFibGen()
for i := 0; i < 10; i++ {
fmt.Printf("%d ", gen())
}
}
image
6、闭包中遇到
的坑
请参考下面的博客:
https://www.jianshu.com/p/fa21e6fada70
https://blog.csdn.net/chunyuan314/article/details/81247746
https://www.jianshu.com/p/faf7ef7fbcf8
https://blog.csdn.net/chunyuan314/article/details/81247746
问题核心是:
-
闭包中的匿名函数,在引用环境时,不是
值
拷贝,而是引用 -
for循环会
很快
执行结束
,而协程
还未开始,导致协程在获取for循环给的变量时,拿到的都是最后一个变量
-
解决措施,
-
在创建协程后,需要添加阻塞,如休息一小段时间
-
传递for循环的变量时,先复制给 一个临时变量,而临时变量属于值拷贝,从而协程拿到了属于自己的变量
-
还可以使用通道,如 https://blog.csdn.net/li_101357/article/details/80196650
-
网友评论