func 函数名(参数)(返回值){
    函数体
}

函数的可变参数和多返回值

package main

import "fmt"

//普通参数声明
func intSum(x, y int64) int64 {
	return x + y
}

/*
可变参数的函数:
可变参数需作为函数的最后一个参数,通过在参数名后加 ... 来标识
并且下面的函数中 写成 x, y... int64编译是报错的
*/
func intSum2(x int64, y ...int64) int64 {
	var res int64
	var i int64
	for i = 0; i < x; i++ {
		res += y[i]
	}
	return res
}

//多个返回值的函数,需要函数签名中 将多个返回值()
func intSum3(x int, y ...int) (bool, int) {
	var bRes bool = false
	var res int = 0
	if x > len(y) {
		bRes = false
		res = 0
	} else {
		for i := 0; i < x; i++ {
			res += y[i]
		}
		bRes = true
	}
	return bRes, res
}

func main() {
	var fun_1 bool = false
	if fun_1 {
		fmt.Println(intSum(1, 2))
	}

	var fun_2 bool = false
	if fun_2 {
		fmt.Println(intSum2(3, 1, 2, 3, 4, 5)) 
	}

	var fun_3 bool = true
	if fun_3 {
		fmt.Println(intSum3(5, 1, 2, 3))
		fmt.Println(intSum3(3, 1, 2, 3, 4))
	}

}

函数类型(感觉类似函数指针)

函数作为参数,返回值

package main

import (
	"fmt"
)

//函数作为类型(感觉像是 函数指针)
type calculation func(int, int) int

func add(x, y int) int {
	return x + y
}

func sub(x, y int) int {
	return x - y
}

//函数作为参数,由于做参数需要由参数名,所以需要定义成一种函数类型
func calc(x, y int, opti func(int, int) int) int {
	return opti(x, y)
}

//函数作为返回值,由于作为返回值不需要函数名,所以按照函数声明的格式写就好
func funRet() func(int, int) int {
	return sub
}

func main() {
	var bt1 bool = false
	if bt1 {
		var t1 calculation = add
		var t2 calculation = sub
		fmt.Println(t1(1, 2))
		fmt.Println(t2(1, 3))
	}

	var bt2 bool = false
	if bt2 {
		fmt.Println(calc(1, 2, add))
	}

	var bt3 bool = true
	if bt3 {
		fmt.Println(funRet()(1, 2))
	}
}

匿名函数

匿名函数多用于实现回调函数和闭包。
由于是匿名函数,所以没有函数名,只能通过某个变量保存然后作为立即执行的函数

package main

import "fmt"

func main() {
	//由于是匿名函数,所以没有函数名,只能通过某个变量保存然后作为立即执行的函数
	add := func(x, y int) int {
		return x + y
	}
	fmt.Println(add(1, 2))

	func(x, y int) {
		fmt.Println(x + y)
	}(1, 2)
}

闭包

闭包首先是一个函数,功能类似于适配器
该函数包含了外部作用域的传入参数,并且在其返回的匿名函数中
可以使用外部传入的参数,这就相当于
改变了函数的参数列表

即 闭包=函数+外部变量的引用

底层原理:

  1. 函数可以作为返回值
  2. 函数内部查找变量时先在自己内部找,再往外层找
package main

import "fmt"

func adder(x, y int) func(int) int {
	return func(i int) int {
		return x + i + y
	}
}

func main() {
	ret := adder(100, 100)
	fmt.Println(ret(100))
} 

通过闭包对函数进行包装
把原来需要参数的函数f2,当做不需要参数的函数ret传给了f1

package main

import "fmt"

//对函数进行包装
/*
this is f1
this is f2
200
200
*/

func f1(f func() int) {
	fmt.Println("this is f1")
	fmt.Println(f())
}

func f2(x, y int) int {
	fmt.Println("this is f2")
	fmt.Println(x + y)
	return x + y
}

func test(fu func(int, int) int, x, y int) func() int {
	ret := func() int {
		return fu(x, y)
	}
	return ret
}

func main() {
	ret := test(f2, 100, 100)
	f1(ret)
	/*
		刚进入f1函数时,输出了this is f1
		然后开始调用f函数,也就是调用了ret函数,而ret函数来自test闭包
		ret实际就是 f2,则又输出了 thsi is f2和 200
		最后f函数输出了200
	*/
}

闭包的思考

闭包引用变量的生命周期

package main

import "fmt"

func adder() func(int) int {
	var x int
	ret := func(y int) int {
		x += y
		return x
	}
	return ret
}

/*变量f1是一个函数,并且引用了其外部作用域中的x变量,
则此时f就是一个闭包
在f的生命周期内,变量x将一直有效
可以理解为,每调用一次adder函数,将会产生一个栈空间
不论后续f1调用几次,都将共用同一个栈空间内的x变量
*/
func main() {
	f1 := adder()
	fmt.Println(f1(10))
	fmt.Println(f1(20))
	fmt.Println(f1(30))

	f2 := adder()
	fmt.Println(f2(10))
	fmt.Println(f2(10))
	fmt.Println(f2(10))
}
package main

import "fmt"

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 main() {
	f1, f2 := calc(10)
	fmt.Println(f1(1), f2(2)) //11 9
	fmt.Println(f1(3), f2(4)) //12 8
	fmt.Println(f1(5), f2(6)) //13 7
}

defer

defer语句会将后面跟随的语句进行延迟处理。在defer所属的函数即将返回时
将延迟处理的语句按defer定义顺序逆序执行,最后defer的语句 最先执行

package main

import "fmt"

func main() {
	fmt.Println("start")
	defer fmt.Println("1")
	defer fmt.Println("2")
	defer fmt.Println("3")
	fmt.Println("end")
    /*
    start
    end 
    3
    2
    1
    */
}

执行时间

在Go语言中return语句在底层执行时,共分为两步 以return x为例:

  1. 返回值赋值,将x的值赋给返回值(即将x的值存入寄存器中)
  2. RET指令:将寄存器中的值进行返回

而defer语句的执行时间在步骤1和步骤2之间

defer分析

变量作用域角度

package main

import "fmt"

/*
1:返回值赋值,由于返回值没有名称,所以开辟了一块内存存储5
2:defer修改x为6,但是由于修改的是x,所以不影响存储好的返回值
3:RET命令,返回之前存储好的 5
*/
func f1() int {
	x := 5
	defer func() {
		x++
	}()
	return x
}

/*
1:返回值赋值,x = 5
2:defer修改x为6,在寻找x时,找到了在返回值处声明的变量,故对其进行修改
3:RET命令,返回6
*/
func f2() (x int) {
	defer func() {
		x++
	}()
	return 5
}

/*
1:返回值赋值,y = x = 5
2:defer修改x为6,但是由于修改的是x,所以不影响存储好的返回值 y
3:RET命令,返回之前存储好的 y也就是5
*/
func f3() (y int) {
	x := 5
	defer func() {
		x++
	}()
	return x
}

/*
1:返回值赋值,x = 5
2:defer语句执行,在执行x++时,找到的x是传入的副本,不会对外面的变量产生影响(如果是传址,就会有影响)
3:RET命令,返回之前存储好的 5
*/
func f4() (x int) {
	defer func(x int) {
		x++
	}(x)
	return 5
}

func main() {
	fmt.Println(f1()) //5
	fmt.Println(f2()) //6
	fmt.Println(f3()) //5
	fmt.Println(f4()) //5
}

defer分析2

package main

import "fmt"

func calc(index string, a, b int) int {
	ret := a + b
	fmt.Println(index, a, b, ret)
	return ret
}

//defer注册要延迟执行的函数时,该函数所有的参数都需要确定其数值,所以被嵌套的calc会先执行
/*
1:defer calc("AA", x, calc("A", x, y)) 执行到这里后延时处理
2:calc("A", x, y) A 1 2 3
3:def calc("AA", 1, 3)
4:x = 10
5:defer calc("BB", x, calc("B", x, y))
6:calc("B", x, y) B 10 2 12
7:defer calc("BB", x, 12)
8:y = 20
9:此时开始逆序执行 defer语句
10:首先执行第七行, BB 10 12 22
11:然后执行第三行,AA 1 3 4

A 1 2 3
B 10 2 12
BB 10 12 22
AA 1 3 4
*/
func main() {
	x := 1
	y := 2
	defer calc("AA", x, calc("A", x, y))
	x = 10
	defer calc("BB", x, calc("B", x, y))
	y = 20
}