<返回目录     Powered by claud/xia兄

第4课: 函数与方法

函数基础

函数是Go语言的基本构建块,用于封装可重用的代码逻辑。

基本函数定义

package main

import "fmt"

// 基本函数
func greet() {
    fmt.Println("Hello, World!")
}

// 带参数的函数
func add(a int, b int) int {
    return a + b
}

// 简化参数类型(相同类型)
func multiply(a, b int) int {
    return a * b
}

func main() {
    greet()
    sum := add(3, 5)
    product := multiply(4, 6)
    fmt.Printf("和: %d, 积: %d\n", sum, product)
}

多返回值

Go语言支持函数返回多个值,这是Go的一大特色。

// 返回多个值
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("除数不能为0")
    }
    return a / b, nil
}

func swap(x, y string) (string, string) {
    return y, x
}

func main() {
    // 接收多个返回值
    result, err := divide(10, 2)
    if err != nil {
        fmt.Println("错误:", err)
    } else {
        fmt.Println("结果:", result)
    }

    // 交换变量
    a, b := swap("hello", "world")
    fmt.Println(a, b)  // world hello
}

命名返回值

// 命名返回值
func calculate(a, b int) (sum, product int) {
    sum = a + b
    product = a * b
    return  // 裸返回,自动返回命名的变量
}

func getStats(numbers []int) (min, max, avg int) {
    if len(numbers) == 0 {
        return
    }

    min, max = numbers[0], numbers[0]
    sum := 0

    for _, num := range numbers {
        if num < min {
            min = num
        }
        if num > max {
            max = num
        }
        sum += num
    }

    avg = sum / len(numbers)
    return
}

func main() {
    s, p := calculate(5, 3)
    fmt.Printf("和: %d, 积: %d\n", s, p)

    nums := []int{3, 7, 1, 9, 4}
    min, max, avg := getStats(nums)
    fmt.Printf("最小: %d, 最大: %d, 平均: %d\n", min, max, avg)
}

可变参数函数

// 可变参数(...)
func sum(numbers ...int) int {
    total := 0
    for _, num := range numbers {
        total += num
    }
    return total
}

func printInfo(prefix string, values ...interface{}) {
    fmt.Print(prefix, ": ")
    for _, v := range values {
        fmt.Print(v, " ")
    }
    fmt.Println()
}

func main() {
    // 传递任意数量的参数
    fmt.Println(sum(1, 2, 3))           // 6
    fmt.Println(sum(1, 2, 3, 4, 5))     // 15

    // 传递切片
    nums := []int{10, 20, 30}
    fmt.Println(sum(nums...))           // 60

    printInfo("数据", 1, "hello", 3.14, true)
}

匿名函数与闭包

匿名函数

package main

import "fmt"

func main() {
    // 定义并立即调用
    func() {
        fmt.Println("匿名函数")
    }()

    // 赋值给变量
    add := func(a, b int) int {
        return a + b
    }
    fmt.Println(add(3, 4))  // 7

    // 作为参数传递
    numbers := []int{1, 2, 3, 4, 5}
    result := filter(numbers, func(n int) bool {
        return n%2 == 0
    })
    fmt.Println(result)  // [2 4]
}

func filter(nums []int, fn func(int) bool) []int {
    var result []int
    for _, num := range nums {
        if fn(num) {
            result = append(result, num)
        }
    }
    return result
}

闭包

闭包是引用了外部变量的函数。

// 计数器闭包
func counter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

// 累加器闭包
func adder(initial int) func(int) int {
    sum := initial
    return func(x int) int {
        sum += x
        return sum
    }
}

func main() {
    // 每个闭包都有自己的状态
    c1 := counter()
    c2 := counter()

    fmt.Println(c1())  // 1
    fmt.Println(c1())  // 2
    fmt.Println(c2())  // 1

    // 累加器
    acc := adder(10)
    fmt.Println(acc(5))   // 15
    fmt.Println(acc(10))  // 25
}

defer语句

defer用于延迟函数调用,常用于资源清理。

package main

import (
    "fmt"
    "os"
)

func main() {
    // defer会在函数返回前执行
    defer fmt.Println("世界")
    fmt.Println("你好")
    // 输出: 你好 世界

    // 多个defer按LIFO顺序执行
    for i := 1; i <= 3; i++ {
        defer fmt.Println(i)
    }
    // 输出: 3 2 1
}

// 实际应用:文件操作
func readFile(filename string) error {
    file, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer file.Close()  // 确保文件被关闭

    // 读取文件内容...
    return nil
}

// defer捕获参数值
func deferExample() {
    x := 10
    defer fmt.Println("defer:", x)  // 捕获当前值10
    x = 20
    fmt.Println("函数:", x)
    // 输出: 函数: 20
    //      defer: 10
}

方法

方法是带有接收者的函数,用于给类型添加行为。

值接收者

package main

import (
    "fmt"
    "math"
)

type Rectangle struct {
    width, height float64
}

type Circle struct {
    radius float64
}

// 值接收者方法
func (r Rectangle) Area() float64 {
    return r.width * r.height
}

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.width + r.height)
}

func (c Circle) Area() float64 {
    return math.Pi * c.radius * c.radius
}

func main() {
    rect := Rectangle{width: 10, height: 5}
    fmt.Printf("面积: %.2f\n", rect.Area())
    fmt.Printf("周长: %.2f\n", rect.Perimeter())

    circle := Circle{radius: 5}
    fmt.Printf("圆面积: %.2f\n", circle.Area())
}

指针接收者

type Counter struct {
    count int
}

// 指针接收者可以修改接收者
func (c *Counter) Increment() {
    c.count++
}

func (c *Counter) Decrement() {
    c.count--
}

func (c Counter) Value() int {
    return c.count
}

func main() {
    counter := Counter{count: 0}

    counter.Increment()
    counter.Increment()
    fmt.Println(counter.Value())  // 2

    counter.Decrement()
    fmt.Println(counter.Value())  // 1
}

值接收者 vs 指针接收者

特性 值接收者 指针接收者
修改接收者 不能 可以
性能 复制整个结构体 只复制指针
使用场景 小型结构体、不需修改 大型结构体、需要修改

函数作为参数

package main

import "fmt"

// 函数类型
type Operation func(int, int) int

func add(a, b int) int {
    return a + b
}

func multiply(a, b int) int {
    return a * b
}

// 接受函数作为参数
func calculate(a, b int, op Operation) int {
    return op(a, b)
}

// 高阶函数:返回函数
func makeMultiplier(factor int) func(int) int {
    return func(x int) int {
        return x * factor
    }
}

func main() {
    result1 := calculate(5, 3, add)
    result2 := calculate(5, 3, multiply)
    fmt.Println(result1, result2)  // 8 15

    double := makeMultiplier(2)
    triple := makeMultiplier(3)
    fmt.Println(double(5))  // 10
    fmt.Println(triple(5))  // 15
}

递归函数

// 阶乘
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 fibonacciMemo(n int, memo map[int]int) int {
    if n <= 1 {
        return n
    }
    if val, ok := memo[n]; ok {
        return val
    }
    memo[n] = fibonacciMemo(n-1, memo) + fibonacciMemo(n-2, memo)
    return memo[n]
}

func main() {
    fmt.Println(factorial(5))  // 120
    fmt.Println(fibonacci(10)) // 55

    memo := make(map[int]int)
    fmt.Println(fibonacciMemo(40, memo))  // 快速计算
}

综合示例

示例1:函数式编程风格

package main

import "fmt"

func Map(nums []int, fn func(int) int) []int {
    result := make([]int, len(nums))
    for i, v := range nums {
        result[i] = fn(v)
    }
    return result
}

func Filter(nums []int, fn func(int) bool) []int {
    var result []int
    for _, v := range nums {
        if fn(v) {
            result = append(result, v)
        }
    }
    return result
}

func Reduce(nums []int, initial int, fn func(int, int) int) int {
    result := initial
    for _, v := range nums {
        result = fn(result, v)
    }
    return result
}

func main() {
    nums := []int{1, 2, 3, 4, 5}

    // Map: 每个元素乘以2
    doubled := Map(nums, func(n int) int {
        return n * 2
    })
    fmt.Println(doubled)  // [2 4 6 8 10]

    // Filter: 过滤偶数
    evens := Filter(nums, func(n int) bool {
        return n%2 == 0
    })
    fmt.Println(evens)  // [2 4]

    // Reduce: 求和
    sum := Reduce(nums, 0, func(acc, n int) int {
        return acc + n
    })
    fmt.Println(sum)  // 15
}

示例2:选项模式

type Server struct {
    host string
    port int
    timeout int
}

type Option func(*Server)

func WithHost(host string) Option {
    return func(s *Server) {
        s.host = host
    }
}

func WithPort(port int) Option {
    return func(s *Server) {
        s.port = port
    }
}

func WithTimeout(timeout int) Option {
    return func(s *Server) {
        s.timeout = timeout
    }
}

func NewServer(opts ...Option) *Server {
    s := &Server{
        host: "localhost",
        port: 8080,
        timeout: 30,
    }

    for _, opt := range opts {
        opt(s)
    }

    return s
}

func main() {
    server := NewServer(
        WithHost("0.0.0.0"),
        WithPort(9000),
        WithTimeout(60),
    )
    fmt.Printf("%+v\n", server)
}
最佳实践:

实践练习

  1. 编写一个函数,计算两个数的最大公约数(GCD)
  2. 实现一个通用的排序函数,接受比较函数作为参数
  3. 创建一个计算器结构体,实现加减乘除方法
  4. 编写一个闭包函数,实现简单的缓存机制
  5. 实现一个递归函数,计算目录的总大小
  6. 使用defer实现函数执行时间统计
  7. 编写一个函数,实现柯里化(Currying)
  8. 创建一个链式调用的API(使用方法链)
常见错误: