GO学习之 函数(Function)

GO系列

1、GO学习之Hello World
2、GO学习之入门语法
3、GO学习之切片操作
4、GO学习之 Map 操作
5、GO学习之 结构体 操作
6、GO学习之 通道(Channel)
7、GO学习之 多线程(goroutine)
8、GO学习之 函数(Function)
9、GO学习之 接口(Interface)

文章目录

  • GO系列
  • 前言
  • 一、什么是函数?
  • 二、函数声明
  • 三、函数调用
  • 四、匿名函数
  • 五、函数参数和返回值
  • 六、延迟执行函数
    • 6.1 defer 先进后出
    • 6.2 defer 闭包函数
  • 七、错误处理
    • 7.1 使用 error 作为返回参数
    • 7.2 使用 panic 触发异常
  • 八、总结

前言

按照公司目前的任务,go 学习是必经之路了,虽然行业卷,不过技多不压身,依旧努力!!!
之前在 结构体 篇中提到了方法,那方法其实和函数是差不多的,不过方法与函数的区别是,函数不属于任何类型,方法属于特定的类型,这句话在 结构体 篇中也说到了,但函数的使用上也有许多细节需要注意的,此篇则给予详解。

一、什么是函数?

  • 在 Go 语言中,函数(Function)是一种可执行的代码块(对特定功能进行提取,形成代码片段),用于执行特定的任务或操作。
  • 函数是 Go 语言中的基本组件,实现了模块化和复用的机制,让代码更加结构化和可维护。

下面列举了我能查到和想到的特点(不仅限于这些):

Go语言中函数的特点:

  • 无需声明原型
  • 支持不定参数
  • 支持多返回值
  • 支持命名返回参数
  • 支持匿名函数和闭包
  • 函数也是一种类型,可以把一个函数赋值给一个变量
  • 不能嵌套定义函数
  • 不能像JAVA中那样重载函数(overload)

二、函数声明

  • 函数的声明使用 关键字 func
  • 基本语法:func 函数名(参数列表) 返回值列表 { 函数体 }
  • 函数名命名规范:1、最好驼峰命名,见名知意,比如:addNum(a,b int){};2、首字母不能是数字;3、首字母大写表示可以被本包和其他包文件使用,类似 public,比如:AddNum(a,b int){},首字母小写则类似 private,比如:addNum(a,b int){}
  • 参数列表用逗号分隔,每个参数有参数名和类型组成,比如:func list(pageNo int, pageSize int) (int, error) { }
  • 如果多个参数是同一类型,则前面参数类型可以省略,比如:func list(pageNo, pageSize int) (int, error) { }
  • 使用 ... 语法可以为函数定义可变参数,运行函数接受不定数量参数,比如:``
  • 如果是无放回值则可省略,比如:func save(id int, name string) { }
  • 如果是一个返回值则不需要(或有都可)小括号,比如:func save(id int, name string) int { }
  • 如果是多个返回值则用小括号包起来,并且和 return 语句一 一对应,比如:func save(id int, name string) (int, string) { ...return 1,'success' }
  • 上面提到支持命名返回函数,比如:func divideAndRemainder(a, b int) (quotient, remainder int) { }
  • 使用关键词 func 定义函数,大括号不能另起一行

上代码:

三、函数调用

  • 在接受函数返回值时,如果多个返回值则一一对应接受,比如:count, result := save(1, "张三")
  • 如果只需要接受其中一个返回值,另一个不需要接受,则可以用下划线 _ 忽略,比如:count, _ := save(1, "张三")
  • 如果 main 包中想调用其他包中的函数,那其他包中的函数则需要定义为包外可访问的,函数名首字母大写,比如定义一个 func1 的包:func SumNum(a, b int) int { },此时需要注意此包名,必须使用包名调用,比如:s := func1.SumNum(1, 2)

下面例子,举例了 Go 语言常用到的函数定义的例子,可供参考。
例子是哥我一个一个敲的,测试通过,并且附上了运行结果,不过只靠眼睛看还是有点繁杂,还是自己在专门敲一遍为好,不过对于大佬无所谓了,对于像我这种小白,则只能按部就班敲一遍,解决各种报错,方能成长!

包路径是这样的:
函数包路径

package func1import "fmt"// 定义无参数无返回值函数
func test() {fmt.Println("call test函数")
}// 定义有参数无返回值函数,此函数私有的,只有内部可调
func addNum(a, b int) {c := a + bfmt.Printf("a + b = c %+v\n", c)
}// 定义有参数有一个返回值函数, 次函数共有的,内部、外部包均可调
func SumNum(a, b int) int {c := a + breturn c
}// 定义可变参数函数
func ParamsFunc(params ...string) {for index, item := range params {fmt.Printf("可变参数为 %d:%+v\n", index, item)}
}// 定义有参数有多个返回值函数
func List(pageNo, pageSize int) (int, []string) {fmt.Printf("查询操作...%d, %d", pageNo, pageSize)result := []string{"特斯拉", "广汽", "丰田", "宝马", "奥迪"}return 5, result
}// 定义命名返回函数
func divideAndRemainder(a, b int) (quotient, remainder int) {quotient = a / bremainder = a % breturn // 省略了 return 语句,并且直接返回了命名的返回值变量
}

下面示例是对上面定义的函数进行调用。
主要,import 其他包的路径是 gotest.com/test/src/functionTest/func1

package mainimport ("fmt""gotest.com/test/src/functionTest/func1"
)func main() {// 调用本包中的 save 函数,接受两个返回值count1, result := save(1, "张三")fmt.Printf("接受 save 函数的两个返回值 count1:%+v, result: %v\n", count1, result)// 调用本包中的 save 函数,接受一个返回值count, _ := save(1, "张三")fmt.Printf("接受 save 函数的一个返回值 count: %+v\n", count)// 调用无返回值函数list2(1, 10)// 调用 func1 包中的 SumNum 函数s := func1.SumNum(1, 2)fmt.Printf("调用 func1 包中的 SunNum 函数结果:%+v\n", s)// 调用可变参数函数func1.ParamsFunc("特斯拉", "广汽", "丰田", "宝马", "奥迪")// 调用 func1 包中的 List 函数totalCount, carBrands := func1.List(1, 10)fmt.Printf("调用 func1 包中的 List 函数,查询结果:%+v 条,数据:%v\n", totalCount, carBrands)
}// 定义有参数有多个返回值函数
func save(id int, name string) (int, string) {fmt.Printf("保存%+v,%v\n", id, name)return 1, "success"
}// 定义有多个参数无返回值函数
func list2(pageNo, pageSize int) {fmt.Println("list 接口")
}

运行结果:

PS D:\workspaceGo\src\functionTest\main> go run funcTest.go
保存1,张三
接受 save 函数的两个返回值 count1:1, result: success
保存1,张三
接受 save 函数的一个返回值 count: 1
list 接口
调用 func1 包中的 SunNum 函数结果:3
可变参数为 0:特斯拉
可变参数为 1:广汽
可变参数为 2:丰田
可变参数为 3:宝马
可变参数为 4:奥迪
查询操作...1, 10调用 func1 包中的 List 函数,查询结果:5 条,数据:[特斯拉 广汽 丰田 宝马 奥迪]

四、匿名函数

  • 在 Go 语言中,支持匿名函数,也就是没有函数名的函数
  • 可以将匿名函数赋值给变量
  • 也可以将匿名函数直接调用,则是闭包
package mainimport "fmt"func main() {// 定义匿名函数直接调用func() {fmt.Println("匿名函数调用!")}()// 定义匿名函数赋值给变量 hellohello := func() {fmt.Println("Hello 函数调用!")}// 调用匿名函数hello()// 定义有参数的匿名函数sum := func(a, b int) int {return a + b}fmt.Printf("加法计算:%+v\n", sum(1, 2))
}

运行结果:

PS D:\workspaceGo\src\functionTest\main> go run .\anonymousFunc.go
匿名函数调用!
Hello 函数调用!
加法计算:3

下面是一个稍微复杂点的例子:

下面例子中,我们把 函数 作为一个成员存放在了 数组 fns、结构体 s、管道 fc 中,并且获取到函数进行调用。

package mainfunc main() {// 定义数据,元素类型是一个函数fns := [](func(a int) int){func(a int) int { return a + 1 }, func(a int) int { return a + 2 }}// 获取数组中的第一个函数调用,传参 10for _, fn := range fns {println(fn(10))}// 定义一个结构体,成员是一个 函数,调用结构体的 函数成员s := struct {fn func() string}{fn: func() string { return "Hello World!" },}println(s.fn())// 定义一个管道,发送一个函数,再接受到函数进行调用fc := make(chan func() string, 2)fc <- func() string { return "fc: Hello World!" }println((<-fc)())
}

运行结果:

PS D:\workspaceGo\src\functionTest\main> go run .\anomymousFunc2.go
11
12
Hello World!
fc: Hello World!

五、函数参数和返回值

  • 在 Go 语言中,函数可以作为参数传递,也可以作为另一个函数的返回值

下面是一个比较简单的示例,例子中接受一个 函数类型参数 fc,返回一个 匿名函数。

package func1import "fmt"func CallFunc(fc func()) func() {fmt.Println("接受到函数 fc, 开始回调!")// 返回一个匿名函数return func() {fc()fmt.Println("call back...")}
}

调用代码:

package mainimport ("fmt""gotest.com/test/src/functionTest/func1"
)func main() {
fc := func() {fmt.Println("我是参数 fc 执行!")}fr := func1.CallFunc(fc)fr()
}

运行结果:

PS D:\workspaceGo\src\functionTest\main> go run .\funcTest.go
接受到函数 fc, 开始回调!
我是参数 fc 执行!
call back...

下面是 ChatGPT给出的经典案例,方便更加深入理解函数如何作为参数和返回值在实际场景中的应用,示例我已测试,ojbk。

  • 函数作为参数使用:
package mainimport "fmt"// 函数类型作为参数
type MathFunc func(int, int) int// 加法函数
func add(a, b int) int {return a + b
}// 减法函数
func subtract(a, b int) int {return a - b
}// 计算函数,接收一个函数类型参数,并执行该函数
func calculate(a, b int, op MathFunc) int {return op(a, b)
}func main() {// 调用 calculate 函数,传入 add 函数作为参数result := calculate(10, 5, add)fmt.Println("加法结果:", result)// 调用 calculate 函数,传入 subtract 函数作为参数result = calculate(10, 5, subtract)fmt.Println("减法结果:", result)
}

运行结果:

PS D:\workspaceGo\src\functionTest\main> go run .\gptFunc.go
加法结果: 15
减法结果: 5
  • 函数作为返回值使用:
package mainimport "fmt"// 返回一个加法函数
func getAddFunc() func(int, int) int {// 返回一个匿名函数,来实现计算return func(a, b int) int {return a + b}
}// 返回一个减法函数
func getSubtractFunc() func(int, int) int {return func(a, b int) int {return a - b}
}func main() {// 获取加法函数并调用addFunc := getAddFunc()result := addFunc(10, 5)fmt.Println("加法结果:", result)// 获取减法函数并调用subtractFunc := getSubtractFunc()result = subtractFunc(10, 5)fmt.Println("减法结果:", result)
}

执行结果:

PS D:\workspaceGo\src\functionTest\main> go run .\gptFunc2.go
加法结果: 15
减法结果: 5

六、延迟执行函数

defer 的特性:

  • 关键字 defer 用户注册延迟调用,比如:defer println(i)
  • 注册的延迟调用直到 return 前才会执行,所以很适合做关闭、资源回收等操作
  • defer 语句,是按照先进后出的方式执行
  • defer 语句中的变量,在 defer 声明是就决定了

defer 适用场景:

  • 关闭流操作
  • 资源释放
  • 数据库连接释放
  • 等…

6.1 defer 先进后出

package mainfunc main() {arr := [5]int{1, 2, 3, 4, 5}for i := range arr {defer println(i)}
}

运行结果:

PS D:\workspaceGo\src\functionTest\main> go run .\deferTest.go
4
3
2
1
0

从结果可以看出,先循环到的 defer 等到后面才执行。

6.2 defer 闭包函数

package mainfunc main() {arr := [5]int{1, 2, 3, 4, 5}for i := range arr {defer func() {println(i)}()}
}

运行结果:

PS D:\workspaceGo\src\functionTest\main> go run .\deferTest.go
4
4
4
4
4

为啥全部变为了 4,由于循环体内的是闭包函数,声明完之后立马执行,但是在函数声明的时候 i 变量已经变为了 4,所以 4 个匿名函数都输出了 4。

由于 defer 看起来情况比较多,所以请移步到这里!

七、错误处理

  • Go 语言中的多数函数会返回一个错误 error 作为额外的返回值,用户表示函数是否执行成功
  • 调用函数通常需要检查错误,以便根据情况进行处理
  • 对于无返回值的函数,可以使用错误类型来表明函数是否执行成功,或者用 panic 来触发异常

7.1 使用 error 作为返回参数

在示例中,我们使用错误类型 error 来表示函数是否执行成功,如果函数出现错误,则返回 error。

package mainimport ("errors""fmt"
)func main() {err := divide(10, 0)if err != nil {fmt.Println("发生异常:", err)}
}func divide(a, b int) error {if b == 0 {return errors.New("参数不能为 0")}return nil
}

运行结果:

PS D:\workspaceGo\src\functionTest\main> go run .\errorFunc.go
发生异常: 参数不能为 0

7.2 使用 panic 触发异常

示例中,用 panic 来触发异常表示函数执行状态,当函数出现错误时,直接触发异常,并中断程序执行。
**注意:**我们用 recover() 函数来捕获并处理异常,避免程序崩溃。recover 函数只在 defer 块中才有效,所以在 main() 函数中使用 defer 来捕获异常。

package mainfunc main() {defer func() {if r := recover(); r != nil {println("发生异常:", r)}}()divide2(10, 0)
}func divide2(a, b int) {if b == 0 {panic("参数不能为 0")}
}

运行结果:

PS D:\workspaceGo\src\functionTest\main> go run .\panicFunc.go
发生异常: (0xff1920,0x1011638)

八、总结

函数是 Go 语言中非常重要的组成部分,它们提供了模块化、代码复用和抽象的能力。通过函数,我们可以将复杂的逻辑划分为多个小模块,使得代码更加清晰、可读性更强,并且更易于维护和扩展。函数的灵活性和多样性使得 Go 语言可以用于解决各种不同的问题和场景。

现阶段还是对 Go 语言的学习阶段,想必有一些地方考虑的不全面,本文示例全部是亲自手敲代码并且执行通过。
如有问题,还请指教。
评论去告诉我哦!!!一起学习一起进步!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/24619.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

神经影像脑网络图、brain map可视化汇总

神经影像脑网络图、brain map可视化汇总 介绍使用R语言进行脑成像可视化的例子代码使用Python制作的脑成像可视化示例代码介绍 神经影像可视化构成了科学结果解释和交流的核心,也是数据质量控制的基石。 通常,这些图像和图形是通过手动更改图形用户界面 (GUI) 上的设置来生成…

机器学习04-数据理解之数据可视化-(基于Pima数据集)

什么是数据可视化? 数据可视化是指通过图表、图形、地图等视觉元素将数据呈现出来的过程。它是将抽象的、复杂的数据转化为直观、易于理解的视觉表达的一种方法。数据可视化的目的是帮助人们更好地理解数据&#xff0c;从中发现模式、趋势、关联和异常&#xff0c;从而作出更明…

【安全测试】Web应用安全之XSS跨站脚本攻击漏洞

目录 前言 XSS概念及分类 反射型XSS(非持久性XSS) 存储型XSS(持久型XSS) 如何测试XSS漏洞 方法一&#xff1a; 方法二&#xff1a; XSS漏洞修复 原则&#xff1a;不相信客户输入的数据 处理建议 资料获取方法 前言 以前都只是在各类文档中见到过XSS&#xff0c;也进…

三星进军机器人市场?特斯拉首款人形机器人“擎天柱”明年上市

根据报道&#xff0c;三星电子正在积极研究进军机器人市场的战略。此战略由三星电子的Device eXperience&#xff08;DX&#xff09;部门的专业企划小组制定。据可靠消息透露&#xff0c;该企划小组已着手制定相关计划&#xff0c;以推动公司在机器人市场的发展。 根据外媒报道…

ClickHouse(九):Clickhouse表引擎 - Log系列表引擎

进入正文前&#xff0c;感谢宝子们订阅专题、点赞、评论、收藏&#xff01;关注IT贫道&#xff0c;获取高质量博客内容&#xff01; &#x1f3e1;个人主页&#xff1a;含各种IT体系技术&#xff0c;IT贫道_Apache Doris,Kerberos安全认证,大数据OLAP体系技术栈-CSDN博客 &…

java反射机制原理、获取Class方式和其应用场景

1、反射是什么&#xff1a; 反射是一种动态地获取和操作类信息的行为。类信息包括类的属性、方法、构造函数等。 类信息在Java中通常存储在.class文件中。当我们编写Java代码并进行编译时&#xff0c;编译器&#xff08;javac&#xff09;将源代码转换为字节码&#xff0c;并将…

Mapper层公共字段自动填充

公共字段自动填充 问题分析 我们在进行一些新增修改操作时&#xff0c;我们需要设置创建时间、创建人、修改时间、修改人等字段。 这些字段属于公共字段&#xff0c;也就是也就是在我们的系统中很多表中都会有这些字段&#xff0c;如下&#xff1a; 序号字段名含义数据类型1c…

【分布式流控组件 Sentinel 快速入门】——图文详解操作流程

&#x1f4a7; 分布式流控组件 S e n t i n e l 快速入门 \color{#FF1493}{分布式流控组件 Sentinel 快速入门} 分布式流控组件Sentinel快速入门&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞见云的博客&#…

智慧工地云平台源码,基于微服务+Java+Spring Cloud +UniApp +MySql开发

智慧工地可视化系统利用物联网、人工智能、云计算、大数据、移动互联网等新一代信息技术&#xff0c;通过工地中台、三维建模服务、视频AI分析服务等技术支撑&#xff0c;实现智慧工地高精度动态仿真&#xff0c;趋势分析、预测、模拟&#xff0c;建设智能化、标准化的智慧工地…

华为云交付

文章目录 一、华为云-公有云架构华为公有云的主要服务1.华为云服务—计算类2.华为云服务——存储类3.华为云服务—网络类4.华为云服务—管理和监督类5.华为云数据库 二、待续 一、华为云-公有云架构 华为公有云的主要服务 ECS&#xff1a;弹性云服务器&#xff08; Elastic Cl…

穷举深搜暴搜回溯剪枝(3)

一)字母大小写全排列 784. 字母大小写全排列 - 力扣&#xff08;LeetCode&#xff09; 1)从每一个字符开始进行枚举&#xff0c;如果枚举的是一个数字字符&#xff0c;直接忽视 如果是字母的话&#xff0c;进行选择是变还是不变 2)当进行遍历到叶子结点的时候&#xff0c;直接将…

(三)Node.js - 模块化

1. Node.js中的模块化 Node.js中根据模块来源不同&#xff0c;将模块分为了3大类&#xff0c;分别是&#xff1a; 内置模块&#xff1a;内置模块由Node.js官方提供的&#xff0c;例如fs、path、http等自定义模块&#xff1a;用户创建的每个.js文件&#xff0c;都是自定义模块…

jmeter 5.1彻底解决中文上传乱码

1.修改源码,然后重新打jar包,就是所有上传文件名重新获取文件名 参考链接:多种Jmeter中文乱码问题处理方法 - 51Testing软件测试网 2.修改Advanced,必须选java

RaabitMQ(三) - RabbitMQ队列类型、死信消息与死信队列、懒队列、集群模式、MQ常见消息问题

RabbitMQ队列类型 Classic经典队列 这是RabbitMQ最为经典的队列类型。在单机环境中&#xff0c;拥有比较高的消息可靠性。 经典队列可以选择是否持久化(Durability)以及是否自动删除(Auto delete)两个属性。 Durability有两个选项&#xff0c;Durable和Transient。 Durable表…

【ARM Coresight 系列文章 2.5 - Coresight 寄存器:PIDR0-PIDR7,CIDR0-CIDR3 介绍】

文章目录 1.1 JEDEC 与 JEP1061.2 PIDR0-PIDR7(peripheral identification registers)1.2 CIDR0-CIDR3(Component Identification Registers) 1.1 JEDEC 与 JEP106 JEDEC和JEP106都是来自美国电子工业联合会&#xff08;JEDEC&#xff0c;Joint Electron Device Engineering C…

深度学习(34)—— StarGAN(1)

深度学习&#xff08;34&#xff09;—— StarGAN&#xff08;1&#xff09; 文章目录 深度学习&#xff08;34&#xff09;—— StarGAN&#xff08;1&#xff09;1. 背景2. 基本思路3. 整体流程4. StarGAN v2(1) 网络结构(2) mapping network(3) style encoder(4)Loss 和之前…

4个顶级的支持消费级硬件的NeRF软件平台

似乎每天都有大量的创新发布&#xff0c;人们很容易感到不知所措。因此&#xff0c;让我们放慢脚步&#xff0c;看看4个主流的支持消费级硬件的NeRF 平台。 推荐&#xff1a;用 NSDT设计器 快速搭建可编程3D场景。 1、Instant-NGP&#xff08;Instant-NeRF&#xff09; 2022 年…

计算机网络-三种交换方式

计算机网络-三种交换方式 电路交换(Circuit Switching) 电话交换机接通电话线的方式称为电路交换从通信资源分配的角度来看&#xff0c;交换(Switching)就是按照某种方式动态的分配传输线路的资源 电话交换机 为了解决电话之间通信两两之间连线过多&#xff0c;所以产生了电话…

认识 spring AOP (面向切面编程) - springboot

前言 本篇介绍什么是spring AOP, AOP的优点&#xff0c;使用场景&#xff0c;spring AOP的组成&#xff0c;简单实现AOP 并 了解它的通知&#xff1b;如有错误&#xff0c;请在评论区指正&#xff0c;让我们一起交流&#xff0c;共同进步&#xff01; 文章目录 前言1. 什么是s…

快速制作美容行业预约小程序

随着科技的不断进步&#xff0c;移动互联网的快速发展&#xff0c;小程序成为了很多行业迅速发展的利器。对于美容行业来说&#xff0c;一款美容预约小程序不仅可以方便用户进行预约&#xff0c;还可以提升美容店铺的服务质量和管理效率。下面&#xff0c;我们来介绍一下如何快…