一、函数
1、函数的定义
函数是基本的代码块,用于执行一个任务。
Go 语言最少有个 main() 函数。
你可以通过函数来划分不同功能,逻辑上每个函数执行的是指定的任务。
函数声明告诉了编译器函数的名称,返回类型,和参数。
Go 语言标准库提供了多种可动用的内置的函数。例如,len() 函数可以接受不同类型参数并返回该类型的长度。如果我们传入的是字符串则返回字符串的长度,如果传入的是数组,则返回数组中包含的元素个数。
Go 语言函数定义格式如下:
func function_name( [parameter list] ) [return_types] {
函数体
}
package main
import "fmt"
func main() {
fmt.Println(add(1, 2))
}
// func 函数名 (参数,参数 ...) 函数调用后的返回值 {
// 函数体
// return
//}
func add(a, b int) int {
c := a + b
return c
}
其输出结果为
3
2、函数的声明
函数分为多种形式
- 无参无返回值函数
- 有一个参数的函数
- 有两个参数的函数
- 有一个返回值的函数
- 有多个返回值的函数
package main
import "fmt"
func main() {
printinfo()
myprint("有一个参数的函数")
cheng(1, 2)
fmt.Println(xinxi(1, 2, "有两个参数的函数"))
fmt.Println(swap("返回值的函数", "有多个"))
}
// 无参无返回值函数
func printinfo() {
fmt.Println("无参无返回值函数")
}
// 有一个参数的函数
func myprint(msg string) {
fmt.Println(msg)
}
// 有两个参数的函数
func cheng(a, b int) {
c := a * b
fmt.Println("有两个参数的函数:", c)
}
// 有一个返回值的函数
func xinxi(a, b int, msg string) string {
c := a + b
fmt.Print(c)
return msg
}
// 有多个返回值的函数
func swap(x, y string) (string, string) {
return y, x
}
其输出结果为
无参无返回值函数
有一个参数的函数
有两个参数的函数: 2
3有两个参数的函数
有多个 返回值的函数
3、形式参数和实际参数
package main
func main() {
// 形参与实参要一一对应(顺序、个数、类型)
max(1, 2)
}
// max 两个数字比大小
// 形式参数:定义函数时,用来接收外部传入数据的参数,就是形参
// 实际参数:调用函数时,传给形参的实际数据叫做实参
func max(num1, num2 int) int {
var result int
if num1 > num2 {
result = num1
} else {
result = num2
}
// 一个函数定义上有返回值,那么函数中必须使用return语句
return result
}
4、可变参数
概念:一个函数的参数类型确定,但个数不确定,就可以使用可变参数
func mydunc(arg ...int) {}
// arg ...int 告诉go这个函数收不定数量的参数,类型全是int
package main
import "fmt"
func main() {
getSum(1, 2, 3, 5)
}
// ...可变参数
func getSum(nums ...int) {
sum := 0
for i := 0; i < len(nums); i++ {
sum += nums[i]
}
fmt.Println("sum:", sum)
}
其输出结果为
sum: 11
注意事项:
- 如果一个函数的参数是可变参数,同时还有其他的参数,可变参数要放在列表的最后。
- 一个函数的参数列表中最多只能有一个可变参数
5、参数传递
按照数据的存储特点来分:
- 值类型的数据:操作的是数据本身、int、string、bool、float64、array…
- 引用类型的数据:操作的是数据的地址 slice、map、chan…
值传递
package main
import "fmt"
func main() {
// 值传递
// arr2的数据是从arr复制来的,修改arr2并不会影响arr1
// 值传递:传递的数据的副本,修改数据,对于原始的数据没有影响
// 定义一个数组
arr := [4]int{1, 2, 3, 4}
fmt.Println("原数据:", arr)
update(arr)
fmt.Println("调用修改后的数据:", arr)
}
func update(arr2 [4]int) {
fmt.Println("接收前的数据:", arr2)
arr2[0] = 100
fmt.Println("修改后的数据:", arr2)
}
其输出结果为
原数据: [1 2 3 4]
接收前的数据: [1 2 3 4]
修改后的数据: [100 2 3 4]
调用修改后的数据: [1 2 3 4]
引用传递
package main
import "fmt"
func main() {
// slice切片,可以扩容的数组,不需要定义大小
// 引用传递相当于一个浅拷贝
s1 := []int{1, 2, 3, 4}
fmt.Println("原数据:", s1)
update2(s1)
fmt.Println("调用修改后的数据:", s1)
}
func update2(s2 []int) {
fmt.Println("接收前的数据:", s2)
s2[0] = 100
fmt.Println("修改后的数据:", s2)
}
其输出结果为
原数据: [1 2 3 4]
接收前的数据: [1 2 3 4]
修改后的数据: [100 2 3 4]
调用修改后的数据: [100 2 3 4]
6、defer延迟函数
package main
import "fmt"
func main() {
f("1")
fmt.Println("2")
defer f("3") // 会被延迟到最后执行
fmt.Println("4")
defer f("5")
fmt.Println("6")
defer f("7")
}
func f(s string) {
fmt.Println(s)
}
其输出结果为
1
2
4
6
7
5
3
defer函数或者方法:一个函数或方法的执行被延迟了
- 你可以在函数中添加多个defer语句,当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回,特别是当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然容易造成资源泄露等问题。
- 如果有很多调用defer,那么defer是采用后进先出(栈)模式。
defer穿参的时候已经传递进去了,只是最后才执行
package main
import "fmt"
// defer 关闭操作
func main() {
a := 10
fmt.Println("a=>", a)
a++
defer f(a) // 参数已经传进去了,在最后执行
a++
fmt.Println("end a=>", a)
}
func f(s int) {
fmt.Println("函数里面的a=>", s)
}
其输出结果为
a=> 10
end a=> 12
函数里面的a=> 11
7、函数的数据类型
package main
import "fmt"
func main() {
// f 如果不加括号,函数就是一个变量
fmt.Printf("%T",f)
}
func f(s int) {
}
其输出结果为
func(int)
package main
import "fmt"
// func() 本身就是一个数据类型
func main() {
// 定义函数类型的变量
var f2 func(int, int)
f2 = f1
f2(1, 2)
f3(f1)
}
func f1(a, b int) {
fmt.Println(a, b)
}
func f3(f4 func(int, int)) {
f4(3, 4)
}
其输出结果为
1 2
3 4
8、匿名函数
package main
import "fmt"
func main() {
f1()
f2 := f1 // 函数本送也是一个变量
f2()
// 匿名函数
f3 := func() {
fmt.Println("我是f3函数")
}
f3()
func() {
fmt.Println("我是f4函数")
}()
func(a, b int) {
fmt.Print("我是f5函数")
fmt.Println(a, b)
}(1, 2)
}
func f1() {
fmt.Println("我是f1函数")
}
其输出结果为
我是f1函数
我是f1函数
我是f3函数
我是f4函数
我是f5函数1 2
Go语言是支持函数式编程:
- 将匿名函数做为另外一个函数的参数,回调函数
- 将匿名函数作为另外一个函数的返回值,可以形成闭包结构
9、回调函数
根据go语言的数据类型的特点,可以将一个函数作为另外一个函数的参数
fun1(), fun2()
将fun1函数作为fun2这个函数的参数
fun2函数:就叫做高阶函数,接收了一个函数作为参数的函数
fun1函数:就叫做回调函数,作为另外一个函数的参数
package main
import "fmt"
func main() {
r1 := add(1, 2)
fmt.Println(r1)
r2 := oper(3, 4, add)
fmt.Println(r2)
r3 := oper(5, 3, sub)
fmt.Println(r3)
r4 := oper(8, 4, func(a int, b int) int {
if b == 0 {
fmt.Println("除数不能为0")
}
return a / b
})
fmt.Println(r4)
}
// 高阶函数
func oper(a, b int, fun func(int, int) int) int {
r := fun(a, b)
return r
}
func add(a, b int) int {
return a + b
}
func sub(a, b int) int {
return a - b
}
其输出结果为
3
7
2
2
10、闭包
package main
import "fmt"
/*
一个外层函数中,有内层函数,该内层函数中,会操作外层函数的局部变量
并且该外层函数的返回值就是这个内层函数。
这个内层函数和外层函数的局部变量,统称为闭包结构
局部变量的生命周期就会发生改变,正常的局部变量会随着函数的调用而创建,随着函数的结束而销毁
但是闭包结构中的外层函数的局部变量并不会随着外层函数的结束而销毁,因为内层函数还在继续使用
*/
func main() {
r1 := increment()
fmt.Println(r1)
v1 := r1()
fmt.Println(v1)
v2 := r1()
fmt.Println(v2)
//
r2 := increment()
v3 := r2()
fmt.Println(v3)
v4 := r2()
fmt.Println(v4)
}
// 自增
func increment() func() int {
// 局部变量
i := 0
// 定义一个匿名函数,给变量自增并返回
fun := func() int {
// 局部变量的生命周期发生了变换
i++
return i
}
return fun
}
其输出结果为
0xafe6e0
1
2
1
2