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)}}

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

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

相关文章

Android 混淆是啥玩意儿?

什么是混淆 Android混淆,是伴随着Android系统的流行而产生的一种Android APP保护技术,用于保护APP不被破解和逆向分析。简单的说,就是将原本正常的项目文件,对其类、方法、字段,重新命名a,b,c…之类的字母&#xff0c…

假如有n个台阶,一次只能上1个台阶或2个台阶,请问走到第n个台阶有几种走法?

说明如下:假如有 3个台阶&#xff0c;那么总计就有3种走法:第一种为每次上1个台阶&#xff0c;上3次;第二种为先上2个台阶&#xff0c;再上1个台阶;第三种为先上1个台阶&#xff0c;再上2个台阶。 解决方法&#xff1a;递归 代码展示&#xff1a; #include <stdio.h> …

SD NAND:为车载显示器注入智能与安全的心脏

SD NAND 在车载显示器的应用 在车载显示器上&#xff0c;SD NAND&#xff08;Secure Digital NAND&#xff09;可以有多种应用&#xff0c;其中一些可能包括&#xff1a; 导航数据存储&#xff1a; SD NAND 可以用于存储地图数据、导航软件以及车载系统的相关信息。这有助于提…

【Vue3】插槽使用和animate使用

插槽使用 插槽slot匿名插槽具名插槽插槽作用域简写 动态插槽transition动画组件自定义过渡class类名如何使用animate动画库组件动画生命周期appear transition- group过渡列表transition- group平面过渡代码展示 gsap动画库 状态过渡 插槽slot 插槽就是子组件中提供给父组件使…

备战蓝桥杯---状态压缩DP进阶题1

我们来看一看一道比较难的问题&#xff08;十分十分的巧妙&#xff09;&#xff1a; 显然我们应该一行一行放&#xff0c;又竖的会对下一行产生影响&#xff0c;我们令横着放为0&#xff0c;竖着放的上方为1. 对于下一行&#xff0c;前一行放1的下面为0&#xff0c;但是会出现…

Python 实现Excel自动化办公(中)

在上一篇文章的基础上进行一些特殊的处理&#xff0c;这里的特殊处理主要是涉及到了日期格式数据的处理&#xff08;上一篇文章大家估计也看到了日期数据的处理是不对的&#xff09;以及常用的聚合数据统计处理&#xff0c;可以有效的实现你的常用统计要求。代码如下&#xff1…

Python爬虫——Urllib库-2

编解码 问题引入 例如&#xff1a; https://www.baidu.com/s?wd章若楠 https://www.baidu.com/s?wd%E7%AB%A0%E8%8B%A5%E6%A5%A0 第二部分的一串乱码就是章若楠 如果这里是写的章若楠就会 产生这样的错误 所以我们就可以使用get请求方式的quote方法了 get请求方式的q…

YOLOv9:使用可编程梯度信息学习您想学习的内容

摘要 arxiv.org/pdf/2402.13616.pdf 当今的深度学习方法侧重于如何设计最合适的目标函数,以便模型的预测结果能最接近于实际结果。同时,还必须设计一个适当的架构,以便于获取足够的预测信息。现有的方法忽略了一个事实,即当输入数据经历层层特征提取和空间变换时,会损失…

(学习日记)2024.02.29:UCOSIII第二节

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

BUGKU 本地管理员

打开环境&#xff0c;先F12查看看到一串代码。Base64解码一下&#xff0c;得到的应该是密码&#xff0c;然后输入admin | test123试一下 使用BP抓包&#xff0c;修改XFF&#xff0c;得到flag

社区分享|中华保险基于MeterSphere开展接口自动化测试

中华联合保险集团股份有限公司&#xff08;以下简称为“中华保险”&#xff09;始创于1986年&#xff0c;是全国唯一一家以“中华”冠名的国有控股保险公司。截至2022年12月底&#xff0c;中华保险总资产为1006.06亿元&#xff0c;在全国拥有超过2900个营业网点&#xff0c;员工…

破解SQL Server迷局,彻底解决“管道的另一端无任何进程错误233”

问题描述&#xff1a;在使用 SQL Server 2014的时候&#xff0c;想用 SQL Server 身份方式登录 SQL Servcer Manager&#xff0c;结果报错&#xff1a; 此错误消息&#xff1a;表示SQL Server未侦听共享内存或命名管道协议。 问题原因&#xff1a;此问题的原因有多种可能 管道…

SpringBoot整合JdbcTemplate

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: 循序渐进学SpringBoot ✨特色专栏: MySQL学习 🥭本文内容:SpringBoot整合JdbcTemplate 📚个人知识库: Leo知识库,欢迎大家访问 目录 …

openGauss学习笔记-232 openGauss性能调优-系统调优-资源负载管理-资源管理准备-资源规划

文章目录 openGauss学习笔记-232 openGauss性能调优-系统调优-资源负载管理-资源管理准备-资源规划 openGauss学习笔记-232 openGauss性能调优-系统调优-资源负载管理-资源管理准备-资源规划 完成资源负载管理功能配置前&#xff0c;需要先根据业务模型完成租户资源的规划。业…

fastjson反序列化漏洞

fastjson反序列化漏洞 文章目录 fastjson反序列化漏洞1.漏洞原理2.探测方式2.1 查看回显2.2 LDAP/RMI服务测试 3.LDAP/RMI服务搭建要求4.漏洞复现4.1 fastjson <1.2.47 反序列化导致任意命令执行漏洞4.1.1 环境准备4.1.2 复现过程 4.2 fastjson <1.2.24 反序列化导致任意…

simple-pytest 框架使用指南

simple-pytest 框架使用指南 一、框架介绍简介框架理念&#xff1a;框架地址 二、实现功能三、目录结构四、依赖库五、启动方式六、使用教程1、快速开始1.1、创建用例&#xff1a;1.2、生成py文件1.3、运行脚本1.3.1 单个脚本运行1.3.2 全部运行 1.4 报告查看 2、功能介绍2.1、…

vue2 开发记录

el-select 如何修改选择项的样式/el-select-dropdown__item 文字上下显示 测试代码 <div stylemargin-left: 100px><!-- 测试代码--><el-select filterablesizemini><div classxxx-el-select><el-optionv-foritem in [{key:1,des:2,…

web自动化笔记八:滚动条处理和窗口截屏

一、滚动条处理 ①为什么要操作滚动条&#xff1f; 1. 在HTML页面中&#xff0c;由于前端技术框架的原因&#xff0c;页面元素为动态显示&#xff0c;元素根据滚动条的下拉而被加载 2. 页面注册同意条款&#xff0c;需要滚动条到最底层&#xff0c;才能点击…

vue cesium加载点与定位到指定位置

vue cesium定位到指定位置 window.viewer.camera.flyTo({destination: Cesium.Cartesian3.fromDegrees(point.longDeg, point.latDeg, 6500000), orientation: {heading: 6.2079384332084935, roll: 0.00031509431759868534, pitch: -1.535}, duration: 3})vue cesium加载点 …

QMLButton背景初始化

文章目录 异常情况解决办法 异常情况 Button {id: controltext: qsTr("Button")width: 100height: 150property color normalColor: "green"property color hoverColor: "yellow"property color pressColor: "red"background: Rectan…