1.4 golang 函数

6 阅读3分钟

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 之前自动执行