GO 匿名函数和闭包

34次阅读

共计 2819 个字符,预计需要花费 8 分钟才能阅读完成。

匿名函数:顾名思义就是没有名字的函数。很多语言都有如:java,js,php 等,其中 js 最钟情。匿名函数最大的用途是来模拟块级作用域, 避免数据污染的。
今天主要讲一下 Golang 语言的匿名函数和闭包。
匿名函数
示例:
1、
package main

import (
“fmt”
)
func main() {
f:=func(){
fmt.Println(“hello world”)
}
f()//hello world
fmt.Printf(“%T\n”, f) // 打印 func()
}
2、带参数
package main

import (
“fmt”
)
func main() {
f:=func(args string){
fmt.Println(args)
}
f(“hello world”)//hello world
// 或
(func(args string){
fmt.Println(args)
})(“hello world”)//hello world
// 或
func(args string) {
fmt.Println(args)
}(“hello world”) //hello world
}
3、带返回值
package main

import “fmt”

func main() {
f:=func()string{
return “hello world”
}
a:=f()
fmt.Println(a)//hello world
}
4、多个匿名函数
package main

import “fmt”

func main() {
f1,f2:=F(1,2)
fmt.Println(f1(4))//6
fmt.Println(f2())//6
}
func F(x, y int)(func(int)int,func()int) {
f1 := func(z int) int {
return (x + y) * z / 2
}

f2 := func() int {
return 2 * (x + y)
}
return f1,f2
}
闭包(closure)
闭包:说白了就是函数的嵌套,内层的函数可以使用外层函数的所有变量,即使外层函数已经执行完毕。
示例:
1、
package main

import “fmt”

func main() {
a := Fun()
b:=a(“hello “)
c:=a(“hello “)
fmt.Println(b)//hello world
fmt.Println(c)//worldhello hello
}
func Fun() func(string) string {
a := “world”
return func(args string) string {
a += args
return a
}
}

2、
package main

import “fmt”

func main() {
a := Fun()
d := Fun()
b:=a(“hello “)
c:=a(“hello “)
e:=d(“hello “)
f:=d(“hello “)
fmt.Println(b)//hello world
fmt.Println(c)//worldhello hello
fmt.Println(e)//hello world
fmt.Println(f)//worldhello hello
}
func Fun() func(string) string {
a := “world”
return func(args string) string {
a += args
return a
}
}
注意两次调用 F(),维护的不是同一个 a 变量。
3、
package main

import “fmt”

func main() {
a := F()
a[0]()//0xc00004c080 3
a[1]()//0xc00004c080 3
a[2]()//0xc00004c080 3
}
func F() []func() {
b := make([]func(), 3, 3)
for i := 0; i < 3; i++ {
b[i] = func() {
fmt.Println(&i,i)
}
}
return b
}
闭包通过引用的方式使用外部函数的变量。例中只调用了一次函数 F, 构成一个闭包,i 在外部函数 B 中定义,所以闭包维护该变量 i,a[0]、a[1]、a[2] 中的 i 都是闭包中 i 的引用。因此执行,i 的值已经变为 3,故再调用 a[0]() 时的输出是 3 而不是 0。
4、如何避免上面的 BUG,用下面的方法,注意和上面示例对比。
package main

import “fmt”

func main() {
a := F()
a[0]() //0xc00000a0a8 0
a[1]() //0xc00000a0c0 1
a[2]() //0xc00000a0c8 2
}
func F() []func() {
b := make([]func(), 3, 3)
for i := 0; i < 3; i++ {
b[i] = (func(j int) func() {
return func() {
fmt.Println(&j, j)
}
})(i)
}
return b
}
或者
package main

import “fmt”

func main() {
a := F()
a[0]() //0xc00004c080 0
a[1]() //0xc00004c088 1
a[2]() //0xc00004c090 2
}
func F() []func() {
b := make([]func(), 3, 3)
for i := 0; i < 3; i++ {
j := i
b[i] = func() {
fmt.Println(&j, j)
}
}
return b
}

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

import “fmt”

func main() {
fmt.Println(F())//2
}
func F() (r int) {
defer func() {
r++
}()
return 1
}
输出结果为 2,即先执行 r =1 , 再执行 r ++。
6、递归函数
还有一种情况就是必须用都闭包,就是递归函数。
package main

import “fmt”

func F(i int) int {
if i <= 1 {
return 1
}
return i * F(i-1)
}

func main() {
var i int = 3
fmt.Println(i, F(i))// 3 6
}
7、斐波那契数列 (Fibonacci)
这个数列从第 3 项开始,每一项都等于前两项之和。
package main

import “fmt”

func fibonaci(i int) int {
if i == 0 {
return 0
}
if i == 1 {
return 1
}
return fibonaci(i-1) + fibonaci(i-2)
}

func main() {
var i int
for i = 0; i < 10; i++ {
fmt.Printf(“%d\n”, fibonaci(i))
}
}
小结:
匿名函数和闭包其实是一回事儿,匿名函数就是闭包。匿名函数给编程带来灵活性的同时也容易产生 bug,在使用过程当中要多注意函数的参数,及可接受的参数的问题。
links
目录

正文完
 0