Golang函数,包笔记

一、函数

函数的基本概念

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

函数的基本语法

Golang函数,包笔记_第1张图片

快速入门案例

Golang函数,包笔记_第2张图片

Golang函数,包笔记_第3张图片

二、包

为什么需要包?

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

包的原理图

包的本质实际上就是创建不同的文件夹,来存放程序文件。 画图说明一下包的原理
Golang函数,包笔记_第4张图片
Golang函数,包笔记_第5张图片

包的基本概念

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

包的三大作用

  • 区分相同名字的函数、变量等标识符
  • 当程序文件很多时,可以很好的管理项目
  • 控制函数、变量等访问范围,即作用域

包的相关说明

打包基本语法
package 包名

引入包的基本语法
import “包的路径”

包使用的快速入门

包快速入门-Go 相互调用函数,我们将 func Cal 定义到文件 utils.go , 将 utils.go 放到一个包中,当 其它文件需要使用到 utils.go 的方法时,可以 import 该包,就可以使用了. 【为演示:新建项目目录结 构】

代码演示:
Golang函数,包笔记_第6张图片
utils.go 文件
Golang函数,包笔记_第7张图片
main.go 文件
Golang函数,包笔记_第8张图片

包使用的注意事项和细节讨论

  1. 在给一个文件打包时,该包对应一个文件夹,比如这里的 utils 文件夹对应的包名就是 utils,
    文件的包名通常和文件所在的文件夹名一致,一般为小写字母。

  2. 当一个文件要使用其它包函数或变量时,需要先引入对应的包

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

    package 指令在 文件第一行,然后是 import 指令。

    在 import 包时,路径从 $GOPATH 的 src 下开始,不用带 src , 编译器会自动从 src 下开始引入

  3. 为了让其它包的文件,可以访问到本包的函数,则该函数名的首字母需要大写,类似其它语言 的 public ,这样才能跨包访问。比如 utils.go 的
    Golang函数,包笔记_第9张图片

  4. 在访问其它包函数,变量时,其语法是 包名.函数名, 比如这里的 main.go 文件中
    在这里插入图片描述

  5. 如果包名较长,Go 支持给包取别名, 注意细节:取别名后,原来的包名就不能使用了
    Golang函数,包笔记_第10张图片
    说明: 如果给包取了别名,则需要使用别名来访问该包的函数和变量。

  6. 在同一包下,不能有相同的函数名(也不能有相同的全局变量名),否则报重复定义

  7. 如果你要编译成一个可执行程序文件,就需要将这个包声明为 main , 即 package main .这个就 是一个语法规范,如果你是写一个库 ,包名可以自定义

Golang函数,包笔记_第11张图片

函数的调用机制

函数-调用过程

介绍:为了让大家更好的理解函数调用过程, 看两个案例,并画出示意图,这个很重要

  1. 传入一个数+1
    Golang函数,包笔记_第12张图片
    对上图说明
    (1) 在调用一个函数时,会给该函数分配一个新的空间,编译器会通过自身的处理让这个新的空间 和其它的栈的空间区分开来
    (2) 在每个函数对应的栈中,数据空间是独立的,不会混淆
    (3) 当一个函数调用完毕(执行完毕)后,程序会销毁这个函数对应的栈空间。

  2. 计算两个数,并返回
    Golang函数,包笔记_第13张图片
    Golang函数,包笔记_第14张图片

return 语句

基本语法和说明
Golang函数,包笔记_第15张图片
案例演示 1
请编写要给函数,可以计算两个数的和和差,并返回结果。
Golang函数,包笔记_第16张图片
Golang函数,包笔记_第17张图片
案例演示 2

一个细节说明: 希望忽略某个返回值,则使用 _ 符号表示占位忽略
在这里插入图片描述

函数的递归调

递归调用快速入门

Golang函数,包笔记_第18张图片
上面代码的分析图:
Golang函数,包笔记_第19张图片

递归调用的总结

函数递归需要遵守的重要原则:

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

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

  1. 函数的形参列表可以是多个,返回值列表也可以是多个。

  2. 形参列表和返回值列表的数据类型可以是值类型和引用类型。

  3. 函数的命名遵循标识符命名规范,首字母不能是数字,首字母大写该函数可以被本包文件和其 它包文件使用,类似 public , 首字母小写,只能被本包文件使用,其它包文件不能使用,类似 privat

  4. 函数中的变量是局部的,函数外不生效【案例说明】
    Golang函数,包笔记_第20张图片

  5. 基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内修改,不会影响到原来的值。
    Golang函数,包笔记_第21张图片

  6. 如果希望函数内的变量能修改函数外的变量(指的是默认以值传递的方式的数据类型),可以传 入变量的地址&,函数内以指针的方式操作变量。从效果上看类似引用 。
    Golang函数,包笔记_第22张图片

  7. Go 函数不支持函数重载
    Golang函数,包笔记_第23张图片

  8. 在 Go 中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量 了。通过该变量可以对函数调用
    Golang函数,包笔记_第24张图片

  9. 函数既然是一种数据类型,因此在 Go 中,函数可以作为形参,并且调用
    在这里插入图片描述
    Golang函数,包笔记_第25张图片

  10. 为了简化数据类型定义,Go 支持自定义数据类型
    基本语法:type 自定义数据类型名 数据类型 // 理解: 相当于一个别名 案例:type myInt int // 这时 myInt 就等价 int 来使用了.

案例:type mySum func (int, int) int // 这时 mySum 就等价 一个 函数类型 func (int, int) int

举例说明自定义数据类型的使用:
Golang函数,包笔记_第26张图片
Golang函数,包笔记_第27张图片
在这里插入图片描述

  1. 支持对函数返回值命名
    Golang函数,包笔记_第28张图片
    在这里插入图片描述

  2. 使用 _ 标识符,忽略返回值
    Golang函数,包笔记_第29张图片

  3. Go 支持可变参数
    Golang函数,包笔记_第30张图片
    代码演示:
    Golang函数,包笔记_第31张图片在这里插入图片描述

init 函数

基本介绍

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

案例说明:
Golang函数,包笔记_第32张图片
在这里插入图片描述

inti 函数的注意事项和细节

  1. 如果一个文件同时包含全局变量定义,init 函数和 main 函数,则执行的流程全局变量定义->init 函数->main 函数
    Golang函数,包笔记_第33张图片
  2. init 函数最主要的作用,就是完成一些初始化的工作,比如下面的案例
    Golang函数,包笔记_第34张图片
    Golang函数,包笔记_第35张图片
  3. 细节说明: 面试题:案例如果 main.go 和 utils.go 都含有 变量定义,init 函数时,执行的流程 又是怎么样的呢?
    Golang函数,包笔记_第36张图片

匿名函数

介绍

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

匿名函数使用方式 1

在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次。 【案例演示】
Golang函数,包笔记_第37张图片
匿名函数使用方式 2

将匿名函数赋给一个变量(函数变量),再通过该变量来调用匿名函数 【案例演示】
Golang函数,包笔记_第38张图片

全局匿名函数

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

闭包

介绍

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

案例演示:

//累加器
func AddUpper() func (int) int {
     
	var n int = 10
	return func (x int) int {
     
		n = n + x
		return n
	}
}

func main() {
     

	f := AddUpper()
	fmt.Println(f(1))//11
	fmt.Println(f(2))//13
	fmt.Println(f(3))//16
}

对上面代码的说明和总结

  1. AddUpper 是一个函数,返回的数据类型是 fun (int) int 2) 闭包的说明
	var n int = 10
	return func (x int) int {
     
		n = n + x
		return n
	}

返回的是一个匿名函数, 但是这个匿名函数引用到函数外的 n ,因此这个匿名函数就和 n 形成一 个整体,构成闭包。

  1. 大家可以这样理解: 闭包是类, 函数是操作,n 是字段。函数和它使用到 n 构成闭包。
  2. 当我们反复的调用 f 函数时,因为 n 是初始化一次,因此每调用一次就进行累计。
  3. 我们要搞清楚闭包的关键,就是要分析出返回的函数它使用(引用)到哪些变量,因为函数和它引 用到的变量共同构成闭包。
  4. 对上面代码的一个修改,加深对闭包的理解

Golang函数,包笔记_第39张图片

闭包的最佳实践

请编写一个程序,具体要求如下

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

}
func main() {
     
	//返回一个闭包
	f := makeSuffix(".jpg")
	
	fmt.Println(f("winter"))
}

上面代码的总结和说明:

  1. 返回的匿名函数和 makeSuffix (suffix string) 的 suffix 变量 组合成一个闭包,因为 返回的函数引用 到 suffix 这个变量
  2. 我们体会一下闭包的好处,如果使用传统的方法,也可以轻松实现这个功能,但是传统方法需要每 次都传入 后缀名,比如 .jpg ,而闭包因为可以保留上次引用的某个值,所以我们传入一次就可以反复 使用。大家可以仔细的体会一把!

函数的 defer

为什么需要 defer

在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等) ,为了在函数执行完 毕后,及时的释放资源,Go 的设计者提供 defer (延时机制)。

快速入门案例
Golang函数,包笔记_第40张图片
执行后,输出的结果:
Golang函数,包笔记_第41张图片
defer 的注意事项和细节

  1. 当 go 执行到一个 defer 时,不会立即执行 defer 后的语句,而是将 defer 后的语句压入到一个栈 中[我为了讲课方便,暂时称该栈为 defer 栈], 然后继续执行函数下一个语句。
  2. 当函数执行完毕后,在从 defer 栈中,依次从栈顶取出语句执行(注:遵守栈 先入后出的机制),所以同学们看到前面案例输出的顺序。
  3. 在 defer 将语句放入到栈时,也会将相关的值拷贝同时入栈。请看一段代码:
    Golang函数,包笔记_第42张图片
    上面代码输出的结果如下:
    Golang函数,包笔记_第43张图片

6.19.4defer 的最佳实践

defer 最主要的价值是在,当函数执行完毕后,可以及时的释放函数创建的资源。看下模拟代码。。
Golang函数,包笔记_第44张图片
说明

  1. 在 golang 编程中的通常做法是,创建资源后,比如(打开了文件,获取了数据库的链接,或者是 锁资源), 可以执行 defer file.Close() defer connect.Close()
  2. 在 defer 后,可以继续使用创建资源.
  3. 当函数完毕后,系统会依次从 defer 栈中,取出语句,关闭资源.
  4. 这种机制,非常简洁,程序员不用再为在什么时机关闭资源而烦心。

函数参数传递方式

基本介绍

我们在讲解函数注意事项和使用细节时,已经讲过值类型和引用类型了,这里我们再系统总结一 下,因为这是重难点,值类型参数默认就是值传递,而引用类型参数默认就是引用传递。

两种传递方式

  1. 值传递
  2. 引用传递 其实,不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是,值传递的是值的拷贝,引用传递的是地址的拷贝,一般来说,地址拷贝效率高,因为数据量小,而值拷贝决定拷贝的 数据大小,数据越大,效率越低。

值类型和引用类型
1)值类型:基本数据类型 int 系列, float 系列, bool, string 、数组和结构体 struct
2) 引用类型:指针、slice 切片、map、管道 chan、interface 等都是引用类型

值传递和引用传递使用特点
Golang函数,包笔记_第45张图片
3) 如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操 作变量。从效果上看类似引用 。这个案例在前面详解函数使用注意事项的
Golang函数,包笔记_第46张图片

变量作用域

  1. 函数内部声明/定义的变量叫局部变量,作用域仅限于函数内部
    Golang函数,包笔记_第47张图片
  2. 函数外部声明/定义的变量叫全局变量,作用域在整个包都有效,如果其首字母为大写,则作用 域在整个程序有效
    Golang函数,包笔记_第48张图片
  3. 如果变量是在一个代码块,比如 for / if 中,那么这个变量的的作用域就在该代码块

Golang函数,包笔记_第49张图片
变量作用域的课堂练习
Golang函数,包笔记_第50张图片

	输出的结果是: tom tom jack tom

Golang函数,包笔记_第51张图片

字符串常用的系统函数

说明:字符串在我们程序开发中,使用的是非常多的,常用的函数需要同学们掌握[带看手册或者 官方编程指南]:

  1. 统计字符串的长度,按字节 len(str)
    Golang函数,包笔记_第52张图片

  2. 字符串遍历,同时处理有中文的问题 r := []rune(str)
    Golang函数,包笔记_第53张图片

  3. 字符串转整数: n, err := strconv.Atoi(“12”)
    Golang函数,包笔记_第54张图片

  4. 整数转字符串 str = strconv.Itoa(12345)
    在这里插入图片描述

  5. 字符串 转 []byte: var bytes = []byte(“hello go”)
    在这里插入图片描述

  6. []byte 转 字符串: str = string([]byte{97, 98, 99})
    在这里插入图片描述

  7. 10 进制转 2, 8, 16 进制: str = strconv.FormatInt(123, 2) // 2-> 8 , 16
    Golang函数,包笔记_第55张图片

  8. 查找子串是否在指定的字符串中: strings.Contains(“seafood”, “foo”) //true
    在这里插入图片描述

  9. 统计一个字符串有几个指定的子串 : strings.Count(“ceheese”, “e”) //4
    在这里插入图片描述

  10. 不区分大小写的字符串比 较(== 是区分字母大小写的 ): fmt.Println(strings.EqualFold(“abc”, “Abc”)) // true
    Golang函数,包笔记_第56张图片

  11. 返回子串在字符串第一次出现的 index 值,如果没有返回-1 : strings.Index(“NLT_abc”, “abc”) // 4
    Golang函数,包笔记_第57张图片

  12. 返回子串在字符串最后一次出现的 index,如没有返回-1 : strings.LastIndex(“go golang”, “go”)
    Golang函数,包笔记_第58张图片

  13. 将指定的子串替换成 另外一个子串: strings.Replace(“go go hello”, “go”, “go 语言”, n) n 可以指 定你希望替换几个,如果 n=-1 表示全部替换

  14. 按 照 指 定 的 某 个 字 符 , 为 分 割 标 识 , 将 一 个 字 符 串 拆 分 成 生成的所有片段组成的切片 :strings.Split(“hello,wrold,ok”, “,”)
    Golang函数,包笔记_第59张图片

  15. 将字符串的字母进行大小写的转换: strings.ToLower(“Go”) // go strings.ToUpper(“Go”) // GO
    Golang函数,包笔记_第60张图片

  16. 将字符串左右两边的空格去掉: strings.TrimSpace(" tn a lone gopher ntrn ")
    在这里插入图片描述

  17. 将字符串左右两边指定的字符去掉 : strings.Trim("! hello! “, " !”) // [“hello”] //将左右两边 !
    和 " "去掉
    在这里插入图片描述

  18. 将字符串左边指定的字符去掉 : strings.TrimLeft("! hello! “, " !”) // [“hello”] //将左边 ! 和 " "去掉

  19. 将字符串右边指定的字符去掉 : strings.TrimRight("! hello! “, " !”) // [“hello”] //将右边 ! 和 " "去掉

  20. 判断字符串是否以指定的字符串开头: strings.HasPrefix(“ftp://192.168.10.1”, “ftp”) // true
    Golang函数,包笔记_第61张图片

  21. 判断字符串是否以指定的字符串结束: strings.HasSuffix(“NLT_abc.jpg”, “abc”) //false

6.24 时间和日期相关函数

6.24.1 基本的介绍

说明:在编程中,程序员会经常使用到日期相关的函数,比如:统计某段代码执行花费的时间等 等。

  1. 时间和日期相关函数,需要导入 time 包
    Golang函数,包笔记_第62张图片

  2. time.Time 类型,用于表示时间
    Golang函数,包笔记_第63张图片

  3. 如何获取到其它的日期信息
    Golang函数,包笔记_第64张图片

  4. 格式化日期时间
    方式 1: 就是使用 Printf 或者 SPrintf
    Golang函数,包笔记_第65张图片
    方式二: 使用 time.Format() 方法完成:在这里插入图片描述
    对上面代码的说明:
    “2006/01/02 15:04:05” 这个字符串的各个数字是固定的,必须是这样写。
    “2006/01/02 15:04:05” 这个字符串各个数字可以自由的组合,这样可以按程序需求来返回时间 和日期

  5. 时间的常量

     const (
     Nanosecond	Duration = 1 //纳秒
     Microsecond	= 1000 * Nanosecond	//微秒
     Millisecond	= 1000 * Microsecond //毫秒 Second		= 1000 * Millisecond //秒
     Minute	= 60 * Second //分钟
     Hour	= 60 * Minute //小时
     )
    

常量的作用:在程序中可用于获取指定时间单位的时间,比如想得到 100 毫秒
100 * time. Millisecond

  1. 结合 Sleep 来使用一下时间常量
    Golang函数,包笔记_第66张图片
  2. time 的 Unix 和 UnixNano 的方法
    Golang函数,包笔记_第67张图片在这里插入图片描述

内置函数

说明:

Golang 设计者为了编程方便,提供了一些函数,这些函数可以直接使用,我们称为 Go 的内置函 数。文档:https://studygolang.com/pkgdoc -> builtin

  1. len:用来求长度,比如 string、array、slice、map、channel
  2. new:用来分配内存,主要用来分配值类型,比如 int、float32,struct…返回的是指针 举例说明 new 的使用:
    Golang函数,包笔记_第68张图片
  3. make:用来分配内存,主要用来分配引用类型,比如 channel、map、slice。这个我们后面讲解。

你可能感兴趣的:(Golang,golang)