千家信息网

Go回调函数和闭包怎么使用

发表于:2025-01-21 作者:千家信息网编辑
千家信息网最后更新 2025年01月21日,本篇内容介绍了"Go回调函数和闭包怎么使用"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Go回调函数
千家信息网最后更新 2025年01月21日Go回调函数和闭包怎么使用

本篇内容介绍了"Go回调函数和闭包怎么使用"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

Go回调函数和闭包

当函数具备以下两种特性的时候,就可以称之为高阶函数(high order functions):

  • 函数可以作为另一个函数的参数(典型用法是回调函数)

  • 函数可以返回另一个函数,即让另一个函数作为这个函数的返回值(典型用法是闭包)

一般来说,附带的还具备一个特性:函数可以作为一个值赋值给变量。

f := func(){...}f()

由于Go中函数不能嵌套命名函数,所以函数返回函数的时候,只能返回匿名函数。

先简单介绍下高阶函数,然后介绍闭包。

高阶函数示例

例如,将函数作为另一个函数的参数:

package mainimport "fmt"func added(msg string, a func(a, b int) int) {        fmt.Println(msg, ":", a(33, 44))}func main() {        // 函数内部不能嵌套命名函数        // 所以main()中只能定义匿名函数        f := func(a, b int) int {                return a + b        }        added("a+b", f)}

以下示例是函数返回另一个函数:

package mainimport "fmt"func added() func(a, b int) int {        f := func(a, b int) int {                return a + b        }        return f}func main() {        m := added()        fmt.Println(m(33, 44))}

回调函数(sort.SliceStable)

将函数B作为另一个函数A的参数,可以使得函数A的通用性更强,可以随意定义函数B,只要满足规则,函数A都可以去处理,这比较适合于回调函数。

在Go的sort包中有一个很强大的Slice排序工具SliceStable(),它就是将排序函数作为参数的:

func SliceStable(slice interface{}, less func(i, j int) bool)

这个函数是什么意思呢?给定一个名为slice的Slice结构,使用名为less的函数去对这个slice排序。这个less函数的结构为less func(i, j int) bool,其中i和j指定排序依据。Go中已经内置好了排序的算法,我们无需自己去定义排序算法,Go会自动从Slice中每次取两个i和j索引对应的元素,然后去回调排序函数less。所以我们只需要传递升序还是降序、根据什么排序就可以。

例如:

package mainimport (        "fmt"        "sort")func main() {        s1 := []int{112, 22, 52, 32, 12}    // 定义排序函数        less := func(i, j int) bool {        // 降序排序                return s1[i] > s1[j]        // 升序排序:s1[i] < s1[j]        }    //        sort.SliceStable(s1, less)        fmt.Println(s1)}

这里的排序函数就是回调函数。每取一次i和j对应的元素,就调用一次less函数。

可以将排序函数直接写在SliceStable()的参数位置:

sort.SliceStable(s1, func(i, j int) bool {        return s1[i] > s1[j]})

还可以更强大更灵活。例如,按照字符大小顺序来比较,而不是按照数值大小比较:

package mainimport (        "fmt"        "sort"        "strconv")func main() {        s1 := []int{112, 220, 52, 32, 42}        sort.SliceStable(s1, func(i, j int) bool {                // 将i和j对应的元素值转换成字符串                bi := strconv.FormatInt(int64(s1[i]), 10)                bj := strconv.FormatInt(int64(s1[j]), 10)                // 按字符顺序降序排序                return bi > bj        })        fmt.Println(s1)}

按照字符串长度来比较:

package mainimport (        "fmt"        "sort")func main() {        s1 := []string{"hello","malong","gaoxiao"}        sort.SliceStable(s1, func(i, j int) bool {                // 按字节大小顺序降序排序                return len(s1[i]) > len(s1[j])        })        fmt.Println(s1)}

更严格地说是按字节数比较,因为len()操作字符串时获取的是字节数而非字符数。如果要按照字符数比较,则使用如下代码:

package mainimport (        "fmt"        "sort")func main() {        s1 := []string{"hello","世界","gaoxiao"}        sort.SliceStable(s1, func(i, j int) bool {                // 按字节大小顺序降序排序                return len([]rune(s1[i])) > len([]rune(s1[j]))        })        fmt.Println(s1)}

闭包

函数A返回函数B,最典型的用法就是闭包(closure)。

简单地说,闭包就是"一个函数+一个作用域环境"组成的特殊函数。这个函数可以访问不是它自己内部的变量,也就是这个变量在其它作用域内,且这个变量是未赋值的,而是等待我们去赋值的。

例如:

package mainimport "fmt"func f(x int) func(int) int{    g := func(y int) int{        return x+y    }    // 返回闭包    return g}func main() {    // 将函数的返回结果"闭包"赋值给变量a    a := f(3)    // 调用存储在变量中的闭包函数    res := a(5)    fmt.Println(res)    // 可以直接调用闭包    // 因为闭包没有赋值给变量,所以它称为匿名闭包    fmt.Println(f(3)(5))}

上面的f()返回的g之所以称为闭包函数,是因为它是一个函数,且引用了不在它自己范围内的变量x,这个变量x是g所在作用域环境内的变量,因为x是未知、未赋值的自由变量。

如果x在传递给g之前是已经赋值的,那么闭包函数就不应该称为闭包,因为这样的闭包已经失去意义了。

下面这个g也是闭包函数,但这个闭包函数是自定义的,而不是通过函数返回函数得到的。

package mainimport "fmt"func main() {        // 自由变量x        var x int        // 闭包函数g        g := func(i int) int {                return x + i        }        x = 5        // 调用闭包函数        fmt.Println(g(5))        x = 10        // 调用闭包函数        fmt.Println(g(3))}

之所以这里的g也是闭包函数,是因为g中访问了不属于自己的变量x,而这个变量在闭包函数定义时是未绑定值的,也就是自由变量。

闭包的作用很明显,在第一个闭包例子中,f(3)退出后,它返回的闭包函数g()仍然记住了原本属于f()中的x=3。这样就可以让很多闭包函数共享同一个自由变量x的值

例如,下面的a(3)a(5)a(8)都共享来自f()的x=3

a := f(3)a(3)a(5)a(8)

再往外层函数看,f(3)可以将自由变量x绑定为x=3,自然也可以绑定为x=5x=8等等。

所以,什么时候使用闭包?一般来说,可以将过程分成两部分或更多部分都进行工厂化的时候,就适合闭包(实际上,有些地方直接将闭包称呼为工厂函数)。第一个部分是可以给自由变量批量绑定不同的值,第二部分是多个闭包函数可以共享第一步绑定后的自由变量

"Go回调函数和闭包怎么使用"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

函数 闭包 变量 排序 自由 字符 参数 作用 大小 字节 就是 时候 顺序 元素 典型 字符串 更多 部分 高阶 强大 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 赛季服哪个服务器预约角色最多 太原软件开发 工资 网络安全员证考试题库 瘦金体字体软件开发 软件开发行业缴纳多少税点 闵行区软件开发培训班 网络安全宣传周海报 信息安全 网络安全考试 杭州宇鑫网络技术有限公司 辽宁语音网络技术服务咨询报价 网络安全问题是什么 软件开发过程中来自用户的干扰 富士康服务器内存报错如何查看 中专网络技术题库 武汉思连网络技术有限公司 视频渲染服务器配置推荐 50人的设计公司如何搭建服务器 奉贤区正规软件开发信息中心 为什么玩游戏就断开服务器 爱情保卫战网络安全专业 IBM 数据库 dba 服务器管理器配置网站 数据库和硬件区别 湖北正规软件开发哪家便宜 数字转换成大写金额数据库 服务器闪断后无法启动 分布式数据库的两种数据 使用过的包含数据库的应用案例 卫计系统网络安全宣传周 你要知道的网络安全知识
0