Go语言中的闭包理解

参考

http://c.biancheng.net/view/59.html
https://www.jianshu.com/p/faf7ef7fbcf8
https://www.calhoun.io/5-useful-ways-to-use-closures-in-go/

关键点

希望通过下面的关键词,实现目的:能够快速回忆理解复习 知识点:

1、闭包 = 匿名函数 + 引用环境

2、函数,是编译器静态的概念

3、闭包,是运行期动态的概念

4、闭包的本质什么?要解决什么问题
可以从以下几个方面去理解:

  • 将函数增加记忆功能,

  • 函数的视角去看,将函数运行结束后状态能够保存下来

  • 引用环境的视角去看,对同一块内存,用同一个函数 操作后的结果,可以操作一次,也可以操作多次

  • 闭包很适合迭代场景下的使用,可以迭代一次,也可以迭代多次

  • 引用环境,可以认为是中间态,或者说,运行态 , 或者说 一个变量,经过匿名函数处理后,会产生不同的,就是不同的状态

  • 还可以从另外一个角度去分析:一个函数内部修改了某个变量(内存),等这个函数执行完毕后,这个变量还在,下次再次执行这个函数的时候,可以继续获取到当前的变量,即执行这个函数的时候,都是在上次的执行结果之上进行的;那么,这个变量,肯定是在函数外部定义的,然后,对这个变量和函数进行封装成一个函数,那么整体就是闭包了。递归操作,就有这种特性
    含有以上特征时,可以考虑使用闭包

1、什么闭包?

简单的说,就是

闭包 = 匿名函数 + 引用环境

同一个函数与不同引用环境的组合,可以形成不同的实例,如下图所示:

闭包与函数引用

2、函数是否可以存储状态,或者说,存储信息?

一个函数类型就像结构体一样,可以被实例化,
函数本身不存储任何信息,只有与引用环境结合后的闭包,才具有"记忆性";

调用函数时会创建内存,
但是,当函数调用结束后,内存释放掉了,
因此, 函数本身不具备存储任何信息的能力

3、什么是 引用环境

由于闭包把函数和运行时的引用环境打包成为一个新的整体, 所以就解决了函数编程中的前嵌套所引发的问题。
当每次调用包含闭包的函数时都将返回一个新的闭包实例,这些实例之间是隔离的, 分别包含调用时不同的引用环境现场。
不同于函数,闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

对引用环境的说明

4、闭包的本质是什么呢?

可以从以下几个方面去理解:
1、将函数增加记忆功能,

2、从函数的视角去看,将函数运行结束后的状态能够保存下来

3、从引用环境的视角去看,对同一块内存,用同一个函数 操作后的结果,可以操作一次,也可以操作多次

4、闭包很适合迭代场景下的使用,可以迭代一次,也可以迭代多次

5、引用环境,可以认为是中间态,或者说,运行态
含有以上特征时,可以考虑使用闭包

5、什么场景下使用闭包呢?

5.1、例子 1:计算一个切片的和

需求分析:
一个切片是有至少一个元素以上构成的,那么切片的和是一个累加的过程,
既然是累加的过程就会有 中间过程,中间态,
把中间过程(中间态)看做是一个引用环境,

package main

import "fmt"

func sum() func(int) int  {
    var num int

    return func(temp int) int {
        num += temp
        return num
    }
}

func main() {
    data := []int{
        2,3,4,1,5,1,
    }
    s := sum()
    var result int
    for _, value := range data{
        result = s(value)
    }

    fmt.Printf("result = %d\n", result)

}

image

5.2、例子 2:计算一下某个函数调用次数

package main

import "fmt"

func createApi(str string) {
    fmt.Printf("createApi recevice str =\t%s\n", str)
}

func countFunc(f func(string)) func(string) int {
    var count int

    return func(str string) int {
        f(str)
        count++
        return count
    }
}

func main() {
    cf := countFunc(createApi)

    cf("hello")
    cf("world")

    result := cf("Golang")

    fmt.Printf("call createApi num %d", result)
}

image

5.3、例子 3: 斐波那契数列

如:
1,2,3,5,8,13,21,34,……

package main

import "fmt"

func makeFibGen() func() int {
    f0 := 0
    f1 := 1

    return func() int {
        f1, f0 = (f0 + f1), f1

        return f0
    }
}

func main() {
    gen := makeFibGen()

    for i := 0; i < 10; i++ {
        fmt.Printf("%d ", gen())
    }
}

image

6、闭包中遇到

请参考下面的博客:
https://www.jianshu.com/p/fa21e6fada70
https://blog.csdn.net/chunyuan314/article/details/81247746
https://www.jianshu.com/p/faf7ef7fbcf8
https://blog.csdn.net/chunyuan314/article/details/81247746

问题核心是:

  • 闭包中的匿名函数,在引用环境时,不是拷贝,而是引用

  • for循环会很快执行结束,而协程还未开始,导致协程在获取for循环给的变量时,拿到的都是 最后一个变量

  • 解决措施,

    • 在创建协程后,需要添加阻塞,如休息一小段时间

    • 传递for循环的变量时,先复制给 一个临时变量,而临时变量属于值拷贝,从而协程拿到了属于自己的变量

    • 还可以使用通道,如 https://blog.csdn.net/li_101357/article/details/80196650

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容