一、带多返回值函数(有参数 + 多命名返回值)
go
运行
Go
package main
import "fmt"
// 有参数、多命名返回值函数:求两个数最大最小值
func MaxAndMin(a, b int) (max, min int) {
if a > b {
max = a
min = b
} else {
max = b
min = a
}
return // 命名返回值可直接空return,自动返回变量值
}
func main() {
// 接收全部返回值
max, min := MaxAndMin(10, 20)
fmt.Printf("max = %d, min = %d\n", max, min)
// 匿名变量 _ 丢弃不需要的返回值
a, _ := MaxAndMin(10, 20)
fmt.Printf("最大值a = %d\n", a)
}
核心知识点
- 命名返回值:函数头部直接定义返回变量,函数内可直接赋值,
return无需写值; - 多返回值接收:变量个数必须和返回值匹配;
- 匿名变量
_:用来舍弃不需要的返回值,避免变量未使用报错。
二、普通函数调用(命名返回值函数)
go
运行
Go
package main
import "fmt"
// 命名返回值a
func funca() (a int) {
a = 111
fmt.Println("funca a =", a)
return
}
func main() {
a := funca()
fmt.Printf("main a = %d", a)
}
三、递归函数(函数调用自身)
核心规则:递归必须设置终止条件,否则无限递归栈溢出
示例 1:递归打印数字(理解执行顺序)
go
运行
Go
package main
import "fmt"
func test(a int) {
// 终止条件
if a == 1 {
fmt.Println("a =", a)
return
}
// 递归调用自身
test(a - 1)
// 递归回溯时执行
fmt.Println("a = ", a)
}
func main() {
test(3)
fmt.Println("main")
}
执行流程拆解 test (3)
- test (3) → 调用 test (2)
- test (2) → 调用 test (1)
- test (1):触发终止条件,打印
a=1,return - 回溯执行 test (2) 剩余代码:打印
a=2 - 回溯执行 test (3) 剩余代码:打印
a=3 - 回到 main 打印
main输出结果:
plaintext
Go
a = 1
a = 2
a = 3
main
示例 2:递归实现 1~100 累加求和
go
运行
Go
package main
import "fmt"
// 循环实现累加(对比递归)
func test01() (sum int) {
for i := 1; i <= 100; i++ {
sum += i
}
return
}
// 递归实现累加
func test02(i int) int {
// 递归终止条件
if i == 1 {
return 1
}
// 递推公式:当前值 + 前面所有数的和
return i + test02(i-1)
}
func main() {
sum := test02(100)
fmt.Println("1~100累加 sum = ", sum)
}
递归公式推导:sum(n) = n + sum(n-1),边界 sum(1)=1
四、函数类型(函数是一等数据类型)
知识点
- Go 中函数属于独立数据类型,可以定义变量、赋值、传参;
type自定义函数类型别名,统一规范同类函数签名;
go
运行
Go
package main
import "fmt"
// 普通加减函数
func Add(a, b int) int {
return a + b
}
func Minus(a, b int) int {
return a - b
}
// 自定义函数类型:接收两个int,返回int
type FuncType func(int, int) int
func main() {
// 方式1:直接调用函数
result := Add(1, 1)
fmt.Println("result = ", result)
// 方式2:函数类型变量,接收函数赋值
var fTest FuncType
fTest = Add
result = fTest(10, 20)
fmt.Println("result2 = ", result)
// 更换函数实现,同一个变量调用不同逻辑(多态)
fTest = Minus
result = fTest(10, 5)
fmt.Println("result3 = ", result)
}
五、回调函数(参数为函数类型)
核心概念
回调函数:函数的参数是另一个函数类型,主函数内部调用传入的外部函数;优势:多态特性,同一套调用逻辑,传入不同函数实现不同功能,业务解耦。
go
运行
Go
package main
import "fmt"
type FuncType func(int, int) int
func Add(a, b int) int {
return a + b
}
// Calc 回调函数:第二个参数为函数,外部传入具体运算逻辑
func Calc(a, b int, fTest FuncType) (result int) {
fmt.Println("Calc 统一计算入口")
result = fTest(a, b) // 执行外部传入的函数
return
}
func main() {
// 将Add函数作为参数传入Calc
a := Calc(1, 1, Add)
fmt.Println("计算结果a = ", a)
}
六、匿名函数 & 闭包
1. 基础概念
- 匿名函数:没有函数名的函数,可直接定义立即执行,或赋值给变量;
- 闭包:匿名函数捕获外层同作用域变量 / 常量,即使脱离外层作用域,变量依然保留状态。
2. 匿名函数基础用法
(1)定义后立即执行 func(){}(参数)
go
运行
Go
package main
import "fmt"
func main() {
a := 10
str := "mike"
// 无参无返回匿名函数,立即执行
func() {
fmt.Printf("无参匿名:a=%d, str=%s\n", a, str)
}()
// 带参数匿名函数,立即传参调用
func(i, j int) {
fmt.Printf("带参匿名 i=%d, j=%d\n", i, j)
}(10, 20)
// 有参有返回值匿名函数
max, min := func(i, j int) (max, min int) {
if i > j {
max = i
min = j
} else {
max = j
min = i
}
return
}(10, 20)
fmt.Printf("匿名函数最值 max=%d min=%d\n", max, min)
}
(2)匿名函数赋值给变量
go
运行
Go
func main() {
a := 10
str := "mike"
// 匿名函数赋值变量,形成闭包捕获外部变量
f1 := func() {
fmt.Printf("捕获外部变量 a=%d str=%s\n", a, str)
}
f1() // 调用
}
3. 闭包修改外部变量(共享引用)
闭包捕获的是变量引用,内部修改会同步改变外层变量:
go
运行
Go
func main() {
a := 10
str := "mike"
// 闭包修改外部变量
func() {
a = 666
str = "go"
fmt.Printf("闭包内部:a = %d, str = %s\n", a, str)
}()
fmt.Printf("外层变量:a = %d, str = %s\n", a, str)
}
4. 闭包核心特性:延长局部变量生命周期
普通函数局部变量每次调用都会重置;闭包会持有外层局部变量,多次调用保留变量状态:
go
运行
Go
package main
import "fmt"
// 普通函数:局部变量x每次调用重新初始化
func test01() int {
var x int
x++
return x * x
}
// 返回闭包函数,捕获局部变量x
func test02() func() int {
var x int
// 返回匿名闭包
return func() int {
x++
return x * x
}
}
func main() {
fmt.Println("普通函数调用:")
fmt.Println(test01()) //1
fmt.Println(test01()) //1
fmt.Println(test01()) //1
fmt.Println("闭包调用:")
f := test02() // f持有变量x,x不会销毁
fmt.Println(f()) //1
fmt.Println(f()) //4
fmt.Println(f()) //9
fmt.Println(f()) //16
fmt.Println(f()) //25
}
原理:只要闭包变量f还在使用,捕获的局部变量x就不会被 GC 回收,持续保存修改后的数值。
七、defer 延迟调用
1. 基础特性
defer只能写在函数内部;- 作用:延迟函数调用,当前函数执行完毕、return 之前执行;
- 多个 defer 遵循后进先出(栈结构),后定义先执行。
示例 1:单个 defer
go
运行
Go
package main
import "fmt"
func main() {
defer fmt.Println("延迟打印 bbbbbbbbbbbbbb")
fmt.Println("先执行 aaaaaaaaaaaa")
}
输出顺序:
plaintext
Go
aaaaaaaaaaaa
bbbbbbbbbbbbbb
示例 2:多个 defer 后进先出
go
运行
Go
func main() {
defer fmt.Println("aaaaaaa")
defer fmt.Println("bbbbbbb")
defer fmt.Println("ccccccccc")
}
执行顺序:ccccccccc → bbbbbbb → aaaaaaa
2. defer + 匿名闭包组合使用
重点:defer 定义时立刻拷贝参数值,延迟执行时不会读取后续修改
go
运行
Go
package main
import "fmt"
func main() {
a := 10
b := 20
// 定义defer时直接传入10、20,参数值已固定
defer func(x, y int) {
fmt.Printf("defer内部:a = %d, b = %d\n", x, y)
}(a, b)
// 后续修改变量不影响defer内打印
a = 111
b = 222
fmt.Printf("外部打印:a = %d, b = %d\n", a, b)
}
输出结果:
plaintext
外部打印:a = 111, b = 222
defer内部:a = 10, b = 20
完整知识总结(复习速记)
- 多返回值函数 :支持命名返回值,
_匿名变量丢弃返回值; - 递归:必须终止条件,先递推再回溯;
- 函数类型:函数是一等类型,type 自定义签名,可赋值变量;
- 回调函数:参数传入函数,实现多态、逻辑解耦;
- 匿名函数:无名称,支持定义后立即执行;
- 闭包:捕获外层变量引用,延长局部变量生命周期;
- defer 延迟:函数退出前执行,多个 defer 后进先出,定义时固化参数。