Hello world

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
*/

  

 

  

  

 

  

  

posted @   hershell  阅读(596)  评论(0)    收藏  举报
编辑推荐:
· 如何统计不同电话号码的个数?—位图法
· C#高性能开发之类型系统:从 C# 7.0 到 C# 14 的类型系统演进全景
· 从零实现富文本编辑器#3-基于Delta的线性数据结构模型
· 记一次 .NET某旅行社酒店管理系统 卡死分析
· 长文讲解 MCP 和案例实战
阅读排行:
· C#高性能开发之类型系统:从 C# 7.0 到 C# 14 的类型系统演进全景
· 管理100个小程序-很难吗
· 基于Blazor实现的运输信息管理系统
· 如何统计不同电话号码的个数?—位图法
· 微信支付功能的设计实现与关键实践(UniApp+Java)全代码
点击右上角即可分享
微信分享提示