一.函数
1.为完成某一功能的程序指令的集合,称为函数。在Go中,函数分为:自定义函数和系统函数
2.基本语法:
func 函数名 (形参列表)(返回值类型列表){
执行语句......
return 返回值列表
}
(1)形参列表:表示函数的输入
(2)函数中的语句:表示为了实现某一功能代码块
(3)函数可以有返回值,也可以没有
案例:
二.包
1.包的本质实际上就是创建不同的文件夹,来存放程序文件
基本概念:go的每一个文件都属于一个包的,也就是说go是以包的形式来管理文件和项目目录 结构的。
原理图:
2.作用:(1)区分相同名字的函数、变量标识符等
(2)当程序文件很多时,可以很好管理项目
(3)控制函数、变量等访问范围,即作用域
3.相关说明:(1)打包基本语法
package 包的名字
(2)引入包的基本语法
import " 路径 "
案例:
细节说明:(1)在给一个文件打包时,该包对应一个文件夹,比如这里的utils文件夹对应的包名就是 utils,文件的包名通常和文件所在的文件夹名一致,一般为小写字母。
(2)当一个文件要使用其它包函数或变量时,需要先引入对应的包 import "包名"
(3)package指令在文件第一行,然后是import指令
(4)在import包时,路径从$PATH的src下开始,不用带src,编译器会自动从src下开始引入
(5)为了让其他包的文件,可以访问到本包的函数,则该函数名的首字母大写,类似其它语 言的public,这样才能垮包访问。比如utils.go
(6)在访问其他包函数时,语法是:包名.函数名
(7)如果包名较长,Go支持给包取别名,但取别名后原来的包就不能,例如:原来调用方式为utils.Cal,取别名为utul后,调用方式为util.Cal
(8)同一个包里面不能有相同的函数(也不能有相同的全局变量名)
(9)如果你要编译成一个可执行程序文件,就需要将这个包声明为main,即package main,这个是语法规范,如果你写的只是一个库,包名可以自定义
三.函数-调用机制
函数-调用过程图:
说明(1):在调用一个函数时,会给该函数分配一个新的空间,编译器会通过自身的处理让这个新的 空间和其他栈的空间区分开来
(2)在每个函数对应的栈中,数据空间是空间的,不会混淆
(3)当一个函数调用完毕或者使用完毕后,程序会销毁这个数对应的栈空间
return语句
GO函数支持返回多个值,这一点是其它编程语言没有的
func 函数名(形参列表) (返回值类型列表){
语句
return返回值列表
}
说明(1)如果返回多个值时,在接收时,希望忽略某个返回值,则使用_符号表示站位忽略
(2)如果返回值只有一个,(返回值类型列表) 可以不写()
三.函数-递归调用
一个函数在函数体内又调用了函数本身,我们称为递归调用
函数递归需要遵守的重要原则:
(1)执行一个函数时,就创建一个新的受保护的独立空间(新函数栈)
(2)函数的局部变量是独立的,不会相互影响
(3)递归必须向退出递归的条件逼近,否则就是无限递归
(4)当一个函数执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁。同时当 函数执行完毕或者返回时,该函数本身也会被系统销毁。
案例:1.用递归方式求斐波那契数。
2.求函数值,已知f(1)=3;f(n)=2*f(n-1)+1,用递归方式求出f(n)的值:
3.猴子吃桃问题,有一堆桃子,猴子每天吃其中的一半并多吃一个,第十天时就剩一个了。
函数的注意事项:
1.函数的形参列表可以是多个,返回值列表也可是多个.
2.形参列表和返回值列表的数据类型可以是值类型和引用类型.
3.函数的命名遵循标识符命名规范,首字母不能是数字,首字母大写该函数可以被本包文件和其他包文件使用,类似public,首字母小写,只能被本包文件使用,其他包文件不能使用,类似private。
4.函数中的变量是局部的,函数外不生效
5.基本数据类型和数组默认是值传递的,即进行值拷贝。在函数类修改,不会影响到原来的值。
6.如果希望函数类的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量
7.Go函数不支持重载 //会报错,会报函数重复定义
8.在Go中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量了,通过该变量可以对函数调用。
9.函数既然是一种数据类型,因此在Go中,函数可以作为形参,并且调用
10.为了简化数据类型定义,Go支持自定义数据类型
基本语法:type 自定义数据类型名 数据类型 // 相当于一个别名
案例:type mylnt int //这时mytln就等价 int 来使用了
案例:type mySum func (int,int) int //这时mySum就等价一个函数类型func(int,int) int
//在Go中 myint和int虽然都是int类型,但是go认为这两者是两个类型,不一样,赋值的时候会 报错,要转换
11.支持对函数返回值命名
12.使用_标识符,忽略返回值
13.Go支持可变参数
说明:
(1)args是slice,通过args[index] 可以访问到各个值.
(2)案例演示:编写一个函数sum,可以求出1到多个int的和
(3)如果一个函数的形参列表中有可变参数,则可变参数需要放在形参列表最后
案例:编写一个函数swap(n1 *int,n2 *int)可以交换n1和n2 的值
四.init函数
每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被Go运行框架调用,也就是说init会在main函数前被调用,通常可以在init函数中完成初始化工作。
注意:(1)如果一个文件同时包含全局变量定义,init函数和main函数,则执行的流程是变量定义->init函数->main函数
(2)init函数最主要的作用,就是完成一些初始化的工作,比如下面的案例:
五.匿名函数
1.Go支持匿名函数,如果我们某个函数只是希望使用一次,可以考虑使用匿名函数,匿名函数也 可以实现多次调用
2.匿名函数使用方法:
(1)在定义匿名函数时就直接调用,这种方式匿名函数就只能调用一次
(2)将匿名函数赋给一个变量(函数变量),再通过该变量来调用匿名函数
3.全局匿名函数:
如果将匿名函数赋给一个全局变量,那么这个匿名函数,就成为一个全局匿名函数,可以在 程序有效
六.闭包
1.闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)
对上面代码的说明和总结
(1)AddUpper 是一个函数,返回的数据类型是fun (int) int
(2)闭包的说明
返回的是一个匿名函数,但这个匿名函数引用到函数外的n,因此这个匿名函数就和n形成一个整体,构成闭包。
(3)闭包是类,函数是操作,n是字段
(4)当我们反复的调用f函数时,因为n是初始化一次,因此每调用一次就进行累加.
2.闭包实践:
编写一个程序,具体要求如下
(1)编写一个函数makeSuffix(suffix string)可以接收一个文件后缀名(比如.jpg),并返回一个闭包
(2)调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如.jpg),则返回 文件名.jpg,如果已经有.jpg后缀,则返回原文件名
(3)要求使用闭包的方式完成
(4)string.HasSuffix
代码说明:
1.返回的匿名函数和 makeSuffix(suffix string)的suffix变量组合成一个闭包,因为返回的函数引用到suffix这个变量
七.函数中-defer
在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等),为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer(延时机制)
defer的细节说明:
(1)当go执行到一个defer时,不会立即执行defer后的语句,而是将defer后的语句压入到一个栈 中,然后继续执行函数下一个语句
(2)当函数执行完毕后,在从defer栈中,依次从栈顶取出语句执行
(3)在defer将语句放入到栈时,也会将相关的值拷贝同时入栈
八.函数参数的传递方式
1.值传递和引用传递
ps:不管是值传递还是引用传递,传递给函数大的都是变量的副本,不同的是,值传递的是值的拷贝,引用传递还是地址的拷贝,一般来说地址拷贝效率高,因为数据量小,而值拷贝决定拷贝的数据大小,数据越大,效率越低
2.值类型和引用类型
1.值类型:基本数据类型int系列,float系列,bool,string、数组和结构体struct
2.引用类型:指针、slice切片、map、管道chan、interface等都是引用类型
九.变量作用域
1.函数内部声明/定义的变量叫局部变量,作用域仅限于函数内部
2.函数外部声明/定义的变量叫全局变量,作用域在整个包都有效,如果其首字母为大写,则作用域在整个程序有效
3.如果变量是在一个代码块,比如for/if中,那么这个变量的作用域就在该代码块