3.4、Golang 函数
Go语言函数特性
- 函数分类:
- 普通函数
- 匿名函数
- 方法
- 函数不能重载,即不允许函数同名
- 函数不能嵌套函数,但可以嵌套匿名函数
- 函数可以赋值给变量
- 函数可以作为参数传递给另一个函数
- 函数的返回值可以是一个函数
- 函数传参传递是参数的副本
- 函数参数可以没有名称
函数定义
go
func function_name([parameter list])[return_type] {
// 函数体
}
示例
go
package main
import "fmt"
func sum(a int, b int) (ret int) {
return a + b
}
func main() {
r := sum(1, 1)
fmt.Printf("%v", r)
// 2
}
函数返回值
没有返回值
go
func foo(){
fmt.Println("Hello")
}
一个返回值
go
func sum(a int, b int) (ret int) {
return a + b
}
多个返回值
go
func foo() (name string, age int) {
return "Tom", 23
}
// 省略命名
func foo() (string, int) {
return "Tom", 23
}
多值返回常用于函数返回错误
value, exists
value, ok
value, err
返回值过多(4个以上),通常放在容器中返回
同类型 slice
不同类型 map
返回值不想使用,可以使用 _
丢弃
函数参数
参数类型:
- 形参 声明函数时的参数列表
- 实参 调用时传递的参数
特点:
- 参数可以0个或者有多个,需要指定数据类型
- 函数是传值的方式进行传参
- 可以使用变长参数
...
示例
go
package main
import "fmt"
// a, b形参
func sum(a int, b int) (ret int) {
return a + b
}
func main() {
// 1, 1 实参
r := sum(1, 1)
fmt.Printf("%v", r)
// 2
}
注意:
- map、slice、interface、channel数据类型是指针,拷贝的是指针,有可能会改变原始数据
go
package main
import "fmt"
func foo(arr []int) {
arr[0] = 100
}
func main() {
a := []int{1, 2, 3}
foo(a)
fmt.Printf("%v", a)
// [100 2 3]
}
变长参数
go
package main
import "fmt"
func foo(args ...int) {
for _, value := range args {
fmt.Printf("%v ", value)
}
}
func main() {
foo(1, 2, 3)
// 1 2 3
}
多种参数混合使用
go
package main
func foo(name string, isMan bool, arr ...int) {
}
func main() {
foo("Tom", true, 1, 2, 3)
}
函数类型与函数变量
定义函数类型
go
// 示例:接收两个参数,返回一个参数
type function_name func(int, int) int
示例
go
package main
import "fmt"
// 定义函数类型
type foo func(int, int) int
func sum(a int, b int) int {
return a + b
}
func max(a int, b int) int {
if a > b {
return a
} else {
return b
}
}
func main() {
// 声明
var f foo
f = sum
s := f(1, 2)
fmt.Printf("%v\n", s) // 3
f = max
m := f(1, 2)
fmt.Printf("%v\n", m) // 2
}
高阶函数
go语言的函数,可以作为函数的参数,也可以作为函数返回值
函数作为参数
go
package main
import "fmt"
func sayHello(name string) {
fmt.Printf("Hello %s", name)
}
func foo(name string, fun func(string)) {
fun(name)
}
func main() {
foo("Tom", sayHello)
// Hello Tom
}
函数作为返回值
go
package main
import "fmt"
func sum(a int, b int) int {
return a + b
}
func sub(a int, b int) int {
return a - b
}
func calc(name string) func(a int, b int) int {
switch name {
case "+":
return sum
case "-":
return sub
default:
return nil
}
}
func main() {
sum := calc("+")
r := sum(1, 1)
fmt.Printf("%v", r)
// 2
}
匿名函数
语法格式
go
// 没有函数名称
func (参数列表)(返回值){
}
示例1
go
package main
import "fmt"
func main() {
sum := func(a int, b int) int {
return a + b
}
ret := sum(1, 1)
fmt.Printf("%v", ret)
// 2
}
示例2
go
package main
import "fmt"
func main() {
ret := func(a int, b int) int {
return a + b
}(1, 1)
fmt.Printf("%v", ret)
// 2
}
闭包
闭包可以理解成:定义在一个函数内部的函数,本质上是将函数内部和函数外部连接起来的桥梁
闭包 = 函数 + 引用环境
示例1
go
package main
import (
"fmt"
)
// 返回一个函数
func add() func(int) int {
var a int
return func(b int) int {
a += b
return a
}
}
func main() {
f := add()
fmt.Println(f(1)) // 1
fmt.Println(f(1)) // 2
fmt.Println(f(1)) // 3
f2 := add()
fmt.Println(f2(1)) // 1
fmt.Println(f2(1)) // 2
fmt.Println(f2(1)) // 3
}
示例2
go
package main
import (
"fmt"
"strings"
)
// 返回一个函数
func makeSuffixFunc(suffix string) func(string) string {
return func(name string) string {
if strings.HasSuffix(name, suffix) {
return name
} else {
return name + suffix
}
}
}
func main() {
txtFunc := makeSuffixFunc(".txt")
jpgFunc := makeSuffixFunc(".jpg")
fmt.Println(txtFunc("test")) // test.txt
fmt.Println(jpgFunc("test")) // test.jpg
}
示例3
go
package main
import (
"fmt"
)
// 返回两个函数
func calc(base int) (func(int) int, func(int) int) {
add := func(i int) int {
return base + i
}
sub := func(i int) int {
return base - i
}
return add, sub
}
func main() {
f1, f2 := calc(10)
fmt.Println(f1(1), f2(2))
// 11 8
}
递归
递归函数:函数内部调用函数自身的函数
递归函数特点
- 递归就是自己调用自己
- 必须定义退出条件,否则就会成为死循环
- 递归很可能会出现栈空间内存溢出
示例:阶乘
for循环实现
go
package main
import "fmt"
// 阶乘
func foo(n int) int {
ret := 1
for i := 1; i <= n; i++ {
ret *= i
}
return ret
}
func main() {
// 5! = 5 x 4 x 3 x 2 x 1
ret := foo(5)
fmt.Println(ret)
// 120
}
递归实现
go
package main
import "fmt"
// 阶乘
func foo(n int) int {
if n == 1 {
// 退出条件
return 1
} else {
// 自己调用自己
return n * foo(n-1)
}
}
func main() {
// 5! = 5 x 4 x 3 x 2 x 1
ret := foo(5)
fmt.Println(ret)
// 120
}
菲波那切数列
计算公式
f(n) = f(n-1) + f(n-2)
且
f(2) = f(1) = 1
代码实现
go
package main
import "fmt"
// 菲波那切数列
func foo(n int) int {
if n == 1 || n == 2 {
// 退出条件
return 1
} else {
// 递归表达式
return foo(n-1) + foo(n-2)
}
}
func main() {
// foo(5) = foo(4) + foo(3) = 3 + 2 = 5
// foo(4) = foo(3) + foo(2) = 2 + 1 = 3
// foo(3) = foo(2) + foo(1) = 1 + 1 = 2
// foo(2) = 1
// foo(1) = 1
ret := foo(5)
fmt.Println(ret)
// 5
}
defer语句
defer语句后面的语句延迟处理,在defer归属的函数即将返回时,将延迟处理的语句按照defer定义的逆序进行执行
也就是说,先定义的defer语句最后执行;后定义的defer语句,先执行
defer特性
- 关键字defer用于注册延迟调用
- 这些调用知道return前才被执行,可以用来做资源清理
- 多个defer语句,按照先进后出的顺序执行
- defer语句中的变量,defer声明时就决定了
defer用途
- 关闭文件句柄
- 锁资源释放
- 数据库连接释放
示例
go
package main
import "fmt"
func main() {
fmt.Println("start")
defer fmt.Println("defer1")
defer fmt.Println("defer2")
defer fmt.Println("defer3")
fmt.Println("end")
// start
// end
// defer3
// defer2
// defer1
}
init函数
init函数是特殊函数,会先于main函数执行,实现包级别的初始化操作
init函数特点
- init函数先于main函数
自动执行
,不能被其他函数调用 - init函数没有输入参数,没有返回值
- 每个包可以有多个init函数
- 包的每个源文件也可以有多个init函数
- 同一个包的init执行顺序,golang没有明确意义
- 不同胞的init函数按照包导入的依赖关系决定执行顺序
golang初始化顺序
变量初始化 -> init() -> main()
示例
go
package main
import "fmt"
var i int = initVar()
func initVar() int {
fmt.Println("initVar")
return 100
}
func init() {
fmt.Println("init")
}
func main() {
fmt.Println("main")
}
// initVar
// init
// main