七叶笔记 » golang编程 » golang2021函数与包(19)Go语言闭包实践

golang2021函数与包(19)Go语言闭包实践

    Go语言闭包

    闭包并不是什么新奇的概念,它早在高级语言开始发展的年代就产生了。

    1、闭包的概念

    闭包(Closure)是词法闭包(Lexical  Closure)的简称。是由函数和与其相关的引用环境组合而成的实体。在实现深约束时,需要创建一个能显式表示引用环境的东西,并将它与相关的子程序捆绑在一起,这样捆绑起来的整体被称为闭包。函数  + 引用环境 = 闭包。

     

    闭包只是在形式和表现上像函数,但实际上不是函数。函数是一些可执行的代码,这些代码在函数被定义后就确定了,不会在执行时发生变化,所以一个函数只有一个实例。

     

    闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。闭包在某些编程语言中被称为  Lambda 表达式。

     

    函数本身不存储任何信息,只有与引用环境结合后形成的闭包才具有“记忆性”。函数是编译器静态的概念,而闭包是运行期动态的概念。

     

    对象是附有行为的数据,而闭包是附有数据的行为。

    2、闭包的优点

    1) 加强模块化

    闭包有益于模块化编程,便于以简单的方式开发较小的模块,从而提高开发速度和程序的可复用性。和没有使用闭包的程序相比,使用闭包可将模块划分得更小。

     

    比如要计算一个数组中所有数字的和,只需要循环遍历数组,把遍历到的数字加起来就行了。如果现在要计算所有元素的积,又或者要打印所有的元素呢?解决这些问题都要对数组进行遍历,如果是在不支持闭包的语言中,程序员不得不一次又一次重复地写循环语句。而这在支持闭包的语言中是不必要的。这种处理方法多少有点像回调函数,不过要比回调函数写法更简单,功能更强大。

    2) 抽象

    闭包是数据和行为的组合,这使得闭包具有较好的抽象能力。

    3) 简化代码

    一个编程语言需要以下特性来支持闭包。

    • 函数是一阶值(First-class       value,一等公民),即函数可以作为另一个函数的返回值或参数,还可以作为一个变量的值;

    • 函数可以嵌套定义,即在一个函数内部可以定义另一个函数;

    • 允许定义匿名函数;

    • 可以捕获引用环境,并把引用环境和函数代码组成一个可调用的实体。

    接下来通过三个示例的对比来更清晰地了解闭包的作用。

     

    【示例】没有使用闭包进行计数的代码。

     

  1. package       main

  2. import “fmt”

  3. func main() {

  4. for i := 0; i < 5; i++ {

  5. fmt.Printf(“i=%d       \t”, i)

  6. fmt.Println(add2(i))

  7. }

  8. }

  9. func add2(x int) int {

  10. sum := 0

  11. sum += x

  12. return sum

  13. }

运行结果如下:

i=0     0

i=1     1

i=2     2

i=3     3

i=4     4

由上面的示例可以看出,for 循环每执行一次,sum 都会清零,没有实现 sum 累加计数。

 

【示例】使用闭包函数实现计数器。

 

  1. package       main

  2. import “fmt”

  3. func main() {

  4. pos := adder()

  5. for i := 0; i < 10; i++       {

  6. fmt.Printf(“i=%d       \t”, i)

  7. fmt.Println(pos(i))

  8. }

  9. fmt.Println(“——————–“)

  10. for i := 0; i < 10; i++       {

  11. fmt.Printf(“i=%d       \t”, i)

  12. fmt.Println(pos(i))

  13. }

  14. }

  15. func adder() func(int) int       {

  16. sum := 0

  17. return func(x int) int {

  18. fmt.Printf(“sum1=%d       \t”, sum)

  19. sum += x

  20. fmt.Printf(“sum2=%d       \t”, sum)

  21. return sum

  22. }

  23. }

运行结果如下:

i=0     sum1=0   sum2=0  0

i=1     sum1=0   sum2=1  1

i=2     sum1=1   sum2=3  3

i=3     sum1=3   sum2=6  6

i=4     sum1=6   sum2=10     10

i=5     sum1=10     sum2=15     15

i=6     sum1=15     sum2=21     21

i=7     sum1=21     sum2=28     28

i=8     sum1=28     sum2=36     36

i=9     sum1=36     sum2=45     45

——————–

i=0     sum1=45     sum2=45     45

i=1     sum1=45     sum2=46     46

i=2     sum1=46     sum2=48     48

i=3     sum1=48     sum2=51     51

i=4     sum1=51     sum2=55     55

i=5     sum1=55     sum2=60     60

i=6     sum1=60     sum2=66     66

i=7     sum1=66     sum2=73     73

i=8     sum1=73     sum2=81     81

i=9     sum1=81     sum2=90     90

【示例】闭包使用案例。

 

  1. package       main

  2. import “fmt”

  3. func main() {

  4. myfunc := Counter()

  5. //       fmt.Printf(“%T\n”, myfunc)

  6. fmt.Println(“myfunc”,       myfunc)

  7. // 调用 myfunc 函数,i变量自增 1 并返回

  8. fmt.Println(myfunc())

  9. fmt.Println(myfunc())

  10. fmt.Println(myfunc())

  11. // 创建新的函数 nextNumber1,并查看结果

  12. myfunc1 := Counter()

  13. fmt.Println(“myfunc1”,       myfunc1)

  14. fmt.Println(myfunc1())

  15. fmt.Println(myfunc1())

  16. }

  17. //计数器,闭包函数

  18. func Counter() func() int {

  19. i := 0

  20. res := func() int {

  21. i += 1

  22. return i

  23. }

  24. // fmt.Printf(“%T, %v       \n”, res, res)

  25. fmt.Println(“Counter       中的内部函数:”, res)

  26. return res

  27. }

运行结果如下:

Counter 中的内部函数:  0x49ac50

myfunc 0x49ac50

1

2

3

Counter 中的内部函数:  0x49ac50

myfunc1 0x49ac50

1

2

由于闭包函数“捕获”了和它在同一作用域的其他常量和变量,所以当闭包在任何地方被调用,闭包都可以使用这些常量或者变量。它不关心这些变量是否已经超出作用域,只要闭包还在使用这些变量,这些变量就依然存在。

相关文章