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

news2024/11/17 8:43:38

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,该函数可以判断某个字符串是否有指定的后缀。

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

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

相关文章

基于flask+bootstrap4实现的注重创作的轻博客系统项目源码

一个注重创作的轻博客系统 作为一名技术人员一定要有自己的博客,用来记录平时技术上遇到的问题,把技术分享出去就像滚雪球一样会越來越大,于是我在何三博客的基础上开发了[l4blog],一个使用python开发的轻量博客系统,…

微信小程序广告banner、滚动屏怎么做?

使用滑块视图容器swiper和swiper-item可以制作滚动屏&#xff0c;代码如下&#xff1a; wxml: <swiper indicator-dots indicator-color"rgba(255,255,255,0.5)" indicator-active-color"white" autoplay interval"3000"><swiper-ite…

达梦数据库安装

一、官网参考文档 达梦数据库官网&#xff1a;https://www.dameng.com/ &#xff0c;参考文档如下&#xff1a; 最后的文档地址为&#xff1a;Docker安装 | 达梦技术文档 二、dcoker安装 docker基本按照官网来就行&#xff0c;点击相应的链接下载镜像包。 复制到linux中&#x…

基于卷积神经网络和客源注意力机制的OD客流预测模型

文章信息 论文题目为《An origin–destination passenger flow prediction system based on convolutional neural network and passenger source-based attention mechanism》&#xff0c;该文于2023年发表于Expert Systems With Applications期刊上。文章提出一种基于乘客源注…

成都瀚网科技有限公司抖音带货正规

随着互联网的蓬勃发展&#xff0c;越来越多的公司开始利用网络平台进行产品销售。其中&#xff0c;抖音作为一款广受欢迎的短视频平台&#xff0c;已经成为众多商家眼中的“香饽饽”。在这场电商狂欢中&#xff0c;成都瀚网科技有限公司&#xff08;以下简称“瀚网科技”&#…

果园自主跟随碎枝机器人

开发背景 农业扶贫项目—— 开发一款适用于猕猴桃果园的跟随碎枝机器人。 在猕猴桃的种植培育过程中&#xff0c;一项非常重要的环节便是剪枝&#xff0c;通常有冬剪和夏剪。以往果农剪完枝条后要将散落于地的枝条归拢后统一粉碎还田。这需要专门收集地面上的枝条并将其归拢到…

Ubuntu环境下为串口设置别名

本文介绍Ubuntu环境下为串口设置别名。 Ubuntu环境下&#xff0c;有时候开发调试会使用到USB转串口&#xff0c;本文介绍在不同使用场景下为串口设置别名的方法。主要分为绑定设备ID和绑定USB端口号。 1.绑定设备ID 绑定设备ID适用于USB转串口的设备ID唯一的情况&#xff0c…

线性代数本质系列(一)向量,线性组合,线性相关,矩阵

本系列文章将从下面不同角度解析线性代数的本质&#xff0c;本文是本系列第一篇 向量究竟是什么&#xff1f; 向量的线性组合&#xff0c;基与线性相关 矩阵与线性相关 矩阵乘法与线性变换 三维空间中的线性变换 行列式 逆矩阵&#xff0c;列空间&#xff0c;秩与零空间 克莱姆…

MySQL的SQL预编译及防SQL注入

文章目录 1 SQL语句的执行处理1.1 即时SQL1.2 预处理SQL1.2.1 预编译SQL的实现步骤1.2.2 预编译SQL的C使用举例1.2.3 MYSQL_BIND()函数中的参数类型&#xff1a; 2 SQL注入2.1 什么是SQL注入2.2 如何防止SQL注入 1 SQL语句的执行处理 SQL的执行可大致分为下面两种模式&#xf…

Linux环境实现mysql所在服务器定时同步数据文件到备份服务器(异地容灾备份场景)

目录 概述 1、建立ssh连接 1.1、操作mysql所在服务器 1.2、操作备份文件服务器 2、创建脚本实现备份以及传输 3、配置定时任务 概述 应对异地容灾备份场景&#xff0c;mysql所在服务器和本分服务器需要建立ssh连接&#xff0c;每天mysql服务器通过定时任务执行脚本&…

微信小程序授权登陆 getUserProfile

目录 前言 步骤&#xff1a; 示例代码: 获取用户信息的接口变化历史: 注意事项&#xff1a; 前言 在微信小程序中&#xff0c;你可以使用 getUserProfile 接口来获取用户的个人信息&#xff0c;并进行授权登录。以下是使用 getUserProfile 的步骤&#xff1a; 小程序发了…

ChatGPT 4 OpenAI 数据分析动态可视化案例

数据分析可视化是一种将原始数据转化为图形或图像的方法,使得数据更易理解和解读。这种方法能够帮助我们更清楚地看到数据中的模式、趋势和关联性,从而更好地理解数据,并据此做出决策。 数据分析可视化的一些常见形式包括: 1. 折线图:常用于展示数据随时间的变化趋势。 …

MATLAB | 官方举办的动图绘制大赛 | 第一周赛情回顾

嘿真的又是很久没见了&#xff0c;最近确实有点非常很特别小忙&#xff0c;今天带来一下MATHWORKS官方举办的迷你黑客大赛第三期(MATLAB Flipbook Mini Hack)的最新进展&#xff01;&#xff01;目前比赛已经刚好进行了一周&#xff0c;前两届都要求提交280个字符内的代码来生成…

一个关于jdbc操作mysql和java基础练手的通讯录管理系统小项目

首先 : 整个项目的项目结构为 : 1.第一步先导入数据库的驱动&#xff0c;我的mysql数据库是8.0以上版本&#xff0c;然后导入的驱动就是8.0.16版本的jar包&#xff1b; 1.JdbcBase : JDBC基础操作封装成了JdbcBase类,在里面先静态定义了数据库连接对象和DQL查询结果&#x…

C++阶段复习‘‘‘‘总结?【4w字。。。】

文章目录 前言类和对象C类定义和对象定义类成员函数C 类访问修饰符公有&#xff08;public&#xff09;成员私有&#xff08;private&#xff09;成员受保护&#xff08;protected&#xff09;成员 继承中的特点类的构造函数和析构函数 友元函数内联函数this指针指向类的指针类…

蓝桥杯 冒泡排序

冒泡排序的思想 冒泡排序的思想是每次将最大的一下一下移动到最右边&#xff0c;然后将最右边这个确定下来。 再来确定第二大的&#xff0c;再确定第三大的… 对于数组a[n]&#xff0c;具体来说&#xff0c;每次确定操作就是从左往右扫描&#xff0c;如果a[i]>a[i1],我们将…

Prim算法(C++)

目录 介绍&#xff1a; 代码&#xff1a; 结果&#xff1a; 介绍&#xff1a; Prim算法是一种用于解决最小生成树问题的贪心算法。该算法的主要思想是从一个顶点开始&#xff0c;不断向图中添加边&#xff0c;直到构成一棵包含所有顶点的生成树&#xff0c;使得树的边权之…

VSCode 好用的插件分享

文章目录 Introlistcode runner 【在文本编辑器中编辑好各类语言的源代码&#xff0c;然后一键运行】gitlens - 【git提交信息即时查看&#xff0c;类似IDEA中的 show annotation】还有更多&#xff0c;会日常补充。 Intro 大四毕业前&#xff0c;我只有一台dell latitude 455…

Vue 小黑记事本组件板

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

Vue3 + Three.js + gltf-pipeline大型园区场景渲染与3D业务

在非使用unity作为3D渲染方案的前提下&#xff0c;对与目前web开发者比较友好的除了canvas场景需要的2D babylon.js&#xff0c;fabric.js, Three.js是目前针对于jsWeb用户最直接且比较友好的3D引擎方案了。 准备工作&#xff1a; 1.明确需要用的场景方案都有那些&#xff0c;模…