爱上开源之golang入门至实战第四章函数(Func)(六)

爱上开源之golang入门至实战第四章函数(Func)(六)

4.4.6 匿名函数和闭包

Go语言支持匿名函数,在前面就讲到过,在Go语言里,函数更像是定义的一个函数类型的对象;在Go语言里,匿名函数更复合这个特性;匿名函数可以在需要使用函数时再定义函数,匿名函数没有函数名只有函数体,这样GO语言的匿名函数作为一种类型被赋值给函数类型的变量,匿名函数也往往以变量方式传递,这与C语言的回调函数比较类似,不同的是,Go语言支持随时在代码里定义匿名函数。 下面来具体介绍一下匿名函数的定义及使用。

匿名函数是指不需要定义函数名的一种函数实现方式,由一个不带函数名的函数声明和函数体组成。

func(参数列表)(返回参数列表){
函数体
}

下面我们来看看一个匿名函数的简单例子

fn := func(x int) int {
    return x * x
}
​
fmt.Printf("Result = %d \n", fn(3))
​
==== OUTPUT ====
Result = 9 

上面的例子就是一个简单的匿名函数的示例,示例里通过 func(x int) int, 定义了一个参数类型是

int,返回值是int的匿名函数;函数体实现了返回参数的平方的结果;然后将匿名函数定义的变量赋

值给变量fn; 最后执行fn(3)对函数进行了调用;

匿名函数也被称闭包函数(函数式语言的术语),它们被允许调用定义在其它环境下的变量。闭包

可使得某个函数捕捉到一些外部状态,

例如:函数被创建时的状态。另一种表示方式为:一个闭包继承了 函数所声明时的作用域。这种

状态(作用域内的变量)都被共享到闭包的环境中,因此这些变量可以在闭包中被操作,直到被销

毁。闭包经常被用作包装函数:它们会预先定义好 1 个或多个参数以用于包装。另一个不错的应用

就是使用闭包来完成更加简洁的错误检查。 仅仅从形式上将闭包简单理解为匿名函数是不够的,

还需要理解闭包;实质上的含义。 实质上看,闭包是由函数及其相关引用环境组合而成的实体

(即:闭包=函数+引用

环境)。闭包在运行时 可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

由闭包的实质含义,可以判断出,闭

包获取捕获变量相当于引用传递,而非值传递;对于闭包函数捕获的常量和变量,无论闭包何时何

处被调用,闭包都可以使用这些常量和

变量,而不用关心它们表面上的作用域。利用匿名函数(闭包函数)的这些作用域特性去实现一些

功能,就是编程技巧的一种体现。看看下面的示例:

func TestAnonymous2(t *testing.T) {
   for idx := 0; idx < 10; idx++ {
​
      fn := func() int {
         return idx * idx
      }
​
      fmt.Printf("Result(%d*%d) = %d \n", idx, idx, fn())
   }
}
​
==== OUTPUT ====
Result(0*0) = 0 
Result(1*1) = 1 
Result(2*2) = 4 
Result(3*3) = 9 
Result(4*4) = 16 
Result(5*5) = 25 
Result(6*6) = 36 
Result(7*7) = 49 
Result(8*8) = 64 
Result(9*9) = 81
 
上面的例子,匿名函数就是用了作用域内的变量idx; 对于闭包函数捕获的常量和变量,无论闭包何时何处被调用,闭包都可以使用这些常量和变量,而不用关心它们表面上的作用域。

把上面的例子,改一改,看看匿名函数对作用域的变量idx的修改会有什么样的影响

func TestAnonymous2(t *testing.T) {
    for idx := 0; idx < 10; {
        fn := func() (rtn int) {
            rtn = idx * idx
            idx++
            return
        }
​
        fmt.Printf("Result(%d*%d) = %d \n", idx, idx, fn())
    }
}
​
==== OUTPUT ====
Result(1*1) = 0 
Result(2*2) = 1 
Result(3*3) = 4 
Result(4*4) = 9 
Result(5*5) = 16 
Result(6*6) = 25 
Result(7*7) = 36 
Result(8*8) = 49 
Result(9*9) = 64 
Result(10*10) = 81 

由这段代码以判断出,闭包获取捕获变量相当于直接引用了变量, 对变量的任何修改都直接修改变量;类似于引用传递的效果,而非

值传递。

func TestAnonymous3(t *testing.T) {
    var fn func(x uint8) []uint8
    fn = func(x uint8) (rtn []uint8) {
​
        if x == 0 {
            rtn = append(rtn, 0)
            return
        }
        if x == 1 {
            rtn = append(rtn, 0, 1)
            return
        }
        if x == 2 {
            rtn = append(rtn, 0, 1, 1)
            return
        }
​
        rtn = append(fn(x - 1))
        rtn = append(rtn, rtn[len(rtn)-1]+rtn[len(rtn)-2])
        return
    }
​
    fmt.Printf("Result(%d) = %v \n", 10, fn(10))
}
​
==== OUTPUT ====
Result(10) = [0 1 1 2 3 5 8 13 21 34 55] 

匿名函数的递归调用的例子; 结合着上面两个例子的有关作用域的特性, 可以写出非常强大的代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

inthirties

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值