Golang函数相关
函数定义
Go语言中定义函数使用func关键字,具体格式如下:
func 函数名(参数)(返回值){
函数体
}
其中:
函数名:由字母、数字、下划线组成。但函数名的第一个字母不能是数字。在同一个包内,函数名也称不能重名(包的概念详见后文)。
参数:参数由参数变量和参数变量的类型组成,多个参数之间使用,分隔。
返回值:返回值由返回值变量和其变量类型组成,也可以只写返回值的类型,多个返回值必须用()包裹,并用,分隔。
函数体:实现指定功能的代码块。
函数类型变量
// 声明函数类型
type fType func(int,int) int
func add(a,b int) int {
return a + b
}
func sub(a, b int) int {
return a - b
}
// 函数类型变量使用测试
func TestFunctionType(t *testing.T) {
var c fType
c = add
fmt.Println(c(1,2))
c = sub
fmt.Println(c(1,2))
}
函数作为参数传递
// 函数作为参数
func CallFunctionParameter(a,b int,c func(int,int) int) int {
return c(a,b)
}
// 参数传递函数测试
func TestFunctionParameter(t *testing.T) {
fmt.Println(CallFunctionParameter(1,2,add))
fmt.Println(CallFunctionParameter(1,2,sub))
}
函数作为返回值
func do(s string) (func(int,int) int,error) {
switch s {
case "+":
return add,nil
case "-":
return sub,nil
default:
err := errors.New("无法识别的操作符")
return nil,err
}
}
// 函数作为返回值测试
func TestDo(t *testing.T) {
c,e := do("*")
if e != nil {
fmt.Println(e.Error())
return
}
fmt.Println(c(1,2))
}
匿名函数
// 匿名函数 用于回调函数和闭包
func TestAnonymous(t *testing.T) {
add := func(a,b int) {
fmt.Println(a +b)
}
add(1,2)
func(a,b int) {
fmt.Println(a+b)
}(1,2)
}
闭包函数
// 闭包 闭包=函数+引用环境
func adder() func(int) int {
var x int
return func(i int) int {
x += i
return x
}
}
// 闭包 进阶1
func adder2(x int) func(int) int {
return func(y int) int {
x += y
return x
}
}
// 闭包进阶2 为文件名添加后缀
func makeSuffixFunc(suffix string) func(string) string {
return func(name string) string {
// 判断是否不存在后缀
if !strings.HasSuffix(name,suffix) {
return name + suffix
}
return name
}
}
// 闭包进阶3 对参数进行运算
func calc(base int) (func(int) int,func(int) int) {
add := func(i int) int {
base += i
return base
}
sub := func(i int) int {
base -= i
return base
}
return add,sub
}
// 闭包函数测试
func TestClosure(t *testing.T) {
var f = adder()
fmt.Println(f(10)) // 10
fmt.Println(f(20)) // 30
fmt.Println(f(30)) // 60
f1 := adder()
fmt.Println(f1(30)) // 30
// 进阶1
f3 := adder2(10)
fmt.Println(f3(10)) // 20
fmt.Println(f3(20)) // 40
// 进阶2
jpgFunc := makeSuffixFunc(".jpg")
txtFunc := makeSuffixFunc(".txt")
fmt.Println(jpgFunc("test")) // test.jpg
fmt.Println(txtFunc("test")) // test.txt
fmt.Println(jpgFunc("test.jpg")) // test.jpg
// 进阶3 f4 add f5 sub
f4,f5 := calc(100)
fmt.Println(f4(10),f5(5)) // 110 105
fmt.Println(f4(5),f5(10)) // 110 100
}
Defer函数
// defer 采用压栈 后入先出的方式 在函数返回时按照语句顺序逆序执行
// 具有延迟调用特性,所以非常方便处理资源释放问题,如 资源清理、文件关闭、解锁及记录时间等
// 在GO语言函数中return语句在底层并不是原子操作,分为赋值和RET指令两步,
// defer语句执行时机在返回值赋值操作后,RET指令执行前
func TestDefer(t *testing.T) {
fmt.Println("Start")
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
fmt.Println("End")
}
func f1() int {
x := 5
defer func() {
// defer在函数返回值赋值之后执行,所以无法改变返回值
x++
}()
return x
}
func f2() (x int) {
defer func() {
// defer操作直接修改了返回值变量,所以5变成6
x++
}()
return 5
}
func f3() (y int) {
x := 5
defer func() {
// defer在函数返回值赋值之后执行,函数返回时已经将x=5赋值给了返回值
// 如果将x++ 修改为 y++ 可以改变返回值
x++
}()
return x
}
func f4() (x int) {
defer func(x int) {
// defer在函数返回值赋值之后执行,函数返回时5赋值给了返回值
// 虽然此处貌似操作了返回变量x,但实际上操作的是defer参数中的x
x++
// 打印结果为多少?
// 答案是 1。 defer函数的参数在注册时就会执行运算确定值,所以刚开始x初始值为0。
fmt.Println(x)
}(x)
return 5
}
// Defer测试
func TestDeferReturn(t *testing.T) {
fmt.Println(f1()) // 5
fmt.Println(f2()) // 6
fmt.Println(f3()) // 5
fmt.Println(f4()) // 5
}
func calcDefer(index string, a, b int) int {
ret := a + b
fmt.Println(index, a, b, ret)
return ret
}
// Defer输出测试
func TestCalcDefer(t *testing.T) {
x := 1
y := 2
defer calcDefer("AA", x, calcDefer("A", x, y))
x = 10
defer calcDefer("BB", x, calcDefer("B", x, y))
y = 20
// 输出结果:
// A 1 2 3
// B 10 2 12
// BB 10 12 22
// AA 1 3 4
// 执行"A",参数x,y分别为1,2,所以输出 A 1 2 3,返回结果3作为参数传递给"AA"
// 执行"B",参数x,y分别为10,2,所以输出 B 10,2,12,返回参数12作为参数传递给"BB"
// 函数执行结束触发延迟函数,defer逆序执行
// 输出"BB" BB 10 12 22
// 输出"AA" AA 1 3 4
}
Panic/Recover
// panic可以在任何地方引发,但recover只有在defer调用的函数中有效
// recover()必须搭配defer使用
// defer一定要在可能引发panic语句之前定义
func panicTest() {
defer func() {
err := recover()
if err != nil {
fmt.Println("recover:",err)
}
}()
panic("Panic")
}
func TestPanicDemo(t *testing.T) {
panicTest()
}
练习题:分金币
func TestDispathCion(t *testing.T) {
var (
coins = 50
users = []string{
"Matthew", "Sarah", "Augustus", "Heidi", "Emilie", "Peter", "Giana", "Adriano", "Aaron", "Elizabeth",
}
//map集合[string]:int 字符串:整型
//初始化一个集合,cap = users切片的长度
distribution = make(map[string]int, len(users))
)
for _,value := range users{
// 用户姓名统一转化小写
name := strings.ToLower(value)
distribution[value] += 1 * strings.Count(name,"e")
distribution[value] += 2 * strings.Count(name,"i")
distribution[value] += 3 * strings.Count(name,"o")
distribution[value] += 4 * strings.Count(name,"u")
}
for k,v := range distribution {
fmt.Println(k,"获得",v,"枚硬币。")
coins-=v
}
fmt.Println("剩下:",coins,"枚硬币。")
}