1. 闭包定义

Go 语言中闭包是引用了自由变量的函数,被引用的自由变量和函数一同存在,即使已经离开了自由变量的环境也不会被释放或者删除,在闭包中可以继续使用这个自由变量,因此,简单的说:

函数 + 引用环境 = 闭包

一个函数类型就像结构体一样,可以被实例化,函数本身不存储任何信息,只有与引用环境结合后形成的闭包才具有“记忆性”,函数是编译期静态的概念,而闭包是运行期动态的概念

Go 语言闭包函数的定义:当匿名函数引用了外部作用域中的变量时就成了闭包函数,闭包函数是函数式编程语言的核心。

也就是匿名函数可以会访问其所在的外层函数内的局部变量。当外层函数运行结束后,匿名函数会与其使用的外部函数的局部变量形成闭包。

2. 闭包示例

2.1 闭包修改变量

闭包对它作用域上部的变量可以进行修改,修改引用的变量会对变量进行实际修改,通过下面的例子来理解:

package mainimport "fmt"func main() {// 准备一个字符串str := "hello world"fmt.Println("str is ", str)// 创建一个匿名函数foo := func() {// 匿名函数中访问str// 在匿名函数中并没有定义 str,str 的定义在匿名函数之前,此时,str 就被引用到了匿名函数中形成了闭包。str = "hello dude"}// 调用匿名函数, 执行闭包,此时 str 发生修改,变为 hello dude。foo()fmt.Println("str is ", str)
}

代码输出:

str is  hello world
str is  hello dude

2.2 闭包记忆效应

被捕获到闭包中的变量让闭包本身拥有了记忆效应,闭包中的逻辑可以修改闭包捕获的变量,变量会跟随闭包生命期一直存在,闭包本身就如同变量一样拥有了记忆效应。

package mainimport ("fmt"
)/*
累加器生成函数,这个函数输出一个初始值,
调用时返回一个为初始值创建的闭包函数
*/
func Accumulate(value int) func() int {// 返回一个闭包,每次返回会创建一个新的函数实例return func() int {// 累加,对引用的 Accumulate 参数变量进行累加,注意 value 不是上一行匿名函数定义的,但是被这个匿名函数引用,所以形成闭包。value++// 返回一个累加值return value}
}func main() {// 创建一个累加器,初始值为 1,返回的 accumulator 是类型为 func()int 的函数变量。accumulator := Accumulate(1)// 累加1并打印fmt.Println(accumulator())fmt.Println(accumulator())// 打印累加器的函数地址fmt.Printf("%p\n", &accumulator)// 创建一个累加器, 初始值为10accumulator2 := Accumulate(10)// 累加1并打印fmt.Println(accumulator2())// 打印累加器的函数地址fmt.Printf("%p\n", &accumulator2)
}

输出结果:

2
3
0xc00000e030
11
0xc00000e040

可以看出 accumulator 与 accumulator2 输出的函数地址不同,因此它们是两个不同的闭包实例。

package mainimport "fmt"func add() func(int) int {var x intreturn func(y int) int {x += yreturn x}
}
func main() {var f = add()fmt.Println(f(1)) //1fmt.Println(f(2)) //3fmt.Println(f(3)) //6f1 := add()fmt.Println(f1(4)) //4fmt.Println(f1(5)) //9
}

add() 函数赋值给变量 f,那么 f(1) 就相当于执行其内部的匿名函数 func(y int)。所以 f(1) 其实执行的是 x = x+y,此时 x 没有给值默认为 0,y 为 1,最终返回 x 的值是 1。

f(2) 同样执行其内部的匿名函数 func(y int),所以 f(2) 其实执行的是 x = x+y。此时 x 已经是 1,y 为 2,最终返回 x 的值是 3,依次类推执行。

2.3 闭包实现生成器

玩家生成器的实现:

package mainimport ("fmt"
)// 创建一个玩家生成器, 输入名称, 输出生成器
func playerGen(name string) func() (string, int) {// 血量一直为150hp := 150// 返回创建的闭包return func() (string, int) {// 将 hp 和 name 变量引用到匿名函数中形成闭包。return name, hp}
}func main() {// 创建一个玩家生成器generator := playerGen("wohu")// 返回玩家的名字和血量name, hp := generator()// 打印值fmt.Println(name, hp)   // wohu 150
}

2.4 闭包复制原对象指针

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

package mainimport "fmt"func test() func() {x := 100fmt.Printf("x (%p) = %d\n", &x, x)return func() {fmt.Printf("x (%p) = %d\n", &x, x)}
}func main() {f := test()f()
}

输出:

x (0xc000018068) = 100
x (0xc000018068) = 100

3. 对比 Python 闭包

def fun1():sum = 0def fun2(v):nonlocal sumsum += vreturn sumreturn fun2a = fun1()
for i in range(10):print(a(i))0
1
3
6
10
15
21
28
36
45

代码解析:
fun1 返回的不是一个值,而是一个函数 fun2,a = fun2,所以 a(i) 会打印 sum 的值。

为什么 sum 一直在加呢,函数里的值为什么可以带到函数体外呢?
其实可以把闭包看做一个类, sum 就是类里的属性,fun2 就是类的方法,所以 fun2 可以使用 sum(自由变量)。

nonlocal 声明的变量不是局部变量,也不是全局变量,而是外部嵌套函数内的变量。嵌套函数 fun2 中的 sum 受到了影响,显示 fun2 中的 sum 是 fun1 函数中的局部变量。

Go 学习笔记(18)— 函数(04)[闭包定义、闭包修改变量、闭包记忆效应、闭包实现生成器、闭包复制原对象指针]相关推荐

  1. MATLAB学习笔记 :函数文件的定义和使用

    数学建模比赛MATLAB从入门到精通教程_哔哩哔哩_bilibili function语法 1.编写函数文件,求半径为r的圆的面积和周长 (1)新建->函数 (2)编辑代码,保存 (3)回命令行 ...

  2. 《JavaScript语言精粹》学习笔记(函数(2))

    <JavaScript语言精粹>学习笔记(函数(2)) 函数(Functions) 参数(Arguments) 当参数被调用时,会得到一个"免费"的参数数组argume ...

  3. Python学习笔记12_函数

    Python学习笔记12_函数 文章目录 Python学习笔记12_函数 1.函数定义 2.函数调用 3.函数的参数 3.1.可更改对象和不可更改对象参数 3.2.必需参数(位置参数) 3.3.关键字 ...

  4. python函数是一段具有特定功能的语句组_Python学习笔记(五)函数和代码复用

    本文将为您描述Python学习笔记(五)函数和代码复用,具体完成步骤: 函数能提高应用的模块性,和代码的重复利用率.在很多高级语言中,都可以使用函数实现多种功能.在之前的学习中,相信你已经知道Pyth ...

  5. php中声明一个函数,php学习笔记之 函数声明

    /* 函数定义: * 1.函数是一个被命名的 * 2.独立的代码段 * 3.函数执行特定任务 * 4.并可以给调用它的程序返回一个值 * * 函数的优点: * 1.提高程序的重用性 * 2.提高程序的 ...

  6. JAVA学习笔记五---函数

    JAVA学习笔记五---函数 5.1 方法的学习 编写一个程序,求圆的周长和面积. package practice; /*** 编写一个程序,求圆的周长和面积.* @author iszhangyo ...

  7. MySQL学习笔记—自定义函数

    MySQL学习笔记-自定义函数 注释语法: MySQL服务器支持3种注释风格: 从'#'字符从行尾. 从'– '序列到行尾.请注意'– '(双破折号)注释风格要求第2个破折号后面至少跟一个空格符(例如 ...

  8. 2020-4-12 深度学习笔记18 - 直面配分函数 5 ( 去噪得分匹配,噪声对比估计NCE--绕开配分函数,估计配分函数)

    第十八章 直面配分函数 Confronting the Partition Function 中文 英文 2020-4-8 深度学习笔记18 - 直面配分函数 1 ( 配分函数概念,对数似然梯度) 2 ...

  9. Python学习笔记18:实操案例十五(记录用户登录日志,模拟淘宝客服自动回复)

    Python学习笔记18:实操案例十五(记录用户登录日志,模拟淘宝客服自动回复) 网课传送门:https://www.bilibili.com/video/BV1Sw411Z779?p=168& ...

最新文章

  1. Hdu 6534 Chika and Friendly Pairs 莫队算法+树状数组
  2. 一套使用注入和Hook技术托管入口函数的方案
  3. linux系统不关机添加硬盘吗,CentOS中不重启添加硬盘
  4. 双通道和单通道区别_实测内存通道的区别:单通道比双通道内存更有优势?
  5. 支付宝app支付java后台流程、原理分析(含nei wang chuan tou)
  6. java图片上传被旋转,在其他大牛那看到的java手机图片上传旋转问题的解决方法...
  7. mysql hbase 同步_HBase 简介和使用 Sqoop 同步 Mysql 数据到 HBase
  8. 2021年美食类短视频及直播营销趋势洞察
  9. Android-Socket的最基础实现以及遇见在2.3可用4.3不可用的解决方法
  10. 理清逻辑,确保云原生时代应用开发的全生命周期安全
  11. 【和谐 OS】来了!鸿蒙现场视频解析
  12. 用 CSS 实现元素垂直居中
  13. 学习笔记之软考数据库系统工程师教程(第一版)
  14. C++ IO库:cmd读写,字符串读写,文件读写,<<重载,标准输出
  15. springboot整合websocket实现微信小程序聊天
  16. Lodop打印控件的学习
  17. [Nowcoder] 2021年度训练联盟热身训练赛第六场 Mini Battleship | 深搜 回溯 乱搞
  18. 《从零开始的 RPG 游戏制作教程》第十五期:地图发布,以及再见
  19. 双赞的一体机主板能应用到哪些行业?
  20. 求1至10乘阶的总和及求任意数乘阶

热门文章

  1. 伍六七带你学算法 进阶篇-三数之和
  2. 2022-2028年中国乙丙橡胶行业市场全景调查及投资潜力研究报告
  3. 2022-2028年中国能源期货市场深度调研及投资前景预测报告
  4. 2022-2028年中国智慧物流行业深度调研及投资前景预测报告(全卷)
  5. RSA、MD5等加密算法的区别和应用
  6. MySQL中对varchar类型排序问题的解决
  7. mysql engine innodb myisam 区别
  8. torch.nn.functional.cross_entropy.ignore_index
  9. 文件读取输出-python
  10. 合肥工业大学—SQL Server数据库实验三:SQL语句创建和删除基本表