go:匿名函数与闭包
一、匿名函数
定义:没有函数名的函数。
作用:在go语言中目前了解的作用就是用于构成闭包。
*注:由于javascript不存在块级作用域,故匿名函数常用来包含代码以不污染全局命名空间,运行后销毁环境。
----来自知乎回答:http://www.zhihu.com/question/34649602
使用方法及其原理请参考:http://www.cnblogs.com/chenxianbin89/archive/2010/01/28/1658392.html
使用举例
(1)
1 2 3 4 | a := func () { fmt.Println(1) } a() //输出:1 |
(2)带参数
1 2 3 4 5 6 7 8 9 | b := func (arg int) { fmt.Println(arg) } b(2) //输出:2 ( func (arg int) { fmt.Println(arg) })(3) //输出:3 |
(3)带返回值
1 2 3 4 5 6 | c := func () int { fmt.Println(4) return 5 } d := c() //打印输出4,并将5赋值给d fmt.Println(d) |
二、闭包(closure)
闭包的理解参考:http://www.cnblogs.com/mzwr1982/archive/2012/05/20/2509295.html
闭包的用途参考:http://blog.csdn.net/sunlylorn/article/details/6534610
和 http://www.cnblogs.com/rainman/archive/2009/05/04/1448899.html
简单来说:
因为把返回的函数赋给了一个变量,虽然函数在执行完一瞬间会销毁其执行环境,
但是如果有闭包的话,闭包会保存外部函数的活动对象(变量),所以如果不把对闭包的引用消除掉,
闭包会一直存在内存中,垃圾收集器不会销毁闭包占用的内存。
----来自知乎回答http://www.zhihu.com/question/34649602
使用举例
(1)
1 2 3 4 5 6 7 8 9 10 | //函数A是一个不带参数,返回值是一个匿名函数,且该函数 //带有一个int类型参数,返回值为一个int类型 func A() func (aa int) int { sum := 0 return func (cc int) int { sum += cc fmt.Println( "aa=" , aa, "bb=" , bb, " sum=" , sum) return sum } } //编译错误,提示aa未定义 |
实际上func(aa int) int只是函数A的返回值,在这里给参数取名无任何作用,反而会影响代码阅读,直接用func(int) int 即可。
更正后:
1 2 3 4 5 6 7 8 | func A() func (int) int { sum := 0 return func (bb int) int { sum += bb fmt.Println( "bb=" , bb, "\tsum=" , sum) return sum } } |
调用1:
1 2 3 4 5 6 7 8 9 10 | func main() { a := A() //定义变量a,并将函数A的返回值赋给a b := a(4) fmt.Println(b) } /* ** 输出: ** bb= 4 sum= 4 ** 4 */ |
调用2:
1 2 3 4 5 6 7 8 9 10 11 12 | func main() { a := A() a(0) a(1) a(5) } /* ** 输出: ** bb= 0 sum= 0 ** bb= 1 sum= 1 ** bb= 5 sum= 6 */ |
以上调用通过闭包实现了sum的累加
调用3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | func main() { a := A() c := A() a(0) a(5) c(10) c(20) } /* ** 输出: ** bb= 0 sum= 0 ** bb= 5 sum= 5 ** bb= 10 sum= 10 ** bb= 20 sum= 30 */ |
可以看出,上例中调用了两次函数A,构成了两个闭包,这两个闭包维护的变量sum不是同一个变量。
(2)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | func B() [] func () { b := make([] func (), 3, 3) for i := 0; i < 3; i++ { b[i] = func () { fmt.Println(i) } } return b } func main() { c := B() c[0]() c[1]() c[2]() } /* ** 输出: ** 3 ** 3 ** 3 */ |
闭包通过引用的方式使用外部函数的变量。
上例中只调用了一次函数B,构成一个闭包,i 在外部函数B中定义,所以闭包维护该变量 i ,c[0]、c[1]、c[2]中的 i 都是闭包中 i 的引用。
因此执行c:=B()后,i 的值已经变为3,故再调用c[0]()时的输出是3而不是0。
可作如下修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | func B() [] func () { b := make([] func (), 3, 3) for i := 0; i < 3; i++ { b[i] = ( func (j int) func () { return func () { fmt.Println(j) } })(i) } return b } func main() { c := B() c[0]() c[1]() c[2]() } /* ** 输出: ** 0 ** 1 ** 2 */ |
以上修改可能没有什么实际意义,此处仅为说明问题使用。
在使用defer的时候可能出现类似问题,需要注意:
1 2 3 4 5 6 7 8 9 10 | for j := 0; j < 2; j++ { defer ( func () { fmt.Println(j) })() } /* ** 输出: ** 2 ** 2 */ |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】凌霞软件回馈社区,携手博客园推出1Panel与Halo联合会员
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何统计不同电话号码的个数?—位图法
· C#高性能开发之类型系统:从 C# 7.0 到 C# 14 的类型系统演进全景
· 从零实现富文本编辑器#3-基于Delta的线性数据结构模型
· 记一次 .NET某旅行社酒店管理系统 卡死分析
· 长文讲解 MCP 和案例实战
· C#高性能开发之类型系统:从 C# 7.0 到 C# 14 的类型系统演进全景
· 管理100个小程序-很难吗
· 基于Blazor实现的运输信息管理系统
· 如何统计不同电话号码的个数?—位图法
· 微信支付功能的设计实现与关键实践(UniApp+Java)全代码