💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] ### 匿名函数 > go语言github项目:https://github.com/minibear2333/how_to_code 话不多说,今天小熊就带各位家人感受下`go`语言函数中的高级语法。 在[前面的文章](https://mp.weixin.qq.com/s/HsaEjO9TgUcfrBhaMS0C5A)里我们学会了把函数当作变量传递,可以在**不改动原有函数内部实现**的情况下,**改变函数实现细节**(设计模式:装饰器)。 这种情况下的作为变量传递的函数往往只有这一个地方用到了,其他地方不会重复使用。那就没必要单独定义一个函数在外面!(多此一举的事本熊不做!) like this: ```go func functionValue(a, b int, do func(int, int) int) { fmt.Println(do(a, b)) } //使用匿名函数的方法调用他 实现匿名加函数 funcationValue(1,2,func(a,b int) int{ return a+b }) //使用匿名函数的方法调用他 实现匿名减函数 funcationValue(1,2,func(a,b int) int{ return a-b }) ``` 在调用的时候我们才实现了一个匿名函数(没有名字的函数) 那是不是只有把函数当变量传递的时候才用到匿名函数呢?并,不,是! 各位同学,让我上黑板给大家实现一个简单的匿名函数用法。 ```go f := func(i int) { fmt.Println(i) } f(1) ``` 把匿名函数赋值给一个变量(这里是`f`),`f`就是他的函数名,后面就可以直接调用啦~,但是这种简单使用的情况实际上会不会用到呢?很残酷,几乎没有。 匿名函数配合下面的场景使用效果更佳。 ### 闭包 你有没有一种情况,常常要定义好多全局变量来共享数据,这种变量一旦多了非常难看,还会污染环境,有没有一种办法,可以通过重复调用同一个函数,来修改函数内部的变量呢? 我翻来覆去发现是真的有!这个东西就叫闭包! ![](https://coding3min.oss-accelerate.aliyuncs.com/coding3min/2020-04-27-135104.jpg) 闭包的简单实现,把函数定义在函数内部,并当作返回值返回。 ```go func closureSample() func() { count := 0 return func() { count ++ fmt.Printf("调用次数 %v \n", count) } } ``` 怎么用才爽?我先丧心病狂的调用两次`closureSample`函数,得到两个函数`c1`、`c2`,这两个函数就是`closureSample`函数的返回值,类型是一个匿名函数。 ```go c1, c2 := closureSample(), closureSample() ``` 疯狂调用!!! ![](https://coding3min.oss-accelerate.aliyuncs.com/coding3min/2020-04-27-140145.jpg) ```go c1() c1() c1() // 你会发现c2又从1开始输出,因为两个函数的变量是独立使用的 c2() c2() ``` 输出 ``` 调用次数 1 调用次数 2 调用次数 3 调用次数 1 调用次数 2 调用次数 3 ``` 神奇不神奇!在调用`c2`的时候,完全没有影响到`c1`! 这是因为各个函数是独立使用一套自己的内部变量,互相不影响,所以闭包也可以当测试用例使用。 用来传入不同的实现,重复调用得到不同的返回,不用定义全局变量。 - 好处:可以减少全局变量防止变量污染 - 坏处:延长了局部变量和函数的生命周期,增加了 gc 的压力 ### 闭包形式 2 通过上面的例子,不难发现闭包内部的匿名函数可以使用到外部的变量。 闭包形式 2,立即执行函数,声明完以后加括号,用以表示即刻调用。 ```go func() { // to do something }() ``` ### 闭包存在的 bug `go` 里创建一个协程(类似于子线程)非常的容易,只要在语句前加一个`go`关键字就可以了。看看下面这个函数会出现什么问题。 ```go for i := 0; i < 3; i++ { fmt.Printf("第一次 i 产生变化中 %v \n", i) go func() { fmt.Printf("第一次输出: %v\n", i) }() } time.Sleep(time.Second) ``` 协程创建完以后立即会执行,但是协程创建这个事件和协程执行代码是分离的,他可以全部创建完再执行,而且主线程和协程是同时运行的(并发),有可能主线程执行完了,协程还没执行。 这个时候协程才会调用外部的变量,i 已经变成 3 了。 ``` 第一次 i 产生变化中 0 第一次 i 产生变化中 1 第一次 i 产生变化中 2 第一次输出: 3 第一次输出: 3 第一次输出: 3 ``` 解决办法,创建副本,可以给匿名函数加一个参数,传值过来自动生成副本 ```go for i := 0; i < 3; i++ { fmt.Printf("第二次 i 产生变化中 %v \n", i) go func(tmp int) { fmt.Printf("第二次输出: %v\n", tmp) }(i) } time.Sleep(time.Second) ``` 输出 ``` 第二次 i 产生变化中 0 第二次 i 产生变化中 1 第二次输出: 0 第二次 i 产生变化中 2 第二次输出: 2 第二次输出: 1 ``` 第二种创建副本的形式 ```go for i := 0; i < 3; i++ { fmt.Printf("第三次 i 产生变化中 %v \n", i) tmp := i go func() { fmt.Printf("第三次输出: %v\n", tmp) }() } time.Sleep(time.Second) ``` 输出 ```go 第三次 i 产生变化中 0 第三次 i 产生变化中 1 第三次 i 产生变化中 2 第三次输出: 0 第三次输出: 2 第三次输出: 1 ``` 欢迎来 b 站看我每天晚上 学习直播哦~!