匿名函数与闭包

匿名函数和闭包是 Go 语言函数式编程的重要特性。本章将详细介绍它们的概念和使用方法。

匿名函数

什么是匿名函数?

匿名函数是没有名称的函数,也称为函数字面量。

定义匿名函数

package main

import "fmt"

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

    result := add(10, 20)
    fmt.Printf("结果: %d\n", result)
}

立即执行

package main

import "fmt"

func main() {
    result := func(a, b int) int {
        return a + b
    }(10, 20)

    fmt.Printf("结果: %d\n", result)
}

作为参数传递

package main

import "fmt"

func process(nums []int, f func(int) int) []int {
    result := make([]int, len(nums))
    for i, num := range nums {
        result[i] = f(num)
    }
    return result
}

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

    doubled := process(nums, func(n int) int {
        return n * 2
    })
    fmt.Printf("翻倍: %v\n", doubled)

    squared := process(nums, func(n int) int {
        return n * n
    })
    fmt.Printf("平方: %v\n", squared)
}

闭包

什么是闭包?

闭包是一个函数值,它引用了其外部作用域中的变量。闭包可以访问和修改外部变量。

基本示例

package main

import "fmt"

func main() {
    x := 10

    f := func() {
        fmt.Printf("x = %d\n", x)
    }

    f()
}

闭包修改外部变量

package main

import "fmt"

func main() {
    counter := 0

    increment := func() int {
        counter++
        return counter
    }

    fmt.Println(increment())
    fmt.Println(increment())
    fmt.Println(increment())
}

输出:

1
2
3

闭包作为返回值

package main

import "fmt"

func counter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

func main() {
    c1 := counter()
    fmt.Println(c1())
    fmt.Println(c1())
    fmt.Println(c1())

    c2 := counter()
    fmt.Println(c2())
}

输出:

1
2
3
1

闭包应用场景

1. 计数器

package main

import "fmt"

func newCounter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

func main() {
    counter := newCounter()
    for i := 0; i < 5; i++ {
        fmt.Printf("调用 %d 次\n", counter())
    }
}

2. 斐波那契数列

package main

import "fmt"

func fibonacci() func() int {
    a, b := 0, 1
    return func() int {
        a, b = b, a+b
        return a
    }
}

func main() {
    f := fibonacci()
    for i := 0; i < 10; i++ {
        fmt.Printf("%d ", f())
    }
}

输出:

1 1 2 3 5 8 13 21 34 55

3. 缓存函数

package main

import "fmt"

func memoize(f func(int) int) func(int) int {
    cache := make(map[int]int)
    return func(n int) int {
        if v, ok := cache[n]; ok {
            fmt.Printf("从缓存获取: %d\n", n)
            return v
        }
        result := f(n)
        cache[n] = result
        fmt.Printf("计算并缓存: %d\n", n)
        return result
    }
}

func slowSquare(n int) int {
    return n * n
}

func main() {
    fastSquare := memoize(slowSquare)

    fmt.Println(fastSquare(5))
    fmt.Println(fastSquare(5))
    fmt.Println(fastSquare(10))
    fmt.Println(fastSquare(10))
}

4. 回调函数

package main

import "fmt"

func processWithCallback(data []int, callback func(int)) {
    for _, item := range data {
        callback(item)
    }
}

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

    fmt.Println("打印每个元素:")
    processWithCallback(nums, func(n int) {
        fmt.Printf("%d ", n)
    })
    fmt.Println()

    fmt.Println("\n打印平方:")
    processWithCallback(nums, func(n int) {
        fmt.Printf("%d ", n*n)
    })
    fmt.Println()
}

闭包陷阱

循环变量陷阱

package main

import (
    "fmt"
    "time"
)

func main() {
    for i := 0; i < 3; i++ {
        go func() {
            fmt.Printf("i = %d\n", i)
        }()
    }
    time.Sleep(time.Second)
}

输出可能:

i = 3
i = 3
i = 3

解决方法:传递参数

package main

import (
    "fmt"
    "time"
)

func main() {
    for i := 0; i < 3; i++ {
        go func(n int) {
            fmt.Printf("n = %d\n", n)
        }(i)
    }
    time.Sleep(time.Second)
}

输出:

n = 0
n = 1
n = 2

总结

本章学习了 Go 语言的匿名函数和闭包:

知识点说明
匿名函数没有名称的函数
立即执行func(){...}()
闭包引用外部变量的函数
应用场景计数器、缓存、回调等
循环陷阱循环变量共享问题