Go 语言核心 36 讲
郝林
《Go 并发编程实战》作者,前轻松筹大数据负责人
79610 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 55 讲
Go 语言核心 36 讲
15
15
1.0x
00:00/00:00
登录|注册

12 | 使用函数的正确姿势

引用类型参数值的注意事项
值类型参数值被复制
动态地生成程序逻辑
使用高阶函数实现闭包
实现高阶函数
编写高阶函数的签名部分
声明函数类型
动态地生成程序逻辑
自由变量
函数中存在对外来标识符的引用
把其他的函数作为结果返回
接受其他的函数作为参数传入
比接口类型更加轻巧、灵活
对一组输入、输出进行模板化的重要工具
函数可以化身为普通的值,在其他函数间传递、赋予变量、做类型判断和转换
函数可以用于封装代码、分割功能、解耦逻辑
函数类型是一等的数据类型
函数真正拿到的参数值其实只是它们的副本,那么函数返回给调用方的结果值也会被复制吗?
complexArray1被传入函数的话,这个函数中对该参数值的修改会影响到它的原值吗?
函数参数值的传递
闭包的实现
怎样编写高阶函数
闭包
高阶函数
函数类型
函数是一等的公民
思考题
问题解析
Go语言中的函数
使用函数的正确姿势

该思维导图由 AI 生成,仅供参考

在前几期文章中,我们分了几次,把 Go 语言自身提供的,所有集合类的数据类型都讲了一遍,额外还讲了标准库的container包中的几个类型。
在几乎所有主流的编程语言中,集合类的数据类型都是最常用和最重要的。我希望通过这几次的讨论,能让你对它们的运用更上一层楼。
从今天开始,我会开始向你介绍使用 Go 语言进行模块化编程时,必须了解的知识,这包括几个重要的数据类型以及一些模块化编程的技巧。首先我们需要了解的是 Go 语言的函数以及函数类型。

前导内容:函数是一等的公民

在 Go 语言中,函数可是一等的(first-class)公民,函数类型也是一等的数据类型。这是什么意思呢?
简单来说,这意味着函数不但可以用于封装代码、分割功能、解耦逻辑,还可以化身为普通的值,在其他函数间传递、赋予变量、做类型判断和转换等等,就像切片和字典的值那样。
而更深层次的含义就是:函数值可以由此成为能够被随意传播的独立逻辑组件(或者说功能模块)。
对于函数类型来说,它是一种对一组输入、输出进行模板化的重要工具,它比接口类型更加轻巧、灵活,它的值也借此变成了可被热替换的逻辑组件。比如,我在 demo26.go 文件中是这样写的:
package main
import "fmt"
type Printer func(contents string) (n int, err error)
func printToStd(contents string) (bytesNum int, err error) {
return fmt.Println(contents)
}
func main() {
var p Printer
p = printToStd
p("something")
}
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了Go语言中函数类型的灵活运用,以及如何利用高阶函数和闭包实现模块化编程的技巧。首先强调了在Go语言中函数是一等公民的重要性,函数类型是一等的数据类型,可以作为普通值在函数间传递、赋值给变量等。接着,文章讨论了高阶函数的概念,即接受其他函数作为参数传入或将其他函数作为结果返回的函数。通过示例,展示了如何编写高阶函数,以及如何实现闭包。文章还介绍了如何使用函数类型进行模块化编程,以及如何利用函数类型实现独立逻辑组件的热替换。此外,还提到了函数传参的注意事项,强调了程序的稳定和安全。整体而言,本文为读者提供了深入了解Go语言函数类型运用的指导,以及如何利用函数式编程的技巧进行模块化编程的实践经验。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Go 语言核心 36 讲》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(52)

  • 最新
  • 精选
  • Geek_牛逼的人
    1.如果是进行一层修改,即数组的某个完整元素进行修改(指针变化),那么原有数组不变;如果进行二层修改,即数组中某个元素切片内的某个元素再进行修改(指针未改变),那么原有数据也会跟着改变,传参可以理解是浅copy,参数本身的指针是不同,但是元素指针相同,对元素指针所指向目的的操作会影响传参过程中的原始数据; 2.一般来说应该是复制的,传参和返回应该是一个对称的过程,本身对这一片内存数据的操作只发生在函数内部,脱离函数就应该脱离这块内存区域

    作者回复: 对的。

    2019-09-09
    3
    76
  • 小强
    表示看不懂闭包存在的意义,没有例子说明为啥要这玩意

    作者回复: 闭包就像样板代码和模版类型一样,其中先写了一部分代码,并确定了一部分功能。然后,当接到我们输入的参数值之后,它就可以确定另外一部分功能了。或许你可以把闭包看成填空题的题干。 闭包的存在意思有三: 1. 提高某个功能的灵活性,可以让使用方提供一部分功能的实现。但却可以控制这一部分的大小。 2. 提供动态替换某个功能的部分实现的可能性。这里的关键在于动态。 3. 使得代码动态替换的粒度缩小到函数级别。相比之下,模版类型的动态替换粒度是实例级别的。

    2020-06-16
    4
    28
  • RyuGou
    第一道题,显然是不会造成原来内容的修改呀 package main import "fmt" func main(){ complexArray := [3][]string{ []string{"d", "s", "f"}, []string{"a", "b", "c"}, []string{"e", "g", "h"}, } tryTest(complexArray) fmt.Println(complexArray) } func tryTest(array [3][]string){ array[1] = []string{"1", "2", "3"} return } 输出: [[d s f] [a b c] [e g h]]

    作者回复: 因为数组的每个元素也会被复制,你这样赋值只会改变数组副本中的一个元素值而已。你直接改其中的切片的元素值就会影响到外边了。

    2018-09-07
    2
    6
  • lixiaofeng
    总结 函数不仅能够封装代码,分割功能,解耦逻辑;还可以化身为普通的值,在函数之间传递,赋予变量,判断转换等 函数值可以成为能够被随意传播的独立逻辑组件(功能模块) 只要两个函数的参数列表和结果列表元素顺序及类型是一致的,我们就说他们是一样的函数(实现了相同函数类型的函数) 函数类型是引用类型 高姐函数: 把函数作为参数值传递 或者 函数的返回值是一个函数

    作者回复: 嗯,总结得挺好的。严格来说不能算是化身,因为函数本来就是值。另外我鸡蛋里挑个骨头,是“高阶函数”。:)

    2019-12-22
    3
  • 兔子高
    哈喽老师你好,我学go的反射是学的最乱的,反射可以拿到您好好讲一讲吗?然后最近看到了go新出的法案里说go 2.0会出泛型有考虑讲一下go的泛型吗?

    作者回复: Go的泛型几年后才会推出,现在没必要讲啊。

    2018-09-07
    3
  • 浩仔是程序员
    闭包函数跟Java中的lambda表达式是同个意思吗?

    作者回复: Java官方不是说Lambda就相当于闭包吗?

    2021-02-04
    2
  • 甜质粥
    老师,您把first class翻译成了“一等公民”,让人更加误解了,不如直接就称呼为first class,比如这个:https://golangbot.com/first-class-functions/

    作者回复: 在很多编程语言里都这么叫啊,这已经是一个约定俗成的中文称呼了。

    2020-11-10
    1
  • Yayu
    请教老师一个关于控制算法相关的内容。本文中提及的卫述语句,经常会在诸如参数检查的时候使用,如果我业务逻辑代码模块中有很多函数,每个函数的参数是个名为 xxrRequest 的结构体,那么我就要在每个函数里写大段大段的卫戍语句。有什么可行的办法可以优化这种写法吗?

    作者回复: 把检查代码封装到结构体的方法中,或者统一到某一个检查专用的程序实体中。

    2019-02-20
    2
    1
  • CrazyCodes
    函数真正拿到的参数值其实只是它们的副本,那么函数返回给调用方的结果值也会被复制吗? func main() { m := [3]int{} m[0] = 0 m[1] = 1 m[2] = 2 fmt.Printf("1-----%p \n", &m[0]) test(m) fmt.Println(m) } func test(a [3]int) [3]int { fmt.Printf("2-----%p \n", &a[0]) a[1] = 100 defer func() { fmt.Printf("3-----%p \n", &a[0]) }() return a } 输出结果是 1-----0x140000140f0 2-----0x14000014120 3-----0x14000014120 不知道这算不算返回值没有复制

    作者回复: 你的1号打印的是m的指针,2号和3号打印的是传到test函数里的参数值的指针,指针变了,这没问题。但是,你在main函数最后`fmt.Println(m)`依然是在打印m啊(而且你在“输出结果”下面也没贴上)。你应该在最后打印test函数返回的值的指针。 函数返回给调用方的结果值也会被复制。

    2023-11-18归属地:北京
  • Geek_4becf0
    老师请问 闭包所引用的自由变量,在确定后,也发生了拷贝吧,就是闭包外的函数调用完成,释放内存会把参数释放掉,但闭包引用了该参数,所以需要拷贝一份?

    作者回复: 自由变量肯定会被复制到堆上一份,因为它发生逃逸了(跨函数引用问题)。

    2021-12-20
收起评论
显示
设置
留言
52
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部