Go语言中的闭包详解
作者:奋斗的大橙子 发布时间:2023-06-30 05:27:14
一、函数的变量作用域和可见性
1.全局变量在main函数执行之前初始化,全局可见
2.局部变量在函数内部或者if、for等语句块有效,使用之后外部不可见
3.全局变量和局部变量同名的情况下,局部变量生效。
4.可见性:
包内任何变量或函数都是能访问的。
包外的话,首字母大写是可以访问的,首字母小写的表示私有的不能被外部调用。
二、匿名函数
1.Go语言中函数也是一种类型,所以可以用一个函数类型的变量进行接收。
func anonyTest1(){
fmt.Println("anonyTest1")
}
//将改函数赋值给一个变量f,执行f
func AnonyTest(){
f:= anonyTest1
f()
}
2.匿名函数就是不指定名称的函数,如下就是匿名函数的使用
func AnonyTest2(){
f:= func() {
fmt.Println("AnonyTest2")
}
f()
//或者
func() {
fmt.Println("AnonyTest2...")
}()
}
3.下面一个例子结合defer来看一下,这三个输出都是什么
func AnonyTest3(){
var i=0
defer func() {
fmt.Printf("defer func i=%v \n",i)
}()
defer fmt.Printf("defer i=%v \n",i)
for;i<10; i++{
}
fmt.Printf("i=%v \n",i)
}
从defer那篇文章我们知道 defer fmt.Printf("defer i=%v \n",i) 打印的就是i初始化后的值,最后一个也一定是for循环之后的值10,
主要就是匿名函数执行之后的值,有意思是10,说明访问了匿名函数外部的i,这就涉及到了闭包
运行结果如下:
i=10
defer i=0
defer func i=10
4.既然函数也是一种类型,那么就可以把函数当做参数进行输入、输出了。(感觉有点类似C#里面的委托)
func Calc(a,b int, op func(int,int)int) int {
return op(a,b)
}
func add(a,b int) int{
return a+b
}
func sub(a,b int)int{
return a-b
}
func AnonyTest4(){
var a = 2
var b = 1
var x = Calc(a,b,add)
var y = Calc(a,b,sub)
fmt.Printf("x=%v, y=%v \n",x,y)
}
结果:
x=3, y=1
三、闭包
闭包是由函数和与其相关的引用环境组合而成的实体(好抽象,难理解啊)
func Adder() func(int) int{
var x int
return func(d int) int{
x+=d
return x
}
}
像上面这段代码,我们可以看到定义了一个变量x,以及return中的匿名函数。我们可以看到匿名函数引用了外部的变量x,我们可以把这个x叫做自由变量。
换句话说,这个匿名函数和这个自由变量x组成了一个整体,只要是在这个整体的生命周期内这个x都是有效的。
下面使用一下这个Adder函数:
func ClosureDemo5(){
var f = Adder()
fmt.Printf("结果=%d\n",f(1))
fmt.Printf("结果=%d\n",f(20))
fmt.Printf("结果=%d\n",f(300))
}
执行结果
结果=1
结果=21
结果=321
正如上面所提到的,这个只要Addr() 也就是f这个对象没有消亡,那么f中的这个x就始终存在,也就是为什么第二次是21,第三次是321的原因了。
其他例子:
例子1:
func Adder2(base int) func(int)int{
return func(i int) int{
base += i
return base
}
}
func main(){
tmp1 := Adder2(10)
fmt.Println(tmp1(1),tmp1(2))
tmp2 := Adder2(100)
fmt.Println(tmp2(10),tmp2(20))
}
这里Adder2接收一个int类型参数base,然后返回一个func,这里这个匿名函数里面引用了这个参数base,那么这个参数base和匿名函数就形成了一个整体。
后面我们 tmp1被赋值为 Adder2(10) ,那么在tmp1这个对象的生命周期内,base是被初始化为10且一直存在,所以结果是 11 和 13,同理后面是 110 和 130
例子2:
func calc(base int) (func(int)int,func(int)int){
add:= func(i int)int{
base +=i
return base
}
sub:= func(i int)int{
base -= i
return base
}
return add,sub
}
func main(){
f1,f2 := calc(10)
fmt.Println(f1(1),f2(2))
fmt.Println(f1(3),f2(4))
fmt.Println(f1(5),f2(6))
fmt.Println(f1(7),f2(8))
}
分析一下:
这里base和 add以及sub的匿名函数也组成了一个实体也就是calc,所以在f1和f2的生命周期内,base一直存在,并被初始化成了10.
所以结果就是 f1(1) 就是10+1 =11 而 f2(2)就是 11-2 = 9,其他同理。
所以结果如下:
11 9
12 8
13 7
14 6
闭包的副作用!
func main(){
for i:=0;i<5;i++{
go func(x int){
fmt.Println(x)
}(i)
}
time.Sleep(time.Second)
}
上述代码应该结果是多少?我的猜想应该是0、1、2、3、4
但是实际结果是:
5
5
5
5
5
为什么会出现这样的情况?实际上面这里每一个go协程中的匿名函数和外部for循环的i也形成了闭包,因为for循环执行比较快,所以go还没来得及执行就变成5了。
我在每一个go协程之后加一个延时,结果就是0,1,2,3,4了。
func main(){
for i:=0;i<5;i++{
go func(){
fmt.Println(i)
}()
time.Sleep(time.Second)
}
time.Sleep(time.Second)
}
结果如下
0
1
2
3
4
问题就在于不可能每次执行都进行延迟吧,所以需要做一件事情打破这个闭包。
func main(){
for i:=0;i<5;i++{
go func(x int){
fmt.Println(x)
}(i)
}
time.Sleep(time.Second)
}
这里把i当做参数传入到匿名函数中,保证了每次循环传的值都不一样。
来源:https://www.cnblogs.com/dcz2015/p/10498343.html


猜你喜欢
- 我们前期开发了一个只有公司客服人员才能使用的系统——有限的几个客服人员。就是这有限的几个客服人员前几天突然就提出这样的问题:我们每隔很短一段
- HTML 的空白符处理规则HTML 中的“空白符”包括空格 (space)、制表符 (tab)、换行符 (CR/LF) 三种。我们知道,在默
- win7以上需要使用管理员权限操作。# -*- coding: utf-8 -*-import osimport globimport sh
- 前言我们经常会与文件和目录打交道,对于这些操作python提供了一个os模块,里面包含了很多操作文件和目录的函数。在写一些系统脚本或者自动化
- 本文实例为大家分享了python3实现点餐系统的具体代码,供大家参考,具体内容如下题目: 某餐厅外卖每天更新菜品,
- 一、时间对象timetime模块使用的是C语言函数库中的函数。只能处理1970/1/1到2038/12/31之间的数据。1.测量运行时间方法
- vue切换“页面”(路由)时保持滚动条回到顶部vue项目做pc端的时候,发现在两个页面切换时 滚动条
- 一、前言 本文翻译自微软的牛人Scott Allen Prototypes and Inheritance in JavaScript ,本
- 无论你在linux上娱乐还是工作,这对你而言都是一个使用python来编程的很好的机会。回到大学我希望他们教我的是Python而不是Java
- 代码如下: Function NumberSplit(num) Dim i,length length=Len(num) For i=1 T
- 解决2个问题:1.身份证之类的文本数据自动转为科学计数法的问题。2.中文乱码的问题excel从web页面上导出的原理。当我们把这些数据发送到
- php统计数组元素个数count():对数组中的元素个数进行统计;sizeof():和count()具有同样的用途,这两个函数都可以返回数组
- -- 任意的测试表 CREATE TABLE test_delete( name varchar(10), value INT ); go
- 整本书围绕着一个叫做“CSS禅意花园”的网站展开,其实N久之前我在一份外国的关于CSS的在线教程上看到了这个网站的链接,可惜进去之后发现内容
- 本文实例讲述了Sanic框架Cookies操作。分享给大家供大家参考,具体如下:简介Sanic是一个类似Flask的Python 3.5+
- 一、检测网络信息和系统信息 在Frontpage 2000 的Explorer管理器中选择帮助(Help)|关于Frontpage管理器(A
- 1.前言(闲话)最近在做电磁炮,发现题目需要用到颜色跟踪,于是花了一点时间学了一下OpenMV,只学习OpenMV是远远不够的,还需要实现与
- YUI3.2.0 的 transition 模块,通过使用 transition:end 事件实现在 transition 完成后执行其他操
- 在ACCESS数据库中可以用MSSQL的形式定义操作字符串,也可以采用OLEDB的形式。MSSQL 形式string sqlText = @
- 第一步把tensorflow保存的.ckpt模型转为pb模型, 并记下模型的输入输出名字.第二步去ncnn的github上把仓库clone下