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)
}
闭包
闭包首先是一个函数,功能类似于适配器
该函数包含了外部作用域的传入参数,并且在其返回的匿名函数中
可以使用外部传入的参数,这就相当于
改变了函数的参数列表
即 闭包=函数+外部变量的引用
底层原理:
- 函数可以作为返回值
- 函数内部查找变量时先在自己内部找,再往外层找
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为例:
- 返回值赋值,将x的值赋给返回值(即将x的值存入寄存器中)
- 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
}