函数
什么是函数
- 函数是基本的代码块,用于执行一个任务
- Go语言最少有个 main() 函数
- 你可以同各国函数来划分不同功能,逻辑上灭个函数执行的是指定的任务
- 函数声明告诉了编译函数的名称,返回类型,和参数。
函数的声明
GO 语言函数定义格式入下
func function_name([parameter list]) [return_types] {
函数体
}
- 无参返回值函数
- 有一个参数的函数
- 有两个参数的函数
- 有一个返回值的函数
- 有多个返回值的函数
函数的声明和调用
package main
import "fmt"
func main() {
fmt.Println(add(1,2))
}
func add(a,b int)(int) {
c := a + b
return c
}
形参和实参
可变参数
概念:一个函数的参数类型确定,但是个数不确定,就可以使用可变参数 ...
注意:
- 如果一个函数是可变参数,同时还有其他的参数,可变参数要放在列表的最后
- 一个函数的参数列表最多只能有一个可变参数
// func myfunc(arg ... int) {}
package main
import "fmt"
func main() {
fmt.Println(getSum(1,2))
}
func getSum(nums ... int) {
sum:=0
for i:=0; i<len(nums);i++{
sum = sum+nums[i]
}
return sum
}
值传递
按照数据的存储特点来分:
- 值类型的数据:操作的是数据本身 int\string\bool\float64\array…
- 引用类型的数据:操作的是数据的地址 slice\map\chan …
引用传递
package main
import "fmt"
func main() {
// 值传递
// 定义一个数组 [个数]类型
arr:=[4]int{1,2,3,4}
fmt.Println(arr)
update(arr)
fmt.Println("调用 update 后的数据",arr)
// 引用传递
s :=[]int{1,2,3,4} // 切片
update2(s)
fmt.Println("调用 update2 后的数据",s)
}
func update(arr2 [4]int) {
arr2[0] = 100
}
func update2(s2 []int) {
s2[0] = 100
}
PS C:\Users\22347\Desktop\test> go run .\test.go
[1 2 3 4]
调用 update 后的数据 [1 2 3 4]
调用 update2 后的数据 [100 2 3 4]
PS C:\Users\22347\Desktop\test>
函数变量的作用域
作用域:变量可以使用的范围
全局变量:函数外部定义的
局部变量:函数内部定义的
递归函数
定义:一个函数自己调用自己,就叫递归函数
注意:递归函数需要一个出口,逐渐向出口靠近,没有出口就会形成死循环
package main
import "fmt"
func main() {
sum := getSum(5)
fmt.Println(sum)
}
func getSum(n int) int {
if n == 1 {
return 1
}
return getSum(n-1) + n
}
defer延迟函数的执行
defer 语义
在go语言中,使用defer关键字来延迟一个函数或方法的执行
package main
import "fmt"
func main(){
f("1")
fmt.Println("2")
defer f("3") // 会被延迟到最后执行
fmt.Println("4")
defer f("7")
defer f("8")
}
func f(s string){
fmt.Println(s)
}
PS C:\Users\22347\Desktop\test> go run .\hello.go
1
2
4
8
7
3
defer函数或方法:一个函数或方法的执行被延迟了
-
你可以在函数中添加多个 defer语句,当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回,特别时当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然很容易照成资源泄露
-
如果有很多调用defer,那么 defer 采用后进先出(栈)模式
-
defer 一般做关闭操作
函数本质
package main
import "fmt"
// func 本身就是一个数据类型
func main(){
// f1 如果不加括号,函数就是一个变量
// f1() 如果加了括号那就是函数的调用
fmt.Printf("%T",f1) // func() | func(int,int) | func(int,int) int
fmt.Printf("%T",10) // int
fmt.Printf("%T","hello") // string
// 定义函数类类型的变量
var f5 func(int,int)
f5 = f1
fmt.Printf(f5)
fmt.Printf(f1)
f5(1,2)
}
func f1(a,b int) int{
return a+b
}
匿名函数
- 函数里面可以传递函数作为参数
// 匿名函数
f3 := func(){
}
// 匿名函数自己调用自己
func() {}()
函数式编程
Go 语言是支持函数式变成:
- 将匿名函数作为另外一个函数的参数,回调函数
- 将匿名函数作为另外一个函数的返回值,可以形成闭包结构
高级:回调函数
高阶函数:根据go语言的函数类型的特点,可以将一个函数作为另外一个函数的参数
fun1(),fun2()
将 fun1 函数作为 fun2 这个函数的参数
fun2 函数: 就叫做高阶函数,接收了一个函数作为参数的函数
fun1函数:就叫做回调函数,作为另外一个函数的参数
闭包结构的理解
一个外层函数中,有内层函数,该内层函数中,会操作外层函数的局部变量
并且该外层函数的返回就是这个内层函数。
这个内层函数和外层函数的局部变量,统称为闭包结构
局部变量的生命周期就会发生改变,正常局部变量就会随着函数的调用二创建,随着函数的结束而销毁
但是闭包结构中外层函数的局部变量并不会随着外层函数的结束而销毁,因为内层函数还在继续使用
package main
import "fmt"
func main(){
r1 := increment()
v1 := r1()
fmt.Println(v1)
v2 := r1()
fmt.Println(v2)
fmt.Println(r1())
fmt.Println(r1())
fmt.Println(r1())
r2 := increment()
v3 := r2()
fmt.Println(v3)
fmt.Println(r1())
fmt.Println(r2())
}
// 自增
func increment() func() int{
// 局部变量 i
i := 0
// 定义一个匿名函数,给变量自增并返回
fun := func() int{ // 内层函数,没有执行的
i++
return i
}
return fun
}