这是我参与8月更文挑战的第12天,活动详情查看:8月更文挑战
函数
一般指具有特定功能的代码块
格式
函数由关键字func来进行声明, 函数名, 参数列表, 返回值列表, 函数体, 返回语句来组成
func 函数名(参数1, 参数2, ...) (返回值1, 返回值2, ...) {
// 函数体
return // 返回语句
}
函数类型
如果函数的参数列表及类型, 返回值的列表及类型一样, 那么它们的类型为同一种函数类型
func add(a, b int) int {
return a + b
}
func sub(a, b int) int {
return a - b
}
add函数和sub函数就是同一种类型的函数
函数的定义
// 无参无返回值的函数
// 定义一个没有参数的函数
func sayHi() {
fmt.Println("Hi, go")
}
// 方式1: 我们可以将函数名直接赋值给一个变量, 变量类型由编译器自行推断
f := sayHi
fmt.Printf("%#v \n", f)
// 方式2: 定义一个函数类型的变量, 将符合这个函数类型的函数赋值给这个变量
// 定义一个函数类型
type sayHelloType func(string)
// 通过函数类型声明一个变量
var f2 sayHelloType
// 为函数变量赋值
f2 = sayHello
fmt.Printf("%#v \n", f2)
函数的调用
// 方式1: 我们可以直接使用函数名来调用
sayHi()
sayHello("shaosiming")
// 方式2: 也可以使用指向函数的变量名来调用
f()
f2("gopher")
f3()
参数的传递
Go中的函数参数是值传递, 也就是传递的是实参的拷贝
对于非引用类型: int float64, struct
在函数中更改非引用类型参数的值, 实参不会改变 想要在函数中更改实参的值, 我们可以传实参的地址
值类型
func changeValue(a int) {
a *= 10
}
func changeValue2(a *int) {
*a *= 10
}
// 非引用类型: 传值
value := 10
fmt.Println("changeValue调用前value的值: ", value)
changeValue(value)
fmt.Println("changeValue调用后value的值: ", value)
// 非引用类型: 传地址
value2 := 8
fmt.Println("changeValue调用前value2的值: ", value2)
changeValue2(&value2)
fmt.Println("changeValue调用后value2的值: ", value2)
引用类型
func changeValue3(m map[string]int) {
m["shaosiming"] = 15
}
// 引用类型
m := map[string]int {"shaosiming":18, "dasiming":20}
fmt.Println("changeValue3调用前m的值: ", m)
changeValue3(m)
fmt.Println("changeValue3调用前m的值: ", m)
变参函数
func printMultiParam(a ...int) {
fmt.Println(a)
}
// 有时我们不能确定函数要接收的同类型的参数个数, 可以使用变参函数
// 可以会多个参数
printMultiParam(1, 3, 5, 7)
// 也可以会一个切片, 获取这个切片中的多个值
s := []int {2, 4, 6, 8}
printMultiParam(s...)
函数的递归
// 使用递归求阶乘
func factorial(a int) int {
if a == 1 {
return 1
}
return a * factorial(a - 1)
}
// 使用循环求阶乘
func factorial2(a int) int {
res := 1
for i := 1; i <= a; i++ {
res *= i
}
return res
}
// 求阶乘
fmt.Println(factorial(5))
fmt.Println(factorial2(5))
匿名函数
// 匿名函数不能独立存在
// 可以直接在匿名函数后面加上()来进行调用
func(str string) {
fmt.Println("hi, ", str)
}("shaosiming")
// 也可以将匿名函数赋值给一个变量, 在合适的时候进行调用
ff := func() {
fmt.Println("这是一个匿名函数")
}
// 使用函数变量, 如果有参数的话,传入参数, 进行调用
ff()
回调函数
// 回调函数
// 声明一种函数类型
type CaluType func(int, int) int
// 定义一个CaluType类型的函数
func add(a, b int) int {
return a + b
}
// 定义另一个CaluType类型的函数
func sub(a, b int) int {
return a - b
}
// 接收两个整型类型的数据和一个CaluType类型的函数
func testFunc(a, b int, calu CaluType) {
res := calu(a, b)
fmt.Println("结果为: ", res)
}
// 回调函数
testFunc(3, 5, add)
testFunc(3, 5, sub)
闭包函数
// 闭包函数
func testClosure() func(int)int {
i := 2
// 在函数内部捕获函数外部变量
return func(value int) int {
i += value
return i
}
}
// 每次调用testClosure函数都会返回一个新的函数实例
// 因此返回的这个函数在调用的时候, i都会初始化为2, 所以调用这个函数输出的值是一样的
fmt.Println(testClosure()(3))
fmt.Println(testClosure()(3))
fmt.Println(testClosure()(3))
// 这里我们只调用了一次testClosure函数
// 因此当我们调用closureFunc时, 对函数内部i的访问都是同一块内存
closureFunc := testClosure()
fmt.Println(closureFunc(3))
fmt.Println(closureFunc(3))
fmt.Println(closureFunc(3))
总结
Go中的函数几乎涵盖了所有语言的函数操作上的特性, 比如: 多参函数, 多返回值, 回调函数, 匿名函数和闭包函数. 如果接触过C语言, 你几乎可以瞬间学会使用Go中的函数, 也更加强大和灵活, 带给我们更加舒适的编程体验!!!