golang闭包中遇到的一点小问题

一直用java没用过闭包(我还很菜,可能java也能闭包我不会罢了,希望有人回帖指点),刚接触golang对其闭包机制也挺疑惑。下面写了六个版本的闭包小谈一下。版本1和版本4代码摘自别家,看了有段时间我一时不记得来源了很对不起作者了!

版本1:

package main

import "fmt"

func main() {

    var fn [10]func()

    for i := 0; i < len(fn); i++ {
        fn[i] = func() {
            fmt.Println(i)
        }
    }

    for _, f := range fn {
        f()
    }
}

结果如下:

10
10
10
10
10
10
10
10
10
10

分析:mian()与func()[]数组构成闭包使用同一个i变量main函数不退出i变量一直存在,f()执行时调用打印语句此时变量i为10。 版本2:

package main

import "fmt"

func main() {

    var fn [10]func()

    for i := 0; i < len(fn); i++ {
        fn[i] = func() {
            fmt.Println(i)
        }
    }

    for i := 0 ; i < len(fn); i++ {
        fn[i]()
    }
}

结果如下:

10
10
10
10
10
10
10
10
10
10

分析:与版本1对比使用显示的i变量做为迭代,但是闭包空间中的i与调用迭代中的i指向内存不同(生存空间也不同)所以使用闭包空间中的i作为打印值为10。 版本3:

package main

import "fmt"

func main() {

    var fn [10]func()

    for i := 0; i < len(fn); i++ {
        fn[i] = func() {
            fmt.Println(i)
        }
    }

    for j := 0 ; j < len(fn); j++ {
        fn[j]()
    }
}

结果如下:

10
10
10
10
10
10
10
10
10
10

分析:为证明版本2中的分析说法,使用j作为迭代变量,同样打印的10. 版本4:

package main

import "fmt"

func main() {

    var fn [10]func()
    var i int

    for i = 0; i < len(fn); i++ {
        fn[i] = func() {
            fmt.Println(i)
        }
    }

    for i = 0; i < len(fn); i++ {
        fn[i]()
    }
}

结果如下:

0
1
2
3
4
5
6
7
8
9

分析:在main()中声明变量i此时i的生存空间扩大到了main()函数,两次迭代使用同一个i变量。故在第二次迭代时i的迭代当前值会作为打印参数。 版本5:

package main

import "fmt"

func main() {

    var fn [10]func()

    for i := 0; i < len(fn); i++ { 
        fn[i] = make_fn(i)
    }

    for _, f := range fn {
        f()
    }
}

func make_fn(i int) func() {
    return func() {
        fmt.Println(i)
    }
}

结果如下:

0
1
2
3
4
5
6
7
8
9

分析:在main()函数外定义单独的闭包函数,构成独立的闭包单元,隔离不同func()[]中不同的func()。隔离与独立才是闭包的意义所在,一个表示一系列状态的集合不该在外部为显示通知改变时改变其内部状态。 版本6:

package main

import "fmt"

func main() {

    var fn [10]func(int)

    for i := 0; i < len(fn); i++ { 
        fn[i] = make_fn()
    }

    for i, f := range fn {
        f(i)
    }
}

func make_fn() func(i int) {
    return func(i int) {
        fmt.Println(i)
    }
}

结果如下:

0
1
2
3
4
5
6
7
8
9

分析:最后这个版本应该为最佳,gotour中的闭包示例使用的正是此中表示方式。
能力有限只能写这么多了,本人第一次发博支持一下,有意见直说谢谢(漫骂亦可)! 发在csdn上一直没人回复,在这里请大家指点一下,我是否理解有误?

共 3 个回复


polaris

判断的标准:闭包引用的外部变量的值是多少。 比如你例子中的i,在闭包内部函数调用时,这个i是多少很重要。 4、5、6的版本,每次fn中的i或者会变化或者跟其他fn中的不一样。

# 0

sese53

谢谢作者oral,很有礼貌而且这些代码例子和结果,对我理解帮助很大。

# 1

a7a2

代码写法很多,真还未见过必须用到闭包的,也不知道闭包存在的意义,对于一个业余程序人员来说

# 2