Loading

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,"枚硬币。")
}

参考来源

posted @ 2021-11-17 08:31  励码万言  阅读(39)  评论(0编辑  收藏  举报