专栏首页技术面面观三分钟学 Go 语言——函数深度解析(中)

三分钟学 Go 语言——函数深度解析(中)

上回函数深度解析给大家聊了一些函数的基本知识,不知道还有没有人记得,不记得赶紧回去复习!

他们是

  • go语言中函数的基本原理
  • 单/多个同/不同类型参数
  • 单/多个同/不同类型返回值
  • 值传递,引用传递
  • 函数进阶,把函数当作变量传递(在不改变函数内部结构的情况下传入新的实现)

B 站直播分享 go 语言开发入门

昨天晚上小熊把咱们技术小组分享搬到了B站上,可谓是历史性的大突破!!虽然讲的有点磕磕绊绊的,但是有小姐姐夸我声音浑厚好听!!为了这些我整整激动了30分钟。

我今天这篇文章也是在直播状态下写的!

匿名函数

话不多说,今天小熊就带各位家人感受下go语言函数中的高级语法。

前面的文章里我们学会了把函数当作变量传递,可以在不改动原有函数内部实现的情况下,改变函数实现细节(设计模式:装饰器)。

这种情况下的作为变量传递的函数往往只有这一个地方用到了,其他地方不会重复使用。那就没必要单独定义一个函数在外面!(多此一举的事本熊不做!)

like this:

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 })

在调用的时候我们才实现了一个匿名函数(没有名字的函数)

那是不是只有把函数当变量传递的时候才用到匿名函数呢?并,不,是!

各位同学,让我上黑板给大家实现一个简单的匿名函数用法。

f := func(i int) {
  fmt.Println(i)
 }

f(1)

把匿名函数赋值给一个变量(这里是f),f就是他的函数名,后面就可以直接调用啦~,但是这种简单使用的情况实际上会不会用到呢?很残酷,几乎没有。

匿名函数配合下面的场景使用效果更佳。

闭包

你有没有一种情况,常常要定义好多全局变量来共享数据,这种变量一旦多了非常难看,还会污染环境,有没有一种办法,可以通过重复调用同一个函数,来修改函数内部的变量呢?

我翻来覆去发现是真的有!这个东西就叫闭包!

闭包的简单实现,把函数定义在函数内部,并当作返回值返回。

func closureSample() func() {
 count := 0
 return func() {
  count ++
  fmt.Printf("调用次数 %v \n", count)
 }
}

怎么用才爽?我先丧心病狂的调用两次closureSample函数,得到两个函数c1c2,这两个函数就是closureSample函数的返回值,类型是一个匿名函数。

c1, c2 := closureSample(), closureSample()

疯狂调用!!!

 c1()
 c1()
 c1()
 // 你会发现c2又从1开始输出,因为两个函数的变量是独立使用的
 c2()
 c2()

输出

调用次数 1
调用次数 2
调用次数 3
调用次数 1
调用次数 2
调用次数 3

神奇不神奇!在调用c2的时候,完全没有影响到c1

这是因为各个函数是独立使用一套自己的内部变量,互相不影响,所以闭包也可以当测试用例使用。

用来传入不同的实现,重复调用得到不同的返回,不用定义全局变量。

  • 好处:可以减少全局变量防止变量污染
  • 坏处:延长了局部变量和函数的生命周期,增加了 gc 的压力

闭包形式 2

通过上面的例子,不难发现闭包内部的匿名函数可以使用到外部的变量。

闭包形式 2,立即执行函数,声明完以后加括号,用以表示即刻调用。

func() {
  // to do something
 }()

闭包存在的 bug

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

解决办法,创建副本,可以给匿名函数加一个参数,传值过来自动生成副本

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

第二种创建副本的形式

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)

输出

第三次 i 产生变化中 0
第三次 i 产生变化中 1
第三次 i 产生变化中 2
第三次输出: 0
第三次输出: 2
第三次输出: 1

本文分享自微信公众号 - 编程三分钟(coding3min),作者:小熊

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-04-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 三分钟学 Go 语言——函数深度解析(下) 可变参数

    小熊这两天因为个人种种令人难受的原因,没有能更新,也没有提前请假,给大家道歉歉了。

    编程三分钟
  • 撤销本地提交-每天三分钟玩转Git (5)

    上一章我们一起入门学习了git的基本概念和git基础操作,包括提交和同步代码、使用分支、出现代码冲突的解决办法、紧急保存现场和恢复现场的操作。几乎已经足够我们使...

    编程三分钟
  • 快速掌握 Go 语言中的集合(map)

    我本来下午打算对新系统,好好研究下模块划分,但因为上一个版本提测,于是我改了一个下午的bug。

    编程三分钟
  • 【DL笔记1】Logistic Regression:最基础的神经网络

    从【DL笔记1】到【DL笔记N】,是我学习深度学习一路上的点点滴滴的记录,是从Coursera网课、各大博客、论文的学习以及自己的实践中总结而来。从基本的概念、...

    beyondGuo
  • 2017年云计算和数据中心产业的回顾与总结

    随着企业将越来越多的工作负载迁移到云端,云计算提供商需要越来越多的数据中心空间和能力来支持这些工作负载。 如果回溯到2016年,很多大型企业的数据中心工作负载正...

    静一
  • 2017年云计算的回顾与总结

    如果回溯到2016年,很多大型企业的数据中心工作负载正计划向云端迁移。而2017年则是企业的业务向云端迁移的一年,这种迁移在整个数据中心行业引起了很大的反响,为...

    BestSDK
  • 忍者级别的操作JavaScript函数

    对于什么是匿名函数,这里就不做过多介绍了。我们需要知道的是,对于JavaScript而言,匿名函数是一个很重要且具有逻辑性的特性。通常,匿名函数的使用情况是:创...

    Nealyang
  • slim.l2_regularizer()

    版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。

    于小勇
  • 开发者福音!面向Web场景的云开发服务正式开放!

    【导语】继支持小程序开发之后,云开发也支持Web使用啦!开发者们可以使用云开发提供的云端能力,直接开发网站应用,如PC端网页、公众号中的网页等。由此开发者可以在...

    腾讯云开发TCB
  • Python之高阶函数、lambda表达式和装饰器

    版权声明:署名,允许他人基于本文进行创作,且必须基于与原先许可协议相同的许可协议分发本文 (Creative Commons)

    魏晓蕾

扫码关注云+社区

领取腾讯云代金券