Golang 【basic_leaming】4 函数

矫情吗;* 2023-10-07 22:23 51阅读 0赞

阅读目录

  • Go 语言函数
    • Go 语言函数章节目录
  • Go 语言函数定义_声明_调用(超详细)
    • 一、定义一个普通函数
      • 1.1 函数名
      • 1.2 参数列表
      • 1.3 返回参数列表
      • 1.4 函数体
    • 二、参数列表简写
    • 三、函数返回值
      • 3.1 同一类型的返回值
      • 3.2 带有变量名的返回值
    • 四、调用函数
  • Go 语言函数变量(将函数保存到变量中)
  • Go 语言匿名函数_回调函数
    • 一、定义一个匿名函数
      • 1.1 定义后立即调用匿名函数
      • 1.2 将匿名函数赋值给变量
    • 二、匿名函数用作回调函数
  • Go 语言函数实现接口(将函数当做接口来调用)
    • 如何定义一个接口
      • 1、结构体实现接口
      • 2、函数实现接口
  • Go 语言闭包函数_作用_应用场景讲解
    • 一、定义闭包
    • 二、闭包的记忆效应
    • 三、通过闭包实现一个生成器
  • 参考文献

Go 语言函数

在编程中,函数是指一段可以直接被另一段程序或代码引用的、可重复使用的、用来实现单一或相关联功能的代码段。目的是为了提高应用的模块性和代码的重复利用率。

相比较其他语言,Go 语言 在设计上对函数进行了优化和改进,使其使用起来更加便利。

Go 语言中函数有着以下特性:

  • 函数本身可以作为值进行传递;
  • 支持普通函数、匿名函数和闭包(closure);
  • 函数可以满足接口;

Go 语言函数章节目录

1、Go 语言函数定义
2、函数变量
3、匿名函数
4、函数接口
5、闭包(closure)
6、可变参数
7、延迟执行语句(defer)
8、处理运行时发生的错误
9、宕机(panic)
10、宕机恢复(recover)

Go 语言函数定义_声明_调用(超详细)

在 Go 语言 中,定义一个函数需要声明参数和函数名等。

一、定义一个普通函数

Go 语言中,定义函数需要以 func 标识开头,后面紧接着函数名、参数列表、返回参数列表以及函数体,格式如下:

  1. func 函数名(参数列表) (返回参数列表) {
  2. 函数体
  3. }

1.1 函数名

函数名的命名有以下约束:

  • 函数名由字母、数字、下划线组成;
  • 函数名不能以数字开头;
  • 同一包内,函数名不能重名。

1.2 参数列表

参数列表中声明的每一个参数由变量名和参数类型组成。

示例代码如下:

  1. func foo(a int, b string)

注意,参数列表中的变量以局部变量的方式存在。

1.3 返回参数列表

可以是返回一个值类型,也可以是一个组合,即返回多个参数。

另外,当函数中声明了有返回值时,函数体中必须 使用 return 语句提供返回值。

1.4 函数体

函数体表示能够被重复调用的代码片段。

二、参数列表简写

当参数列表中定义了多个参数,且参数类型相同时,代码如下:

  1. func foo(a int, b int) int {
  2. }

上面代码中,变量 a、b 的类型均为整型 int,可以采用以下简写方式:

  1. func foo(a , b int) int {
  2. }

统一定义一个 int 类型即可。

三、函数返回值

在 Go 语言中,返回值支持返回多个,常用场景下,多返回值的最后一个参数会返回函数执行中可能发生的错误,示例代码如下:

  1. conn, err := connectToDatabase()

上面这段代码中,函数 connectToDatabase() 用来获取数据库连接,conn 表示数据库连接,err 用来接收获取过程中可能发生的错误。

3.1 同一类型的返回值

如果函数返回值是统一类型,则用括号将多个返回值括起来,以逗号隔开,示例代码如下:

  1. package main
  2. import "fmt"
  3. func say(name, content string) (string, string) {
  4. return name, content
  5. }
  6. func main() {
  7. name, content := say("知其黑", "受其白")
  8. fmt.Println(name, content)
  9. }
  10. 知其黑 受其白

注意:使用 return 语句返回多个值时,值的顺序需要与函数声明的返回值一致。

3.2 带有变量名的返回值

Go 语言支持对返回值进行命名,命名后代码的可读性更佳。

命名的返回值默认值为该类型的默认值,例如,若返回值为整型 int,则默认值为 0;若为字符串 string,则默认值为空字符串;布尔为 false; 指针为 nil 等。

下面是代码示例:

  1. func initValue() (a int, b int) {
  2. a = 1
  3. b = 2
  4. return
  5. }

上面这段代码中,函数声明中将返回值变量命名为 a、b,然后在函数体中分别对 a 、 b 进行赋值, 然后使用 return 语句进行返回。

注意: 当函数使用命名进行返回时,可以在 return 语句中不填返回值列表,当然填写也是可以的,上面的代码与下面的代码执行效果相同:

  1. func initValue() (a int, b int) {
  2. a = 1
  3. return a, 2
  4. }

四、调用函数

函数在定义以后,要如何调用呢?

函数的调用格式如下:

  1. 返回值列表 = 函数名(参数列表)
  • 函数名:需要被调用的函数名;
  • 参数列表: 传入的参数列表,以逗号隔开;
  • 返回值列表:多个返回值以逗号隔开;

下面代码演示如何调用函数:

  1. package main
  2. import "fmt"
  3. func initValue() (a int, b int) {
  4. a = 1
  5. return a, 2
  6. }
  7. func main() {
  8. // 调用 initValue 函数
  9. a, b := initValue()
  10. fmt.Println(a, b)
  11. }
  12. building...
  13. running...
  14. 1 2

PS: 函数内定义的局部变量只能作用在函数体中,函数执行结束后,这些变量都会被释放掉,无法再次访问。

Go 语言函数变量(将函数保存到变量中)

在 Go 语言中,函数也是一种类型,同样可以和其他类型(如 int 、float 、string 等)一样被保存到变量中。

示例代码如下:

  1. package main
  2. import "fmt"
  3. func sayHello() {
  4. fmt.Println("hello , 知其黑,受其 ...")
  5. }
  6. func main() {
  7. // 声明一个函数类型的变量,注意类型为 func()
  8. var f func()
  9. // 将函数名赋值给变量 f
  10. f = sayHello
  11. // 通过变量 f 直接调用函数
  12. f()
  13. }
  14. running...
  15. hello , 知其黑,受其 ...

Go 语言匿名函数_回调函数

在 Go 语言 中,匿名函数是没有名字的函数,只有函数体。

匿名函数经常以变量的形式被传递。

大部分场景下,匿名函数经常被使用于实现函数回调、闭包等。

一、定义一个匿名函数

匿名函数的定义格式如下:

  1. func(参数列表) (返回参数列表) {
  2. 函数体
  3. }

从上面可以看出来,匿名函数的定义格式其实就是没有函数名的普通函数定义格式。

1.1 定义后立即调用匿名函数

可以定义完匿名函数后,立即调用它,例如:

  1. package main
  2. import "fmt"
  3. func main() {
  4. func(name string) {
  5. fmt.Printf("hello, %s", name)
  6. }("知其黑,受其白")
  7. }
  8. running...
  9. hello, 知其黑,受其白

上面的代码,我们注意到函数后面立刻跟上传入参数,表示对匿名函数的调用。

1.2 将匿名函数赋值给变量

匿名函数可以赋值给变量,示例如下:

  1. package main
  2. import "fmt"
  3. func main() {
  4. // 将匿名函数赋值给变量 function
  5. function := func(name string) {
  6. fmt.Printf("hello, %s", name)
  7. }
  8. // 使用变量 function 调用函数
  9. function("知其黑,受其白")
  10. }
  11. running...
  12. hello, 知其黑,受其白

二、匿名函数用作回调函数

下面演示一段匿名函数充当回调函数的示例:

  1. package main
  2. import "fmt"
  3. func visit(list []string, f func(string)) {
  4. // 遍历切片中的元素,并将值作为参数传给回调函数
  5. for _, value := range list {
  6. // 调用回调函数
  7. f(value)
  8. }
  9. }
  10. func main() {
  11. // 定义一个切片
  12. list := []string{
  13. "知其黑,受其白", "wgchen.blog.csdn.net"}
  14. // 使用匿名函数打印切片内容
  15. visit(list, func(value string) {
  16. fmt.Println(value + " func \n")
  17. })
  18. }
  19. running...
  20. 知其黑,受其白 func
  21. wgchen.blog.csdn.net func

Go 语言函数实现接口(将函数当做接口来调用)

在 Go 语言 中,其他基本类型能够实现接口,函数同样可以实现接口。

如何定义一个接口

下面我们定义一个名为狗 Dog 的接口:

  1. // 定义一个名为 Dog 的接口
  2. type Dog interface {
  3. // 需要实现一个姓名 Name() 方法
  4. Say(interface{
  5. })
  6. }

上面这个接口需要实现 Say() 方法,调用时需传入一个 interface{} 类型的变量。这是个啥类型呢?

这种类型表示你可以传入任意类型的值。

接下来,我们将实现这个接口。

实现接口有两种方式:

  • 结构体实现接口;
  • 函数体实现接口;

1、结构体实现接口

下面将定义一个哈士奇的结构体,并实现 Dog 接口,代码如下:

  1. // 定义一个结构体类型:哈士奇
  2. type Husky struct {
  3. }
  4. // 实现接口 Dog 定义的 Say 方法,方法中打印一句话
  5. func (s *Husky) Say(p interface{
  6. }) {
  7. fmt.Println("哈士奇说: ", p)
  8. }

接下来,将定义的 Husky 类型实例化并调用接口方法,代码如下:

  1. // 定义接口
  2. var dog Dog
  3. // 实例化哈士奇结构体
  4. husky := new(Husky)
  5. // 将实例化的结构体赋给接口
  6. dog = husky
  7. // 使用接口调用实例化结构体的方法 Say()
  8. dog.Say("知其黑,受其白")

完整代码

  1. package main
  2. import "fmt"
  3. // 定义一个名为 Dog 的接口
  4. type Dog interface {
  5. // 需要实现一个姓名 Name() 方法
  6. Say(interface{
  7. })
  8. }
  9. // 定义一个结构体类型:哈士奇
  10. type Husky struct{
  11. }
  12. // 实现接口 Dog 定义的 Say 方法,方法中打印一句话
  13. func (s *Husky) Say(p interface{
  14. }) {
  15. fmt.Println("哈士奇说: ", p)
  16. }
  17. func main() {
  18. // 定义接口
  19. var dog Dog
  20. // 实例化哈士奇结构体
  21. husky := new(Husky)
  22. // 将实例化的结构体赋给接口
  23. dog = husky
  24. // 使用接口调用实例化结构体的方法 Say()
  25. dog.Say("知其黑,受其白")
  26. }
  27. running...
  28. 哈士奇说: 知其黑,受其白

2、函数实现接口

函数要想实现接口,需要先将自己定义为类型,然后实现接口方法,同时需要再方法中调用函数本体,示例代码如下:

  1. // 将函数定义为类型
  2. type FuncHusky func(interface{
  3. })
  4. // 实现接口 Dog 的 Say 方法
  5. func (f FuncHusky) Say(p interface{
  6. }) {
  7. // 调用 f() 函数本体
  8. f(p)
  9. }

FuncHusky 无需实例化,只需要将函数转换为 FuncHusky 类型即可,示例代码如下:

  1. // 定义接口
  2. var dog Dog
  3. // 将匿名函数转换为 FuncHusky 类型,此时 FuncHusky 类型实现了 Say 方法,赋值给接口是成功的
  4. dog = FuncHusky(func(i interface{
  5. }) {
  6. fmt.Println("哈士奇说: ", i)
  7. })
  8. // 使用接口调用 Call 方法
  9. dog.Say("知其黑,受其白")

完整代码

  1. package main
  2. import "fmt"
  3. // 定义一个名为 Dog 的接口
  4. type Dog interface {
  5. // 需要实现一个姓名 Name() 方法
  6. Say(interface{
  7. })
  8. }
  9. // 将函数定义为类型
  10. type FuncHusky func(interface{
  11. })
  12. // 实现接口 Dog 的 Say 方法
  13. func (f FuncHusky) Say(p interface{
  14. }) {
  15. // 调用 f() 函数本体
  16. f(p)
  17. }
  18. func main() {
  19. // 定义接口
  20. var dog Dog
  21. // 将匿名函数转换为 FuncHusky 类型,此时 FuncHusky 类型实现了 Say 方法,赋值给接口是成功的
  22. dog = FuncHusky(func(i interface{
  23. }) {
  24. fmt.Println("哈士奇说: ", i)
  25. })
  26. // 使用接口调用 Call 方法
  27. dog.Say("知其黑,受其白")
  28. }
  29. running...
  30. 哈士奇说: 知其黑,受其白

Go 语言闭包函数_作用_应用场景讲解

在 Go 语言 中,闭包是个啥概念呢?
一句话来讲:引用了外部变量的匿名函数 。

公式如下:

  1. 函数 + 引用外部变量 = 闭包

一、定义闭包

下面代码演示了如何在 Go 语言中定义闭包:

  1. package main
  2. import "fmt"
  3. func main() {
  4. // 定义一个字符串
  5. str := "wgchen.blog.csdn.net"
  6. // 创建一个匿名函数
  7. function := func() {
  8. // 给字符串 str 赋予一个新的值,注意: 匿名函数引用了外部变量,这种情况形成了闭包
  9. str = "知其黑,受其白"
  10. // 打印
  11. fmt.Println(str)
  12. }
  13. // 执行闭包
  14. function()
  15. }
  16. running...
  17. 知其黑,受其白

二、闭包的记忆效应

闭包在引用外部变量后具有记忆效应,闭包中可以修改变量,变量会随着闭包的生命周期一直存在,此时,闭包如同变量一样拥有了记忆效应。

示例代码如下:

  1. package main
  2. import "fmt"
  3. // 定义一个累加函数,返回类型为 func() int,
  4. // 入参为整数类型,每次调用函数对该值进行累加
  5. func Add(value int) func() int {
  6. // 返回一个闭包
  7. return func() int {
  8. // 累加
  9. value++
  10. // 返回累加值
  11. return value
  12. }
  13. }
  14. func main() {
  15. // 创建一个累加器,初始值为 1
  16. accumulator := Add(1)
  17. // 累加1并打印
  18. fmt.Println(accumulator())
  19. // 再来一次
  20. fmt.Println(accumulator())
  21. // 创建另一个累加器,初始值为 10
  22. accumulator2 := Add(10)
  23. // 累加1并打印
  24. fmt.Println(accumulator2())
  25. }
  26. running...
  27. 2
  28. 3
  29. 11

通过输出可以看出闭包的记忆效应,每次调用 accumulator() 后,都会对 value 进行累加操作。

三、通过闭包实现一个生成器

可以通过闭包的记忆效应来实现设计模式中工厂模式的生成器。

下面的代码示例展示了创建游戏玩家生成器的过程。

  1. package main
  2. import "fmt"
  3. /*
  4. 定义一个玩家生成器,
  5. 它的返回类型为 func() (string, int),输入名称,
  6. 返回新的玩家数据
  7. */
  8. func genPlayer(name string) func() (string, int) {
  9. // 定义玩家血量
  10. hp := 1000
  11. // 返回闭包
  12. return func() (string, int) {
  13. // 引用了外部的 hp 变量, 形成了闭包
  14. return name, hp
  15. }
  16. }
  17. func main() {
  18. // 创建一个玩家生成器
  19. generator := genPlayer("知其黑,受其白")
  20. // 返回新创建玩家的姓名, 血量
  21. name, hp := generator()
  22. // 打印
  23. fmt.Println(name, hp)
  24. }
  25. running...
  26. 知其黑,受其白 1000

参考文献

Golang 【basic_leaming】函数详解

在这里插入图片描述

发表评论

表情:
评论列表 (有 0 条评论,51人围观)

还没有评论,来说两句吧...

相关阅读

    相关 golang:构造函数

    Go语言的类型或结构体没有构造函数的功能,但是我们可以使用结构体初始化的过程来模拟实现构造函数。 其他编程语言构造函数的一些常见功能及特性如下: 每个类可以添加构造函

    相关 golang 函数 05

     func进行函数声明 函数的左花括号也不能另起一行 不允许函数内嵌定义 支持多返回值、支持命名返回值 函数只能判断是否为nil 参数视为局

    相关 golang中init函数

    init函数有一下几点特性: init函数在main执行之前,自动被调用执行的,不能显示调用 每个包的init函数在包被引用时,自动被调用 每个包可以有多个

    相关 golang:匿名函数

    匿名函数——没有函数名字的函数 Go语言支持匿名函数,即在需要使用函数时再定义函数,匿名函数没有函数名只有函数体,函数可以作为一种类型被赋值给函数类型的变量

    相关 golang函数

    函数是组织好的、可重复使用的、用来实现单一或相关联功能的代码段,其可以提高应用的模块性和代码的重复利用率。 Go 语言支持普通函数、匿名函数和闭包,从设计上对函数进行了优化和