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.mzph.cn/news/715181.shtml

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

相关文章

推荐6款SSH远程连接工具

1、Xshell 介绍: xshell是一个非常强大的安全终端模拟软件,它支持SSH1, SSH2, 以及Windows平台的TELNET 协议。Xshell可以在Windows界面下用来访问远端不同系统下的服务器,从而比较好的达到远程控制终端的目的。 业界最强大的SSH客户机 官…

数据分析-Pandas数据的直方图探查

数据分析-Pandas数据的直方图探查 数据分析和处理中,难免会遇到各种数据,那么数据呈现怎样的规律呢?不管金融数据,风控数据,营销数据等等,莫不如此。如何通过图示展示数据的规律? 数据表&…

农产品质量追溯系统—功能介绍(2)

储藏管理 储藏信息管理对需要储藏的农产品,记录储藏的相关信息,如储藏开始时间、存放仓库、操作人员、储藏原因等; 仓库信息管理物流管理 物流公司管理对相关的物流公司信息进行登记,以便于管理和追溯; 车辆管理

我的秋招数据分析岗面经分享(京东,美团,阿里,拼多多,vivo,滴滴)

节前,我们社群组织了一场技术&面试讨论会,邀请了一些互联网大厂同学、参加社招和校招面试的同学,针对新手如何入门数据分析、机器学习算法、该如何备战面试、面试常考点分享等热门话题进行了深入的讨论。 基于社群的讨论,今天…

OpenHarmony、HarmonyOS打开编辑 PDF 等操作的三方组件使用教程

项目场景: 随着数字化时代的发展,PDF 文档成为广泛应用于各行业的重要文件格式。为了提高OpenHarmony/HarmonyOS生态系统的功能性和用户体验,我们需要一款支持打开、编辑PDF文件的应用程序。 使用户能够轻松打开、浏览和编辑PDF文件。该应用将充分利用OpenHarmony/HarmonyO…

【NTN 卫星通信】卫星和无人机配合的应用场景

1 场景概述 卫星接入网是一种有潜力的技术,可以为地面覆盖差地区的用户提供无处不在的网络服务。然而,卫星覆盖范围对于位于考古或采矿地点内部/被茂密森林覆盖的村庄/山谷/靠近山丘或大型建筑物的用户可能很稀疏。因此,涉及卫星接入和无人驾…

HarmonyOS Full SDK的安装

OpenHarmony的应用开发工具HUAWEI DevEco Studio现在随着OpenHarmony版本发布而发布,只能在版本发布说明中下载,例如最新版本的OpenHarmony 4.0 Release。对应的需要下载DevEco Studio 4.0 Release,如下图。 图片 下载Full SDK主要有两种方式,一种是通过DevEco Studio下载…

教你用Fiddler捕获HTTPS请求

安装Fiddler 这里不特别说明了,网上搜索一大把,根据安装引导一步步安装即可。(这里采用的是fiddler v4.6) 配置Fiddler 1、打开fiddler配置Tools –>Telerik Fiddler Options。 2、打开HTTPS配置项,勾选“Captur…

ctf_show笔记篇(web入门---爆破)

爆破 21:直接bp抓包跑字典,需base64加密 22:可用工具跑也可用浏览器找还可以用网上做好的域名查找去找 23:此题需跑脚本已经附上自写脚本 最后跑出来六个答案一个一个尝试得到答案为3j import hashlibm "0123456789qwert…

C++_AVL树

目录 1、AVL的概念 2、平衡因子的调整概念 3、AVL树的插入 3.1 调整平衡因子代码实现 3.2 右旋操作 3.2 左旋操作 3.3 双旋-先右旋再左旋 3.4 双旋-先左旋再右旋 3.5 旋转操作的小结 4、AVL的验证与实现 结语 前言: 在C中,AVL树是在二叉搜索…

旧的Spring Security OAuth已停止维护,全面拥抱新解决方案Spring SAS

Spring Authorization Server 替换 Shiro 指引 背景 Spring 团队正式宣布 Spring Security OAuth 停止维护,该项目将不会再进行任何的迭代 目前 Spring 生态中的 OAuth2 授权服务器是 Spring Authorization Server 已经可以正式生产使用作为 SpringBoot 3.0 的最新…

如何使用naive 做一个模态框的方式

1.我的问题使用了一个table 表格&#xff0c;在表格中设置俩个按钮 最后做出来的效果 <template><div><h1>测试文件</h1><!-- 表格 --><n-data-table :columns"columns" :data"data" :pagination"pagination" …

Linux内核队列queue.h

文章目录 一、简介二、SLIST单向无尾链表2.1 介绍2.2 操作2.3 例子 三、STAILQ单向有尾链表四、LIST双向无尾链表五、TAILQ双向有尾链表六、CIRCLEQ循环链表七、queue源码参考 一、简介 queue.h是一个非常经典的文件&#xff0c;定义了一系列宏的操作&#xff0c;它定义了一系…

笔记72:关于IMU(惯性测量单元)传感器的作用【不涉及公式推导】

一、IMU传感器是什么&#xff1a; 惯性测量单元IMU&#xff08;Inertial Measurement Unit&#xff09;是一种使用【加速度计】和【陀螺仪】来测量【物体三轴姿态角&#xff08;空间姿态&#xff09;】的装置&#xff1b;IMU在坐标系的每个坐标轴上&#xff0c;均安装有1个陀螺…

Vue2:用node+express部署Vue项目

一、编译项目 命令 npm run build执行命令后&#xff0c;我们会在项目文件夹中看到如下生成的文件 二、部署Vue项目 接上一篇&#xff0c;nodeexpress编写轻量级服务 1、在demo中创建static文件夹 2、将dist目录中的文件放入static中 3、修改server.js文件 关键配置&…

DFA还原白盒AES密钥

本期内容是关于某app模拟登录的,涉及的知识点比较多,有unidbg补环境及辅助还原算法,ida中的md5以及白盒aes,fart脱壳,frida反调试 本章所有样本及资料均上传到了123云盘 llb资料官方版下载丨最新版下载丨绿色版下载丨APP下载-123云盘 目录 首先抓包 fart脱壳 加密位置定位…

win11安装nodejs

一、下载安装包 链接: https://pan.baidu.com/s/1_df8s1UlgNNaewWrWgI59A?pwdpsjm 提取码: psjm 二、安装步骤 1.双击安装包 2.Next> 3.勾选之后&#xff0c;Next> 4.点击Change&#xff0c;选择你要安装的路径&#xff0c;然后Next> 5.点击Install安装 二、…

学生云服务器腾讯云_腾讯云学生学生_腾讯云学生云主机

2024年腾讯云学生服务器优惠活动「云校园」&#xff0c;学生服务器优惠价格&#xff1a;轻量应用服务器2核2G学生价30元3个月、58元6个月、112元一年&#xff0c;轻量应用服务器4核8G配置191.1元3个月、352.8元6个月、646.8元一年&#xff0c;CVM云服务器2核4G配置842.4元一年&…

基于扩散模型的图像编辑:首篇综述

AIGC 大模型最火热的任务之一——基于 Diffusion Model 的图像编辑(editing)领域的首篇综述。长达 26 页&#xff0c;涵盖 297 篇文献&#xff01;本文全面研究图像编辑前沿方法&#xff0c;并根据技术路线精炼地划分为 3 个大类、14 个子类&#xff0c;通过表格列明每个方法的…

LeetCode 热题 100 | 图论(一)

目录 1 200. 岛屿数量 2 994. 腐烂的橘子 2.1 智障遍历法 2.2 仿层序遍历法 菜鸟做题&#xff0c;语言是 C 1 200. 岛屿数量 解题思路&#xff1a; 遍历二维数组&#xff0c;寻找 “1”&#xff08;若找到则岛屿数量 1&#xff09;寻找与当前 “1” 直接或间接连接在…