08【保姆级】-GO语言的函数、包、错误处理

08【保姆级】-GO语言的函数、包、错误处理

  • 一、 函数基本介绍
    • 1.1 基本概念
    • 1.2 包的概念
    • 1.3 包使用的注意事项和细节
    • 1.4 函数的调用机制
    • 1.5 函数的递归调用
    • 1.6 函数使用的注意事项和细节讨论
    • 1.7 init函数
    • 1.8 匿名函数
      • 1.8.1 匿名函数使用方式
      • 1.8.2 全局匿名函数
    • 1.9 闭包
      • 1.9.1 闭包的最佳实践

之前我学过C、Java、Python语言时总结的经验:

  1. 先建立整体框架,然后再去抠细节。
  2. 先Know how,然后know why。
  3. 先做出来,然后再去一点点研究,才会事半功倍。
  4. 适当的囫囵吞枣。因为死抠某个知识点很浪费时间的。
  5. 对于GO语言,切记遵守语法格式规则。(例如python语言、例如SpringBoot框架等)
    解释:某些知识点,就是很难了解,那么先做出来,然后继续向前学习,可能在某个时间点我们就会恍然大悟。

一、 函数基本介绍

1.1 基本概念

为完成某一功能的程序指令(语句)的集合,称为函数
在Go语言中分为:自定义函数、系统函数。

func 函数名(形参列表) (返回值类型列表){
	执行语句
	return 返回值列表
}

1. 形参列表: 表示函数的输入
2. 函数中的语句:表示为了实现某一功能代码块
3. 函数可以有返回值,也可以没有

例子: j

func main() {

	c := add(2, 5)
	fmt.Println("c=", c)

}

func add(a int, b int) int {

	c := a + b
	return c
}

1.2 包的概念

  1. 在实际的开发中,我们往往需要在不同的文件中,去调用其它文件的定义的函数,比如 main.go中,去使用 utils.go 文件中的函数,如何实现? -》包
  2. 现在有两个程序员共同开发一个 Go 项目,程序员 xiaoming 希望定义函数 Cal,程序员 xiaoqiang也想定义函数也叫 Cal。两个程序员为此还吵了起来,怎么办?-》包

包的本质实际上就是创建不同的文件夹,来存放程序文件。

包的基本概念: go 的每一个文件都是属于一个包的,也就是说 go 是以包的形式来管理文件和项目目录结构

  • 包的三大作用
  1. 区分相同名字的函数、变量等标识符
  2. 当程序文件很多时,可以很好的管理项目
  3. 控制函数、变量等访问范围,即作用域
  • 包的基本语法
package 包名
  • 引入包的基本语法
import "包的路径"
  • 包使用的快速入门
    包快速入门-Go 相互调用函数,我们将func Cal 定义到文件utils.go,将utils.go放入一个包中,当其他文件需要使用utils.go的方法时,可以import该包,就可以使用了。

在这里插入图片描述

1.3 包使用的注意事项和细节

  1. 在给一个文件打包时,该包对应一个文件夹,比如这里的utils文件夹,对应的包名是utils,文件的包名通常和文件所在的文件夹名一致,一般为小写字母。
  2. 当一个文件要使用其它包函数或变量时,需要先引入对应的包。
引入方式1import “包名”

引入方式2import(
	"包名"
	"包名"
)


package 指令在 文件第一行,然后是import指令
在import包时,路径从$GOPATH 的src下开始,不用带src,编译器会自动从src下开始引入。
  1. 为了让其他包的文件,可以访问到本包的函数,则该函数名的首字母需要大写,类似其他语言的public,这样才能跨包访问。比如 utils.go

  2. 在访问其他函数,变量时,其语法是 包名.函数名 ,比如这里的 main.go 文件中:

  3. 如果包名较长,Go支持给包取别名,注意细节:取别名后,原来的包名就不能使用了。

说明:如果给包取了别名,则需要使用别名来访问该包的函数和变量

  1. 在同一包下,不能有相同的函数名(也不能有相同的全局变量名),否则报重复定义
  2. 如果你要编译成一个可执行程序文件,就需要将这个包申明为main,既 package main。 这个就是一个语法规范,如果你是写一个库,包名可以自定义。
路径下写:
- src 
 - 项目名称
   - 各个包名

1.4 函数的调用机制

关于栈区:
函数在调用的时候,基本数据类型,一般说分配到栈区,编译器存在一个逃逸分析
栈区:
add 栈区
main 栈区

  1. 在调用一个函数时,会给该函数分配一个新的空间,编译器会通过自身的处理,让这个新的空间和其他的栈的空间区分开来
  2. 在每个函数对应的栈中, 数据空间是独立的。不会混淆
  3. 当一个函数调用完毕(执行完毕)后,程序会销毁这个函数对应的栈空间。
func main() {

	n := 10
	add(n)
	fmt.Println("main() 的n =", n)   // main() 的n = 10

}
func add(n int) {
	n = n + 1
	fmt.Println("add() 的n =", n)  // add() 的n = 11
}

1. 其中两个函数都是在栈中,当main函数只是将 n 的值 传给了add函数(值传递).
2. 这样说来,add更改n的值,并不会更改main的n的值.
3. 其中根据栈的原则,先进后出,main先进去的,所以,先打印add函数的println语句。

关于堆区:
堆区:引用数据类型一般说分配到堆区,编译器存在一个逃逸分析

代码区:
所有的代码存储到此位置。


案例要求:写一个函数,传入两个int值,返回两者的相加和相减

func main() {

	n1 := 10
	n2 := 5
	add, sub := addOrSub(n1, n2)
	fmt.Println("add=", add, "sub=", sub) // add= 15 sub= 5

}
func addOrSub(n1 int, n2 int) (int, int) {
	return n1 + n2, n1 - n2
}

1.5 函数的递归调用

一个函数在函数体内调用了本身,我们称为递归调用。

  1. 执行一个函数时,就创建一个新的受保护的独立空间(新函数栈)
  2. 函数的局部变量是独立的,不会相互影响
  3. 递归必须向退出递归的条件逼近,否则就是无限递归,死鬼了
  4. 当一个函数执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当函数执行完毕或者返回时,该函数本身也会被系统销毁。

递归的演示案例:

-- 第一种: 
//  第一种自己的思路(方便理解):
// 当第1次调用test时,调用了递归,我们可以先把fmt.println打印的结果写出来,也就是3
// 当第2次调用test时,调用了递归,我们可以先把fmt.println打印的结果写出来,也就是2
// 当第3次调用test时,没有调用递归,我们可以先把fmt.println打印的结果写出来,也就是2
// 那么最后的结果,反推上去也就是:2/2/3


func main() {
	n1 := 4
	test(n1)
}
func test(n1 int) {
	if n1 > 2 {
		n1--
		test(n1)
	}
	fmt.Println("n1=", n1)
}
n1= 2
n1= 2
n1= 3


-- 第二种:
// 自己的思路:
// 不管多少次递归,那么最后结果是,最后一次不符合if条件后,才会执行的else结果。
func main() {

	n1 := 4
	test(n1)

}
func test(n1 int) {
	if n1 > 2 {
		n1--
		test(n1)
	} else {
		fmt.Println("n1=", n1)
	}
}
n1= 2

1.6 函数使用的注意事项和细节讨论

  1. 函数的形参列表可以是多个,返回值列表也可以是多个。
  2. 形参列表和返回值列表的数据类型可以是值类型和引用类型。
  3. 函数的命名遵循标识符命名规范,首字母不能是数字,首字母大写该函数可以被本包文件和其它文件使用,类似public首字母小写只能被本包文件使用,其它包文件不能使用,类似private
  4. 函数中的变量是局部的,函数外不生效
  5. 基本数据类型和数组 默认都是值传递的,既进行值拷贝。在函数内修改,不会影响到原来的值
  6. 如果希望函数内的变量能修改函数外的变量(指的是 默认以值传递的方式的数据类型),而已传入的地址&,函数内以指针的方式操作变量。
func main() {
	n1 := 4
	test(&n1)
	fmt.Println("main() n1=", n1)  //main() n1= 14
}
func test(n1 *int) {
	*n1 = *n1 + 10
	fmt.Println("test() n1=", *n1)  // test() n1= 14
}
  1. Go函数不支持函数重载。
  2. 在Go中,函数也是一种数据类型 ,可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该变量可以对函数 调用。
func main() {
	//在Go中,函数也是一种数据类型
	a := getSum
	fmt.Println(a(1, 2))
}

func getSum(n1 int, n2 int) int {
	return n1 + n2
}

  1. 函数既然是一个种函数类型,因此在Go中,函数可以作为函数,并且调用。
1. myFun函数,将三个参数分别赋值给myFun() 其中func(int ,int)int 是一个类型。
	funvar = getSum
	num1 = 50
	num2 = 60
2. 然后将 num1 和 num2 赋值给 funvar(num1,num2)
3. 根据第一点得知,funvar 等于 getSum函数。
4. 根据getSum()函数得知,n1 + n2 =50+60. 得到结果是110getSum()函数返回110给的是res2
5. 切记:getSum返回的110,是返回给res2的

在这里插入图片描述

  1. 为了简化数据类型定义,Go支持自定义数据类型
基本语法:type 自定义数据类型名   数据类型 
// 理解:相当于一个别名
案例:type myInt int  
// 这时  myInt 就等价 int来使用了
// myInt 是一个类型,虽然根据定义是一个类型,是一个int类型,但是Go认为并不是同种类型。
//如代码:

在这里插入图片描述

案例:type mySum func(int,int)int 
// 这时 mySum就等价 一个函数类型func(int,int) int
//  创建一个mySum 类型,该类型是一个函数,名为:func(int,int)int函数

在这里插入图片描述

  1. 支持对函数返回值命名
func main() {
	var num1 int
	var num2 int
	num1 = 10
	num2 = 10
	add, sub := addOrSub(num1, num2)
	fmt.Println("add=", add, "sub=", sub)
	// add= 20 sub= 0
}

func addOrSub(num1 int, num2 int) (add int, sub int) {
	add = num1 + num2
	sub = num1 - num2
	return
	// 此时可以忽略掉 返回类型和顺序
}
  1. 只用 _ 标识符,忽略返回值
  2. Go支持可变参数
// 支持0到多个参数
func sunc(args... int ) sum int {
}
// 支持1到多个参数
func sum(n1 int ,args... int) sum int{
}
14. args是slice切片(可以理解java的数组),通过args[index] key访问到各个值
15. 如果一个函数的形参列表中有可变参数,则可变参数需要放在形参列表最后。

编写函数swap(n1 *int,n2 *int) 进行交换n1 和 n2的值

func main() {
	var n1 int = 10
	var n2 int = 20
	fmt.Println("前n1=", n1, "前n2=", n2)
	swap(&n1, &n2)
	fmt.Println("后n1=", n1, "后n2=", n2)

	//前n1= 10 前n2= 20
	//后n1= 20 后n2= 10
}

func swap(a *int, b *int) {
	var test int
	test = *a
	*a = *b
	*b = test
}

1.7 init函数

每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被Go运行框架调用,也就是说init会在main函数前被调用。

在这里插入图片描述在这里插入图片描述

  1. 如果一个文件同时包含全局变量定义init函数,和main函数 ,则 执行流程:全局变量定义 -> init 函数 -> main函数

    1. 当如果有被引入的包,那么会先执行被引入的包。例如 fmt 、 utils
      在这里插入图片描述
  2. init函数最主要的作用,就是完成一些初始化的工作

在这里插入图片描述

1.8 匿名函数

Go支持匿名函数,匿名函数就是没有名字的函数,如果我们某个函数只是希望使用一次,可以考虑使用匿名函数,匿名函数也可以实现多次调用。

1.8.1 匿名函数使用方式

在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次。

// 在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次。
func main() {
	resAdd := func(a int, b int) int {
		return a + b
	}(1, 2)
	fmt.Println("resAdd=", resAdd) //  resAdd= 3
}

匿名函数赋给一个变量(函数变量),再通过该变量来调用匿名函数

resSub := func(c int, d int) int {
		return c - d
	}
	sub := resSub(1, 2)
	fmt.Println("sub=", sub) // sub= -1

1.8.2 全局匿名函数

如果将匿名函数赋给一个全局变量,那么这个匿名变量,就成为一个全局匿名函数,可以在程序有效。

var (
	myAllFunc = func(num1 int, num2 int) int {
		return num1 + num2
	}
)

// 在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次。
func main() {
	fmt.Println("调用全局的匿名函数的结果为:", myAllFunc(1, 2))
	// 调用全局的匿名函数的结果为: 3
}

在这里插入图片描述

1.9 闭包

闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)

  1. AddUpper 是一个函数,返回的数据类型是fun (int) int
  2. 闭包的说明
    返回的是一个匿名函数,但是这个匿名函数引用到函数外的 n,因此这个匿名函数就和n形成个整体,构成闭包。
  3. 大家可以这样理解: 闭包是类,函数是操作,n 是字段。函数和它使用到 n 构成闭包。
  4. 当我们反复的调用 f函数时,因为 n 是初始化一次,因此每调用一次就进行累计。
  5. 我们要搞清楚闭包的关键,就是要分析出返回的函数它使用(引用)到哪些变量,因为函数和它引用到的变量共同构成闭包。

1.9.1 闭包的最佳实践

  1. 编写一个函数 makeSuffix(sufix string) 可以接收一个文件后缀名(比如jpg),并返回一个闭包2. 调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如.jpg),则返回 文件名jpg,如果已经有.ipg 后缀,则返回原文件名。
  2. 要求使用闭包的方式完成
  3. strings.HasSuffix,该函数可以判断某个字符串是否有指定的后缀。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

0基础学习PyFlink——水位线(watermark)触发计算

在《0基础学习PyFlink——个数滚动窗口(Tumbling Count Windows)》和《0基础学习PyFlink——个数滑动窗口(Sliding Count Windows)》中,我们发现如果窗口中元素个数没有把窗口填满,则不会触发计算。 为了解决长期不计算的问题&a…

Docker安装详细步骤及相关环境安装配置(mysql、jdk、redis、自己的私有仓库Gitlab 、C和C++环境以及Nginx服务代理)

目录 一、从空白系统中克隆Centos7系统 二、使用xshell连接docker_tigerhhzz虚拟机​编辑 三、在CentOS7基础上安装Docker容器 四、在Docker中进行安装Portainer 4.1、在Docker中安装MySQL 4.2、在Docker中安装JDK8,安装Java环境 4.3、Docker安装redis&#…

聚观早报 |京东11.11公布成绩单;2023数字科技生态大会

【聚观365】11月13日消息 京东11.11公布成绩单 2023数字科技生态大会 TikTok深受英国中小企业青睐 周鸿祎称大模型2年内可“进”智能汽车 双11全国快递业务量达 6.39 亿件 京东11.11公布成绩单 京东11.11公布成绩单:截至11月11日晚23:59,2023年京东…

桌面云架构讲解(VDI、IDV、VOI/TCI、RDS)

目录 云桌面架构 VDI 虚拟桌面基础架构 IDV 智能桌面虚拟化 VOI/TCI VOI 虚拟系统架构 TCI 透明计算机架构 RDS 远程桌面服务 不同厂商云桌面架构 桌面传输协议 什么是云桌面 桌面云是虚拟化技术成熟后发展起来的一种应用,桌面云通常也称为云桌面、VDI等 …

OpenCV 笔记(6):像素间的基本关系——邻域、邻接、通路、连通、距离

像素是图像的基本元素,像素与像素之间存在着某些联系,理解像素间的基本关系是数字图像处理的基础。常见的像素间的基本关系包括:邻域、邻接、通路、连通、距离。 Part11. 邻域 邻域表示了像素之间的连接关系。 像素(x,y)的邻域,是…

计算机二级Office真题解析 excel减免税,订单,成绩

第一题 1.将“Excel 减免税.xlsx”文件另存为 excel.xlsx,最后提交该文件(1 分)。 2.将“对应代码.xlsx”文件中的 sheet1 工作表插入到 excel.xlsx 中,工作 表名重命名为“代码”(3 分)。 3.在"序号&…

从关键新闻和最新技术看AI行业发展(2023.10.23-11.5第九期) |【WeThinkIn老实人报】

Rocky Ding 公众号:WeThinkIn 写在前面 【WeThinkIn老实人报】旨在整理&挖掘AI行业的关键新闻和最新技术,同时Rocky会对这些关键信息进行解读,力求让读者们能从容跟随AI科技潮流。也欢迎大家提出宝贵的优化建议,一起交流学习&…

将随机数设成3407,让你的深度学习模型再涨一个点!文再附3种随机数设定方法

随机数重要性 深度学习已经在计算机视觉领域取得了巨大的成功,但我们是否曾想过为什么同样的模型在不同的训练过程中会有不同的表现?为什么使用同样的代码,就是和别人得到的结果不一样?怎么样才能保证自己每次跑同一个实验得到的…

Django中Cookie和Session的使用

目录 一、Cookie的使用 1、什么是Cookie? 2、Cookie的优点 3、Cookie的缺点 4、Django中Cookie的使用 二、Session的使用 1、什么是Session? 2、Session的优点 3、Session的缺点 4、Django中Session的使用 三、Cookie和Session的对比 总结 D…

Vue 小黑记事本组件板

渲染功能: 1.提供数据: 提供在公共的父组件 App.vue 2.通过父传子,将数据传递给TodoMain 3.利用 v-for渲染 添加功能: 1.收集表单数据 v-model 2.监听事件(回车点击都要添加) 3.子传父,讲…

【嵌入式设计】Main Memory:SPM 便签存储器 | 缓存锁定 | 读取 DRAM 内存 | DREM 猝发(Brust)

目录 0x00 便签存储器(Scratchpad memory) 0x01 缓存锁定(Cache lockdown) 0x02 读取 DRAM 内存 0x03 DREM Banking 0x04 DRAM 猝发(DRAM Burst) 0x00 便签存储器(Scratchpad memory&#…

Flutter有状态组件StatefulWidget生命周期

StatefulWidget是Flutter中的一个有状态的组件,它的生命周期相对复杂一些。下面是StatefulWidget的生命周期方法及其调用顺序: 1. createState(): 当StatefulWidget被插入到Widget树中时,会调用createState()方法来创建与之关联的State对象。…

软路由R4S+iStoreOS实现公网远程桌面局域网内电脑

软路由R4SiStoreOS实现公网远程桌面局域网内电脑 文章目录 软路由R4SiStoreOS实现公网远程桌面局域网内电脑简介 一、配置远程桌面公网地址配置隧道 二、家中使用永久固定地址 访问公司电脑具体操作方法是:2.1 登录页面2.2 再次配置隧道2.3 查看访问效果 简介 上篇…

力扣511. 游戏玩法分析 I

答案: select player_id,min(event_date) as first_login from Activity a group by player_id我最开始写的错误答案是这样的: select player_id,event_date as first_login from Activity a group by player_id having event_date min(event_date…

Docker - DockerFile

Docker - DockerFile DockerFile 描述 dockerfile 是用来构建docker镜像的文件!命令参数脚本! 构建步骤: 编写一个dockerfile 文件docker build 构建成为一个镜像docker run 运行脚本docker push 发布镜像(dockerhub&#xff0…

无监督学习的集成方法:相似性矩阵的聚类

在机器学习中,术语Ensemble指的是并行组合多个模型,这个想法是利用群体的智慧,在给出的最终答案上形成更好的共识。 这种类型的方法已经在监督学习领域得到了广泛的研究和应用,特别是在分类问题上,像RandomForest这样…

【KVM-5】KVM架构

前言 大家好,我是秋意零。今天分析的内容是KVM架构。 👿 简介 🏠 个人主页: 秋意零🔥 账号:全平台同名, 秋意零 账号创作者、 云社区 创建者🧑 个人介绍:在校期间参与…

正点原子嵌入式linux驱动开发——Linux IIO驱动

工业场合里面也有大量的模拟量和数字量之间的转换,也就是常说的ADC和DAC。而且随着手机、物联网、工业物联网和可穿戴设备的爆发,传感器的需求只持续增强。比如手机或者手环里面的加速度计、光传感器、陀螺仪、气压计、磁力计等,这些传感器本…

计算机视觉(CV)技术的优势和挑战

计算机视觉技术在很多领域具有很大的优势,例如: 自动化:计算机视觉技术可以帮助实现自动化生产和检测,省去了人力成本和时间成本。 准确性:计算机视觉技术可以提高生产和检测的准确性,降低了人工操作产生的误差。 速度:计算机视觉技术可以实现高速速度的生产和检测,提高…

flv.js在vue中的使用

Flv.js 是 HTML5 Flash 视频(FLV)播放器,纯原生 JavaScript 开发,没有用到 Flash。由 bilibili 网站开源。它的工作原理是将 FLV 文件流转码复用成 ISO BMFF(MP4 碎片)片段,然后通过 Media Sour…