9.8 GO 匿名函数和闭包

匿名函数:顾名思义就是没有名字的函数。很多语言都有如:java,js,php等,其中js最钟情。匿名函数最大的用途是来模拟块级作用域,避免数据污染的。

今天主要讲一下Golang语言的匿名函数和闭包。

匿名函数

示例:

1、

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main() {
  6. f:=func(){
  7. fmt.Println("hello world")
  8. }
  9. f()//hello world
  10. fmt.Printf("%T\n", f) //打印 func()
  11. }

2、带参数

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main() {
  6. f:=func(args string){
  7. fmt.Println(args)
  8. }
  9. f("hello world")//hello world
  10. //或
  11. (func(args string){
  12. fmt.Println(args)
  13. })("hello world")//hello world
  14. //或
  15. func(args string) {
  16. fmt.Println(args)
  17. }("hello world") //hello world
  18. }

3、带返回值

  1. package main
  2. import "fmt"
  3. func main() {
  4. f:=func()string{
  5. return "hello world"
  6. }
  7. a:=f()
  8. fmt.Println(a)//hello world
  9. }

4、多个匿名函数

  1. package main
  2. import "fmt"
  3. func main() {
  4. f1,f2:=F(1,2)
  5. fmt.Println(f1(4))//6
  6. fmt.Println(f2())//6
  7. }
  8. func F(x, y int)(func(int)int,func()int) {
  9. f1 := func(z int) int {
  10. return (x + y) * z / 2
  11. }
  12. f2 := func() int {
  13. return 2 * (x + y)
  14. }
  15. return f1,f2
  16. }

闭包(closure)

闭包:说白了就是函数的嵌套,内层的函数可以使用外层函数的所有变量,即使外层函数已经执行完毕。

示例:

1、

  1. package main
  2. import "fmt"
  3. func main() {
  4. a := Fun()
  5. b:=a("hello ")
  6. c:=a("hello ")
  7. fmt.Println(b)//worldhello
  8. fmt.Println(c)//worldhello hello
  9. }
  10. func Fun() func(string) string {
  11. a := "world"
  12. return func(args string) string {
  13. a += args
  14. return a
  15. }
  16. }

2、

  1. package main
  2. import "fmt"
  3. func main() {
  4. a := Fun()
  5. d := Fun()
  6. b:=a("hello ")
  7. c:=a("hello ")
  8. e:=d("hello ")
  9. f:=d("hello ")
  10. fmt.Println(b)//worldhello
  11. fmt.Println(c)//worldhello hello
  12. fmt.Println(e)//worldhello
  13. fmt.Println(f)//worldhello hello
  14. }
  15. func Fun() func(string) string {
  16. a := "world"
  17. return func(args string) string {
  18. a += args
  19. return a
  20. }
  21. }

注意两次调用F(),维护的不是同一个a变量。

3、

  1. package main
  2. import "fmt"
  3. func main() {
  4. a := F()
  5. a[0]()//0xc00004c080 3
  6. a[1]()//0xc00004c080 3
  7. a[2]()//0xc00004c080 3
  8. }
  9. func F() []func() {
  10. b := make([]func(), 3, 3)
  11. for i := 0; i < 3; i++ {
  12. b[i] = func() {
  13. fmt.Println(&i,i)
  14. }
  15. }
  16. return b
  17. }

闭包通过引用的方式使用外部函数的变量。例中只调用了一次函数F,构成一个闭包,i 在外部函数B中定义,所以闭包维护该变量 i ,a[0]、a[1]、a[2]中的 i 都是闭包中 i 的引用。因此执行,i 的值已经变为3,故再调用a0时的输出是3而不是0。

4、如何避免上面的BUG ,用下面的方法,注意和上面示例对比。

  1. package main
  2. import "fmt"
  3. func main() {
  4. a := F()
  5. a[0]() //0xc00000a0a8 0
  6. a[1]() //0xc00000a0c0 1
  7. a[2]() //0xc00000a0c8 2
  8. }
  9. func F() []func() {
  10. b := make([]func(), 3, 3)
  11. for i := 0; i < 3; i++ {
  12. b[i] = (func(j int) func() {
  13. return func() {
  14. fmt.Println(&j, j)
  15. }
  16. })(i)
  17. }
  18. return b
  19. }
  20. 或者
  21. package main
  22. import "fmt"
  23. func main() {
  24. a := F()
  25. a[0]() //0xc00004c080 0
  26. a[1]() //0xc00004c088 1
  27. a[2]() //0xc00004c090 2
  28. }
  29. func F() []func() {
  30. b := make([]func(), 3, 3)
  31. for i := 0; i < 3; i++ {
  32. j := i
  33. b[i] = func() {
  34. fmt.Println(&j, j)
  35. }
  36. }
  37. return b
  38. }

每次 操作仅将匿名函数放入到数组中,但并未执行,并且引用的变量都是 i,随着 i 的改变匿名函数中的 i 也在改变,所以当执行这些函数时,他们读取的都是环境变量 i 最后一次的值。解决的方法就是每次复制变量 i 然后传到匿名函数中,让闭包的环境变量不相同。

5、

  1. package main
  2. import "fmt"
  3. func main() {
  4. fmt.Println(F())//2
  5. }
  6. func F() (r int) {
  7. defer func() {
  8. r++
  9. }()
  10. return 1
  11. }

输出结果为2,即先执行r=1 ,再执行r++。

6、递归函数

还有一种情况就是必须用都闭包,就是递归函数。

  1. package main
  2. import "fmt"
  3. func F(i int) int {
  4. if i <= 1 {
  5. return 1
  6. }
  7. return i * F(i-1)
  8. }
  9. func main() {
  10. var i int = 3
  11. fmt.Println(i, F(i))// 3 6
  12. }

7、斐波那契数列(Fibonacci)

这个数列从第3项开始,每一项都等于前两项之和。

  1. package main
  2. import "fmt"
  3. func fibonaci(i int) int {
  4. if i == 0 {
  5. return 0
  6. }
  7. if i == 1 {
  8. return 1
  9. }
  10. return fibonaci(i-1) + fibonaci(i-2)
  11. }
  12. func main() {
  13. var i int
  14. for i = 0; i < 10; i++ {
  15. fmt.Printf("%d\n", fibonaci(i))
  16. }
  17. }

小结:

匿名函数和闭包其实是一回事儿,匿名函数就是闭包。匿名函数给编程带来灵活性的同时也容易产生bug,在使用过程当中要多注意函数的参数,及可接受的参数的问题。

links