1.4 函数
函数定义
package main
import "fmt"
// 基本函数定义
func greet(name string) {
fmt.Println("Hello,", name)
}
// 带返回值的函数
func add(a int, b int) int {
return a + b
}
// 参数类型相同时可以简写
func multiply(a, b int) int {
return a * b
}
func main() {
greet("Go")
fmt.Println(add(3, 5)) // 8
fmt.Println(multiply(4, 6)) // 24
}
多返回值
Go 函数可以返回多个值,这是 Go 最重要的特性之一:
// 返回两个值
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("除数不能为 0")
}
return a / b, nil
}
// 交换两个值
func swap(a, b string) (string, string) {
return b, a
}
func main() {
// 接收多个返回值
result, err := divide(10, 3)
if err != nil {
fmt.Println("错误:", err)
} else {
fmt.Printf("结果: %.2f\n", result)
}
// 忽略某个返回值
_, err2 := divide(10, 0)
if err2 != nil {
fmt.Println("错误:", err2)
}
x, y := swap("hello", "world")
fmt.Println(x, y) // world hello
}
命名返回值
// 返回值可以命名,相当于在函数内预先声明了变量
func rectangleArea(width, height float64) (area, perimeter float64) {
area = width * height
perimeter = 2 * (width + height)
return // "裸" return,自动返回命名返回值
}
func main() {
a, p := rectangleArea(5, 3)
fmt.Printf("面积: %.1f, 周长: %.1f\n", a, p)
// 面积: 15.0, 周长: 16.0
}
💡 命名返回值在短函数中可以提高可读性,但在长函数中建议显式
return具体的值。
可变参数
// 使用 ... 接收任意数量的参数
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
func main() {
fmt.Println(sum(1, 2, 3)) // 6
fmt.Println(sum(1, 2, 3, 4, 5)) // 15
// 传入切片时需要用 ... 展开
numbers := []int{10, 20, 30}
fmt.Println(sum(numbers...)) // 60
}
匿名函数
func main() {
// 将匿名函数赋值给变量
square := func(n int) int {
return n * n
}
fmt.Println(square(5)) // 25
// 立即执行的匿名函数(IIFE)
result := func(a, b int) int {
return a + b
}(3, 4)
fmt.Println(result) // 7
}
闭包(Closure)
闭包是引用了外部变量的函数。闭包会"捕获"外部变量:
// 计数器工厂:每次调用返回一个新的计数器
func makeCounter() func() int {
count := 0 // 这个变量被闭包捕获
return func() int {
count++
return count
}
}
func main() {
counter1 := makeCounter()
fmt.Println(counter1()) // 1
fmt.Println(counter1()) // 2
fmt.Println(counter1()) // 3
// counter2 有自己独立的 count
counter2 := makeCounter()
fmt.Println(counter2()) // 1
}
闭包的实际应用
// 创建一个带前缀的日志函数
func makeLogger(prefix string) func(string) {
return func(msg string) {
fmt.Printf("[%s] %s\n", prefix, msg)
}
}
func main() {
infoLog := makeLogger("INFO")
errLog := makeLogger("ERROR")
infoLog("服务启动") // [INFO] 服务启动
errLog("连接超时") // [ERROR] 连接超时
}
函数作为参数(高阶函数)
// 函数作为参数
func apply(nums []int, fn func(int) int) []int {
result := make([]int, len(nums))
for i, n := range nums {
result[i] = fn(n)
}
return result
}
func main() {
numbers := []int{1, 2, 3, 4, 5}
// 传入不同的函数实现不同的功能
doubled := apply(numbers, func(n int) int { return n * 2 })
squared := apply(numbers, func(n int) int { return n * n })
fmt.Println(doubled) // [2 4 6 8 10]
fmt.Println(squared) // [1 4 9 16 25]
}
函数类型
// 定义函数类型
type MathFunc func(int, int) int
func operate(a, b int, op MathFunc) int {
return op(a, b)
}
func main() {
add := func(a, b int) int { return a + b }
sub := func(a, b int) int { return a - b }
mul := func(a, b int) int { return a * b }
fmt.Println(operate(10, 3, add)) // 13
fmt.Println(operate(10, 3, sub)) // 7
fmt.Println(operate(10, 3, mul)) // 30
}
init 函数
每个包可以有一个或多个 init 函数,在 main 函数之前自动执行:
package main
import "fmt"
// init 在 main 之前自动执行
func init() {
fmt.Println("init 函数被调用")
}
func main() {
fmt.Println("main 函数被调用")
}
// 输出:
// init 函数被调用
// main 函数被调用
执行顺序:导入包的 init → 当前包的 init → main
递归
// 阶乘
func factorial(n int) int {
if n <= 1 {
return 1
}
return n * factorial(n-1)
}
// 斐波那契数列
func fibonacci(n int) int {
if n <= 1 {
return n
}
return fibonacci(n-1) + fibonacci(n-2)
}
func main() {
fmt.Println("5! =", factorial(5)) // 120
// 打印前10个斐波那契数
for i := 0; i < 10; i++ {
fmt.Printf("%d ", fibonacci(i))
}
// 0 1 1 2 3 5 8 13 21 34
}
小结
| 特性 | 说明 |
|---|---|
| 多返回值 | func f() (int, error),Go 最常见的模式 |
| 命名返回值 | 适用于短函数,长函数建议显式 return |
| 可变参数 | func f(nums ...int),传切片时用 slice... |
| 闭包 | 捕获外部变量,常用于工厂模式 |
| 高阶函数 | 函数作参数/返回值,实现灵活的逻辑复用 |
| init 函数 | 包初始化,main 之前自动执行 |