Go语言中的闭包
一、函数的变量作用域和可见性
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,其他同理。
所以结果如下:
闭包的副作用!
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:=;i<;i++{
go func(x int){
fmt.Println(x)
}(i)
}
time.Sleep(time.Second)
}
这里把i当做参数传入到匿名函数中,保证了每次循环传的值都不一样。
Go语言中的闭包的更多相关文章
- 001_解析go语言中的闭包
go语言中的闭包,是大家学习go语言的一个大难点,笔者在学习时候也是痛苦不堪,在来回对比了其它语言的用法,并且查阅了很多网上的文章,终于对闭包有了一个较为清晰的认识,以下就是关于闭包的解析 首先看一个 ...
- 015_go语言中的闭包
代码演示 package main import "fmt" func intSeq() func() int { i := 0 return func() int { i++ r ...
- [译] Closures in Lua - Lua中的闭包
原文:(PDF) . 摘要 一等(first-class)函数是一种非常强大的语言结构,并且是函数式语言的基础特性.少数过程式语言由于其基于栈的实现,也支持一等函数.本文讨论了Lua 5.x用于实现一 ...
- [转][译] Closures in Lua - Lua中的闭包
http://www.cnblogs.com/plodsoft/p/5900270.html?utm_source=tuicool&utm_medium=referral 原文:(PDF) . ...
- javaScript中的闭包原理 (译)
这篇文章通过javaScript代码解释了闭包的原理,来让编程人员理解闭包.它不是写给大牛或使用功能性语言进行编程的程序员的.一旦意会了其核心概念,闭包理解起来并不难.然而,你不可能通过阅读任何有关闭 ...
- 彻底弄懂js循环中的闭包问题
来源:http://www.108js.com/article/article1/10177.html?id=899 第一次接触这个问题还是在我刚开始学js的时候,当时就是一头雾水,时隔一年多了,突然 ...
- javascript中的闭包解析
学习javaScript已经有一段时间了,在这段时间里,已经感受到了JavaScript的种种魅力,这是一门神奇的语言,同时也是一门正在逐步完善的语言,相信在大家的逐步修改中,这门语言会逐步的完善下去 ...
- 如何在C语言中调用Swift函数
在Apple官方的<Using Swift with Cocoa and Objectgive-C>一书中详细地介绍了如何在Objective-C中使用Swift的类以及如何在Swift中 ...
- js中的闭包之我理解
闭包是一个比较抽象的概念,尤其是对js新手来说.书上的解释实在是比较晦涩,对我来说也是一样. 但是他也是js能力提升中无法绕过的一环,几乎每次面试必问的问题,因为在回答的时候.你的答案的深度,对术语的 ...
随机推荐
- JDK源码分析(2)之 Array 相关
在深入了解 Array 之前,一直以为 Array 比较简单,但是深入了解后才发现其实挺复杂的.所以我把重要的写在最前面,但凡遇到和语言本身相关的问题,都可以查阅 Java Language and ...
- 【带着canvas去流浪(4)】绘制散点图
目录 一. 任务说明 二. 重点提示 三. 示例代码 四.散点hover交互效果的实现 4.1 基本算法 4.2 参考代码 4.3 Demo中的小问题 示例代码托管在:http://www.githu ...
- Java 8中Stream API学习笔记
1)函数式编程的优势和劣势分别是什么?优势:①不可变性 ②并行操作 ③执行顺序更灵活 ④代码更加简洁纯粹的函数式编程,变量具有不可变性,同一个参数不会在不同场景下得出不同的结果,因此大大增强了系统的稳 ...
- nginx系列10:通过upstream模块选择上游服务器和负载均衡策略round-robin
upstream模块的使用方法 1,使用upstream和server指令来选择上游服务器 这两个指令的语法如下图: 示例: 2,对上游服务使用keepalive长连接 负载均衡策略round-rob ...
- Java 工厂模式(一)— 工厂方法(Factory Method)模式
一.工厂方法(Factory Method)模式: 1.什么是工厂方法模式? 工厂方法模式是类的创建型模式,又叫做虚拟构造子模式或者多态工厂模式.它的意义是创建产品对象的工厂接口,将实际创建工作推迟到 ...
- IO流-输入输出的简单实例
InputStream和OutputStream 抽象类InputStream和OutputStream是IO流最底层的两个抽象类,所有输入/输出流的类都基于这两个类. 这两个类里最核心的三个方法是r ...
- [总结]CSS/CSS3常用样式与web移动端资源
CSS/CSS3常用样式与知识点 IE条件注释 条件注释简介 IE中的条件注释(Conditional comments)对IE的版本和IE非IE有优秀的区分能力,是WEB设计中常用的hack方法.条 ...
- 阿里云小程序云应用环境DIY,延长3倍免费期
阿里云清明节前刚刚推出了小程序云应用扶持计划一期活动 (活动链接见文章底部).假期研究了下以后,发觉不太给力.基本上就是给了2个月的免费测试环境,和平均2个月的基础版生产环境.而如果选用标准版生产环境 ...
- C#开发命名规范总结整理
1. 命名规范a) 类[规则1-1]使用Pascal规则命名类名,即首字母要大写.eg:Class Test{ ...}[规则1-2]使用能够反映类功能的名词或名词短语命名类.[规则1-3]不 ...
- 生鲜配送管理系统_升鲜宝供应链系统V2.0 设计思想及主要模块,欢迎大家批评指点。
前言: 经过这几年的开发,升鲜宝生鲜供应链系统管理软件,终于完成C/S与B/S二个版本的开发,先主要介绍B/S版本的功能,C/S版本的功能更加完善. 升鲜宝供应链系统主要由以下几个主要端组成: ...