当前位置: 首页 > news >正文

GO—函数

Go 语言支持普通函数、匿名函数和闭包,从设计上对函数进行了优化和改进,让函数使用起来更加方便。

Go 语言的函数属于“一等公民”(first-class),也就是说:

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

函数定义:

func function_name( [parameter list] ) [return_types] {函数体
}
  • func:函数由 func 开始声明
  • function_name:函数名称,函数名和参数列表一起构成了函数签名。
  • parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
  • return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
  • 函数体:函数定义的代码集合。

返回值可以为多个:

func test(x, y int, s string) (int, string) {// 类型相同的相邻参数,参数类型可合并。 多返回值必须用括号。n := x + y          return n, fmt.Sprintf(s, n)
}

1.1 函数做为参数

函数做为一等公民,可以做为参数传递。

func fn() int {return 300
}
func test(fn func() int) int {return fn()
}
func main() {//这是直接使用匿名函数s := test(func() int { return 200 })fmt.Println(s) //200//传入一个函数s2 := test(fn)fmt.Println(s2) //300
}

在将函数做为参数的时候,我们可以使用类型定义,将函数定义为类型,这样便于阅读

type myFunc func(s string, x, y int) stringfunc format(fn myFunc, s string, x, y int) string {return fn(s, x, y)
}func formatFunc(s string, x, y int) string {return fmt.Sprintf(s, x, y)
}
func main() {s2 := format(formatFunc, "%d %d", 10, 20)fmt.Println(s2)
}

1.2 函数返回值

函数返回值可以有多个,同时Go支持对返回值命名

func main() {var a, b = 10, 20fmt.Println(sum(a, b))
}
//多个返回值 用括号扩起来
func sum(a, b int) (int, int) {return a, b
}

.3 参数

函数定义时指出,函数定义时有参数,该变量可称为函数的形参。

形参就像定义在函数体内的局部变量。

但当调用函数,传递过来的变量就是函数的实参,函数可以通过两种方式来传递参数:

  1. 值传递:指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
func swap(x, y int) int {... ...}

  1. 引用传递:是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
package mainimport ("fmt"
)/* 定义相互交换值的函数 */
func swap(x, y *int) {*x,*y = *y,*x
}func main() {var a, b int = 1, 2/*调用 swap() 函数&a 指向 a 指针,a 变量的地址&b 指向 b 指针,b 变量的地址*/swap(&a, &b)fmt.Println(a, b)
}

map、slice、chan、指针、interface默认以引用的方式传递。

不定参数传值

不定参数传值 就是函数的参数不是固定的,后面的类型是固定的。(可变参数)

Golang 可变参数本质上就是 slice。只能有一个,且必须是最后一个。

在参数赋值时可以不用用一个一个的赋值,可以直接传递一个数组或者切片。

格式:

func test1(args ...int) { //0个或多个参数}func test2(a int, args ...int) int { //1个或多个参数}func test3(a int, b int, args ...int) int {   //2个或多个参数}

注意:其中args是一个slice,我们可以通过arg[index]依次访问所有参数,通过len(arg)来判断传递参数的个数.

func main() {a := []int{10, 20, 10}fmt.Println(test("sum: %d", a...)) // slice... 展开slice
}func test(s string, n ...int) string {var x = 0for _, v := range n {x += v}return fmt.Sprintf(s, x)
}

2. 匿名函数

匿名函数是指不需要定义函数名的一种函数实现方式。

在Go里面,函数可以像普通变量一样被传递或使用,Go语言支持随时在代码里定义匿名函数。

匿名函数由一个不带函数名的函数声明和函数体组成。匿名函数的优越性在于可以直接使用函数内的变量,不必声明。

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

func(参数列表)(返回参数列表){函数体
}

func main() {//这里将一个函数当做一个变量一样的操作。getSqrt := func(a float64) float64 { return math.Sqrt(a)}fmt.Println(getSqrt(4.123))
}

在定义时调用匿名函数

匿名函数可以在声明后调用,例如:

func main() {func(a int) {fmt.Println(a)}(100) //(100),表示对匿名函数进行调用,传递参数为 100。
}

返回多个匿名函数

package mainimport "fmt"func FGen(x, y int) (func() int, func(int) int) {//求和的匿名函sum := func() int {return x + y}// (x+y) *z 的匿名函数avg := func(z int) int {return (x + y) * z}return sum, avg
}func main() {f1, f2 := FGen(1, 2)fmt.Println(f1())fmt.Println(f2(3))
}

3. 闭包

所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

闭包=函数+引用环境

package mainimport "fmt"func main() {// 创建一个玩家生成器generator := playGen("码神")// 返回玩家的名字和血量name, hp := generator()// 打印值fmt.Println(name, hp)generator1 := playGen2()name1, hp1 := generator1("码神")// 打印值fmt.Println(name1, hp1)
}// 创建一个玩家生成器, 输入名称, 输出生成器
func playGen(s string) func() (string, int) {// 血量一直为150hp := 150// 返回创建的闭包return func() (string, int) {// 将变量引用到闭包中return s, hp}
}// 创建一个玩家生成器, 输入名称, 输出生成器
func playGen2() func(name string) (string, int) {hp := 150return func(name string) (string, int) {return name, hp}
}

. 延迟调用

Go语言的 defer 语句会将其后面跟随的语句进行延迟处理

defer特性:

  1. 关键字 defer 用于注册延迟调用。
  2. 这些调用直到 return 前才被执。因此,可以用来做资源清理。
  3. 多个defer语句,按先进后出的方式执行。
  4. defer语句中的变量,在defer声明时就决定了。

defer的用途:

  1. 关闭文件句柄
  2. 锁资源释放
  3. 数据库连接释放

go 语言的defer功能强大,对于资源管理非常方便,但是如果没用好,也会有陷阱。

package mainimport ("log""time"
)func main() {start := time.Now()log.Printf("开始时间为:%v", start)defer log.Printf("时间差:%v", time.Since(start))  // Now()此时已经copy进去了//不受这3秒睡眠的影响time.Sleep(3 * time.Second)log.Printf("函数结束")
}
  • Go 语言中所有的函数调用都是传值的
  • 调用 defer 关键字会立刻拷贝函数中引用的外部参数 ,包括start 和time.Since中的Now
  • defer的函数在压栈的时候也会保存参数的值,并非在执行时取值。

如何解决上述问题:使用defer func()

func main() {start := time.Now()defer func() {log.Printf("开始调用defer")log.Printf("时间差:%v", time.Since(start))log.Printf("结束调用defer")}()time.Sleep(3 * time.Second)log.Printf("函数结束")
}

因为拷贝的是函数指针,函数属于引用传递

另一个问题:

func main() {var whatever = [5]int{1, 2, 3, 4, 5}for i, _ := range whatever {//函数正常执行,由于闭包用到的变量 i 在执行的时候已经变成4,所以输出全都是4.defer func() { fmt.Println(i) }()}
}

解决:

func main() {var whatever = [5]int{1, 2, 3, 4, 5}for i, _ := range whatever {i := idefer func() { fmt.Println(i) }()}
}

5. 异常处理

Go语言中使用 panic 抛出错误,recover 捕获错误。

异常的使用场景简单描述:Go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理。

panic:

  1. 内置函数
  2. 假如函数F中书写了panic语句,会终止其后要执行的代码,在panic所在函数F内如果存在要执行的defer函数列表,按照defer的逆序执行
  3. 返回函数F的调用者G,在G中,调用函数F语句之后的代码不会执行,假如函数G中存在要执行的defer函数列表,按照defer的逆序执行
  4. 直到goroutine整个退出,并报告错误

recover:

  1. 内置函数
  2. 用来捕获panic,从而影响应用的行为

golang 的错误处理流程:当一个函数在执行过程中出现了异常或遇到 panic(),正常语句就会立即终止,然后执行 defer 语句,再报告异常信息,最后退出 goroutine。如果在 defer 中使用了 recover() 函数,则会捕获错误信息,使该错误信息终止报告。

注意:

  1. 利用recover处理panic指令,defer 必须放在 panic 之前定义,另外 recover 只有在 defer 调用的函数中才有效。否则当panic时,recover无法捕获到panic,无法防止panic扩散。
  2. recover 处理异常后,逻辑并不会恢复到 panic 那个点去,函数跑到 defer 之后的那个点。
  3. 多个 defer 会形成 defer 栈,后定义的 defer 语句会被最先调用。

func main() {test()
}func test() {defer func() {if err := recover(); err != nil {log.Println(err) //2024/02/28 23:03:38 print out panic}}()panic("print out panic")}

由于 panic、recover 参数类型为 interface{},因此可抛出任何类型对象。

func panic(v interface{})func recover() interface{}

延迟调用中引发的错误,可被后续延迟调用捕获,但仅最后一个错误可被捕获:

func test() {defer func() {// defer panic 会打印fmt.Println(recover())  //defer panic}()defer func() {panic("defer panic")}()panic("test panic")
}func main() {test()
}

除用 panic 引发中断性错误外,还可返回 error 类型错误对象来表示函数调用状态:

type error interface {Error() string
}

标准库 errors.New 和 fmt.Errorf 函数用于创建实现 error 接口的错误对象。通过判断错误对象实例来确定具体错误类型。

package mainimport ("errors""fmt""log"
)var divError = errors.New("div error:分母不能为0")func div(x, y int) (int, error) {if y == 0 {return 0, divError  }return x / y, nil
}func main() {defer func() {if err := recover(); err != nil {log.Fatalln(err)  //2024/02/28 23:17:14 div error:分母不能为0}}()res, err := div(8, 0)switch err {case nil:fmt.Println(res)case divError:panic(err)}}

相关文章:

GO—函数

Go 语言支持普通函数、匿名函数和闭包,从设计上对函数进行了优化和改进,让函数使用起来更加方便。 Go 语言的函数属于“一等公民”(first-class),也就是说: 函数本身可以作为值进行传递。支持匿名函数和闭…...

[WebUI Forge]ForgeUI的安装与使用 | 相比较于Auto1111 webui 6G显存速度提升60-75%

ForgeUI的github主页地址:https://github.com/lllyasviel/stable-diffusion-webui-forge Stable Diffusion WebUI Forge 是一个基于Stable Diffusion WebUI(基于Gradio)的平台,可简化开发、优化资源管理并加快推理速度。 “Forge”这个名字的灵感来自于“Minecraft Forge”…...

高刷显示器 - HKC VG253KM

🔥🔥 今天来给大家揭秘一款电竞神器 - HKC VG253KM 高刷电竞显示器!这款显示器可是有着雄鹰展翅般的设计灵感,背后的大鹏展翅鹰翼图腾让人过目难忘。那么,这款显示器到底有哪些过人之处呢?一起来看看吧&…...

javascript实现的星座查询

今天在这个网站http://xzxys.wiicha.com/看到查询星座幸运色的效果,想研究一下代码,结果右键禁用。后来参考了一下别人的代码,琢磨着先实现了一下星座查询的功能,输入月份和日期四位数后,可以查询属于哪个星座&#xf…...

全国青少年软件编程(Python)等级考试试卷(一级) 测试卷2021年12月

第 1 题 【 单选题 】 下面程序的运行结果是什么?( ) a10 b5 ca*b print(c) A :10 B :15 C :50 D :5 正确答案:C 试题解析: 第 2 题 【 单选题 】 与a>b and b>c等价的是?( ) A…...

昇腾ACL应用开发之硬件编解码dvpp

1.前言 在我们进行实际的应用开发时,都会随着对一款产品或者AI芯片的了解加深,大家都会想到有什么可以加速预处理啊或者后处理的手段?常见的不同厂家对于应用开发的时候,都会提供一个硬件解码和硬件编码的能力,这也是抛…...

MFC 模态对话框退出机制的探究

一位读者问了这样一个问题: ” 如果我创建了一个可见的模态对话框,却对用户来说不可用。举个例子,假设我在程序中的其他位置收到一个事件,并且我从事件中调用模态 CDialog 上的 DestroyWindow。我注意到 OnDestroy 是在 CDialog 上调用的,但在将 WM_QUIT 消息发送到模态对…...

Android13 framework添加关机接口

framework层修改: t0_sys/frameworks/base/core/api/current.txt method RequiresPermission(android.Manifest.permission.REBOOT) public void reboot(Nullable String);method public void rebootp();t0_sys/frameworks/base/core/java/android/os/IPowerManager…...

如何使用ArcGIS Pro为栅格图添加坐标信息

在某些时候,我们从网上获取的资源是一张普通的栅格图,没有任何的坐标信息,如果想要和带坐标信息的数据一起使用就需要先添加坐标信息,在GIS上,我们把这个过程叫做地理配准,这里为大家介绍一下地理配准的方法…...

FDM打印机学习

以下内容摘自网络,仅供学习讨论,侵删。 持续更新。。。 FDM打印机是通过喷头融化丝状耗材(PLA,ABS等材料),然后逐层涂在热床上,一层一层逐级抬高。 结构分类 Prusa i3型是一种龙门结构&#…...

C++进阶-- map和set

关联式容器 在前面,我们所学的vector、list、deque,这些都是序列容器,也就是底层为线性序列的数据结构。 而关联式容器是C标准库中的一种类别,用于存储键值对(key-value pair),关联式容器中的元…...

AI-数学-高中-33概率-事件的关系与运算

原作者视频:【概率】【一数辞典】2事件的关系与运算_哔哩哔哩_bilibili 事件: 和/并事件;积/交事件;互诉事件;对立(补集)事件;...

数据结构:链队

一、定义两个结构体 定义两个结构体,一个结构体是结点的结构体&#xff0c;一个结构体是保留指向对头结点和队尾结点指针的结构体 #ifndef __LINK_QUEUE_H__ #define __LINK_QUEUE_H__ #include <stdio.h> #include <stdlib.h>typedef struct link_node{int data…...

十四、计算机视觉-形态学梯度

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、梯度的概念二、梯度的应用三、梯度如何实现 一、梯度的概念 形态学梯度&#xff08;Morphological Gradient&#xff09;是数字图像处理中的一种基本操作&…...

3月03日,每日信息差

&#x1f396; 素材来源官方媒体/网络新闻 &#x1f384; 国产商用飞机C919及ARJ21首次飞抵老挝 &#x1f30d; 北京将打造新一批智能工厂 &#x1f30b; 阿里云将于3月29日停止商标代理服务 &#x1f381; 起亚在美因远光灯故障召回3.5万辆Telluride汽车 ✨ 天涯社区拟5月1日前…...

leetcode 简单

1. 两数之和 两数之和 方法1&#xff1a;暴力枚举 两次for 循环&#xff0c;记录索引和值&#xff0c;找到合适的值然后返回 方法2&#xff1a;使用哈希表 第一次for循环的时候&#xff0c;就可以使用哈希表记录key的value&#xff0c;可以实现时间复杂度是1&#xff0c;要分…...

服务器硬件基础知识全解析

在信息技术日新月异的今天&#xff0c;服务器作为数据处理和存储的核心&#xff0c;其重要性不言而喻。了解服务器硬件基础知识&#xff0c;对于IT从业者以及广大技术爱好者来说&#xff0c;都是不可或缺的技能。本文将详细解析服务器硬件的基础知识&#xff0c;帮助读者建立起…...

python毕设选题 - 大数据商城人流数据分析与可视化 - python 大数据分析

文章目录 0 前言课题背景分析方法与过程初步分析&#xff1a;总体流程&#xff1a;1.数据探索分析2.数据预处理3.构建模型 总结 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到…...

vmware网络负载均衡方式

基于 IP 哈希的路由&#xff1a; 原理&#xff1a; 基于虚拟机的源和目标 IP 地址以及 TCP/UDP 端口号计算哈希值&#xff0c;并使用该哈希值确定出口网络适配器。这样可以确保同一对源和目标的网络流量始终被路由到相同的网络适配器。应用场景&#xff1a; 适用于大量使用虚拟…...

Docker基础教程 - 2 Docker安装

更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 2 Docker安装 Docker 的官网地址&#xff1a;https://www.docker.com/&#xff0c;在官网可以找到 Docker Engine 的安装步骤。 下面进行 Docker 环境的安装&#xff0c;正常情况下 Docker …...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

微服务商城-商品微服务

数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...