Go 学习笔记(18)— 函数(04)[闭包定义、闭包修改变量、闭包记忆效应、闭包实现生成器、闭包复制原对象指针]
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)[闭包定义、闭包修改变量、闭包记忆效应、闭包实现生成器、闭包复制原对象指针]相关推荐
- MATLAB学习笔记 :函数文件的定义和使用
数学建模比赛MATLAB从入门到精通教程_哔哩哔哩_bilibili function语法 1.编写函数文件,求半径为r的圆的面积和周长 (1)新建->函数 (2)编辑代码,保存 (3)回命令行 ...
- 《JavaScript语言精粹》学习笔记(函数(2))
<JavaScript语言精粹>学习笔记(函数(2)) 函数(Functions) 参数(Arguments) 当参数被调用时,会得到一个"免费"的参数数组argume ...
- Python学习笔记12_函数
Python学习笔记12_函数 文章目录 Python学习笔记12_函数 1.函数定义 2.函数调用 3.函数的参数 3.1.可更改对象和不可更改对象参数 3.2.必需参数(位置参数) 3.3.关键字 ...
- python函数是一段具有特定功能的语句组_Python学习笔记(五)函数和代码复用
本文将为您描述Python学习笔记(五)函数和代码复用,具体完成步骤: 函数能提高应用的模块性,和代码的重复利用率.在很多高级语言中,都可以使用函数实现多种功能.在之前的学习中,相信你已经知道Pyth ...
- php中声明一个函数,php学习笔记之 函数声明
/* 函数定义: * 1.函数是一个被命名的 * 2.独立的代码段 * 3.函数执行特定任务 * 4.并可以给调用它的程序返回一个值 * * 函数的优点: * 1.提高程序的重用性 * 2.提高程序的 ...
- JAVA学习笔记五---函数
JAVA学习笔记五---函数 5.1 方法的学习 编写一个程序,求圆的周长和面积. package practice; /*** 编写一个程序,求圆的周长和面积.* @author iszhangyo ...
- MySQL学习笔记—自定义函数
MySQL学习笔记-自定义函数 注释语法: MySQL服务器支持3种注释风格: 从'#'字符从行尾. 从'– '序列到行尾.请注意'– '(双破折号)注释风格要求第2个破折号后面至少跟一个空格符(例如 ...
- 2020-4-12 深度学习笔记18 - 直面配分函数 5 ( 去噪得分匹配,噪声对比估计NCE--绕开配分函数,估计配分函数)
第十八章 直面配分函数 Confronting the Partition Function 中文 英文 2020-4-8 深度学习笔记18 - 直面配分函数 1 ( 配分函数概念,对数似然梯度) 2 ...
- Python学习笔记18:实操案例十五(记录用户登录日志,模拟淘宝客服自动回复)
Python学习笔记18:实操案例十五(记录用户登录日志,模拟淘宝客服自动回复) 网课传送门:https://www.bilibili.com/video/BV1Sw411Z779?p=168& ...
最新文章
- Hdu 6534 Chika and Friendly Pairs 莫队算法+树状数组
- 一套使用注入和Hook技术托管入口函数的方案
- linux系统不关机添加硬盘吗,CentOS中不重启添加硬盘
- 双通道和单通道区别_实测内存通道的区别:单通道比双通道内存更有优势?
- 支付宝app支付java后台流程、原理分析(含nei wang chuan tou)
- java图片上传被旋转,在其他大牛那看到的java手机图片上传旋转问题的解决方法...
- mysql hbase 同步_HBase 简介和使用 Sqoop 同步 Mysql 数据到 HBase
- 2021年美食类短视频及直播营销趋势洞察
- Android-Socket的最基础实现以及遇见在2.3可用4.3不可用的解决方法
- 理清逻辑,确保云原生时代应用开发的全生命周期安全
- 【和谐 OS】来了!鸿蒙现场视频解析
- 用 CSS 实现元素垂直居中
- 学习笔记之软考数据库系统工程师教程(第一版)
- C++ IO库:cmd读写,字符串读写,文件读写,<<重载,标准输出
- springboot整合websocket实现微信小程序聊天
- Lodop打印控件的学习
- [Nowcoder] 2021年度训练联盟热身训练赛第六场 Mini Battleship | 深搜 回溯 乱搞
- 《从零开始的 RPG 游戏制作教程》第十五期:地图发布,以及再见
- 双赞的一体机主板能应用到哪些行业?
- 求1至10乘阶的总和及求任意数乘阶
热门文章
- 伍六七带你学算法 进阶篇-三数之和
- 2022-2028年中国乙丙橡胶行业市场全景调查及投资潜力研究报告
- 2022-2028年中国能源期货市场深度调研及投资前景预测报告
- 2022-2028年中国智慧物流行业深度调研及投资前景预测报告(全卷)
- RSA、MD5等加密算法的区别和应用
- MySQL中对varchar类型排序问题的解决
- mysql engine innodb myisam 区别
- torch.nn.functional.cross_entropy.ignore_index
- 文件读取输出-python
- 合肥工业大学—SQL Server数据库实验三:SQL语句创建和删除基本表