Go之路 - 7.go的函数

一、函数基础

1.1 函数声明

go 复制代码
// 基本语法
func 函数名(参数列表) (返回值列表) {
    // 函数体
}

// 示例
func add(x int, y int) int {
    return x + y
}

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

1.2 多返回值

go 复制代码
// 返回多个值
func swap(x, y string) (string, string) {
    return y, x
}

// 命名返回值
func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return // 裸返回,自动返回x和y
}

二、函数类型与高级特性

2.1 函数作为值

go 复制代码
func main() {
    // 函数赋值给变量
    add := func(x, y int) int {
        return x + y
    }
    
    fmt.Println(add(3, 4)) // 7
    
    // 函数作为参数
    calculate := func(fn func(int, int) int, a, b int) int {
        return fn(a, b)
    }
    
    result := calculate(add, 5, 3)
    fmt.Println(result) // 8
}

2.2 闭包

go 复制代码
// 返回函数的函数
func adder() func(int) int {
    sum := 0
    return func(x int) int {
        sum += x
        return sum
    }
}

func main() {
    pos, neg := adder(), adder()
    
    for i := 0; i < 10; i++ {
        fmt.Println(
            pos(i),    // 0, 1, 3, 6, 10...
            neg(-2*i), // 0, -2, -6, -12...
        )
    }
}

2.3 可变参数

go 复制代码
// ... 表示可变参数
func sum(numbers ...int) int {
    total := 0
    for _, num := range numbers {
        total += num
    }
    return total
}

func main() {
    fmt.Println(sum(1, 2, 3))        // 6
    fmt.Println(sum(1, 2, 3, 4, 5))  // 15
    
    // 切片作为可变参数
    nums := []int{1, 2, 3, 4}
    fmt.Println(sum(nums...))        // 10
}

三、方法与接收者

3.1 方法定义

go 复制代码
type Rectangle struct {
    width, height float64
}

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

// 指针接收者(可以修改结构体)
func (r *Rectangle) scale(factor float64) {
    r.width *= factor
    r.height *= factor
}

func main() {
    rect := Rectangle{width: 3, height: 4}
    
    fmt.Println(rect.area()) // 12
    
    rect.scale(2)
    fmt.Println(rect.width, rect.height) // 6 8
}

3.2 接收者选择

go 复制代码
type Counter struct {
    value int
}

// 值接收者:不修改原对象,适用于小型结构体
func (c Counter) GetValue() int {
    return c.value
}

// 指针接收者:需要修改对象或避免复制大对象
func (c *Counter) Increment() {
    c.value++
}

// 指针接收者:确保一致性(推荐)
func (c *Counter) Decrement() {
    c.value--
}

四、函数高级特性

4.1 延迟执行(defer)

go 复制代码
func processFile(filename string) error {
    file, err := os.Open(filename)
    if err != nil {
        return err
    }
    
    defer file.Close() // 确保文件被关闭
    
    // 多个defer按LIFO顺序执行
    defer fmt.Println("文件处理完成")
    defer fmt.Println("清理临时资源")
    
    // 处理文件...
    return nil
}

4.2 匿名函数

go 复制代码
func main() {
    // 立即执行函数
    func() {
        fmt.Println("立即执行")
    }()
    
    // 延迟执行的匿名函数
    defer func() {
        fmt.Println("延迟执行")
    }()
    
    // 作为回调
    nums := []int{1, 2, 3, 4}
    squares := make([]int, len(nums))
    
    for i, v := range nums {
        func(x int) {
            squares[i] = x * x
        }(v)
    }
    
    fmt.Println(squares) // [1 4 9 16]
}

4.3 错误处理函数

go 复制代码
// 返回错误的函数
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("除数不能为零")
    }
    return a / b, nil
}

// 带错误处理的辅助函数
func mustDivide(a, b float64) float64 {
    result, err := divide(a, b)
    if err != nil {
        panic(err)
    }
    return result
}

五、接口与函数

5.1 函数类型实现接口

go 复制代码
type Handler func(string) string

// 为函数类型添加方法
func (h Handler) Process(s string) string {
    return h(s)
}

// 函数作为接口实现
type StringProcessor interface {
    Process(string) string
}

func main() {
    var processor StringProcessor
    
    // 函数转换为Handler类型
    upper := Handler(strings.ToUpper)
    processor = upper
    
    fmt.Println(processor.Process("hello")) // HELLO
}

5.2 回调函数模式

go 复制代码
type FilterFunc func(int) bool

func filter(numbers []int, fn FilterFunc) []int {
    var result []int
    for _, n := range numbers {
        if fn(n) {
            result = append(result, n)
        }
    }
    return result
}

func main() {
    numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
    
    // 偶数筛选
    even := filter(numbers, func(n int) bool {
        return n%2 == 0
    })
    
    // 大于5筛选
    large := filter(numbers, func(n int) bool {
        return n > 5
    })
    
    fmt.Println(even)  // [2 4 6 8]
    fmt.Println(large) // [6 7 8 9]
}

六、最佳实践与模式

6.1 函数选项模式

go 复制代码
type Server struct {
    host    string
    port    int
    timeout time.Duration
}

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 time.Duration) Option {
    return func(s *Server) {
        s.timeout = timeout
    }
}

func NewServer(opts ...Option) *Server {
    s := &Server{
        host:    "localhost",
        port:    8080,
        timeout: 30 * time.Second,
    }
    
    for _, opt := range opts {
        opt(s)
    }
    
    return s
}

func main() {
    // 使用选项模式
    server := NewServer(
        WithHost("127.0.0.1"),
        WithPort(9000),
        WithTimeout(60*time.Second),
    )
}

6.2 中间件模式

go 复制代码
type Middleware func(http.HandlerFunc) http.HandlerFunc

func logging(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        next(w, r)
        fmt.Printf("请求处理耗时: %v\n", time.Since(start))
    }
}

func auth(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("Authorization")
        if token == "" {
            http.Error(w, "未授权", http.StatusUnauthorized)
            return
        }
        next(w, r)
    }
}

// 组合中间件
func chain(middlewares ...Middleware) Middleware {
    return func(next http.HandlerFunc) http.HandlerFunc {
        for i := len(middlewares) - 1; i >= 0; i-- {
            next = middlewares[i](next)
        }
        return next
    }
}

七、性能考虑

7.1 内联优化

go 复制代码
// 小函数会被编译器内联
func add(a, b int) int {
    return a + b
}

// 避免复杂函数影响内联
func complexCalculation(x, y int) int {
    // 复杂逻辑...
    return result
}

7.2 内存分配

go 复制代码
// 避免在热路径中分配内存
func process(data []byte) {
    // 复用缓冲区
    var buf [1024]byte
    // ... 处理逻辑
}

// 使用sync.Pool重用对象
var bufferPool = sync.Pool{
    New: func() interface{} {
        return make([]byte, 0, 1024)
    },
}

总结要点

  1. 函数设计原则

    • 保持函数简短(建议不超过50行)
    • 单一职责原则
    • 良好的命名和文档
  2. 错误处理

    • 使用多返回值处理错误
    • 尽早返回错误
    • 提供上下文信息
  3. 性能考虑

    • 小函数有利于内联
    • 避免不必要的内存分配
    • 合理使用指针接收者
  4. 代码组织

    • 相关函数分组
    • 使用接口抽象行为
    • 遵循Go惯用模式
相关推荐
Geoking.2 小时前
深度理解 Java synchronized —— 从原理到实战
java·开发语言
sailing-data2 小时前
【UI Qt】入门笔记
开发语言·qt·ui
Salt_07282 小时前
DAY32 类的定义和方法
开发语言·python·算法·机器学习
martinzh2 小时前
NL2SQL解决了?别闹了!大模型让你和数据库聊天背后的真相
后端
未来影子2 小时前
Java领域构建Agent新杀入一匹黑马(agentscope-java)
java·开发语言·python
靓仔建2 小时前
在asp.net web应用程序,老是访问同一个Handler1.ashx
后端·asp.net·一般处理程序
不会写DN2 小时前
JavaScript call、apply、bind 方法解析
开发语言·前端·javascript·node.js
阿里嘎多学长2 小时前
2025-12-12 GitHub 热点项目精选
开发语言·程序员·github·代码托管
goxingman2 小时前
实体类上关于时间字符串和时间字段转换涉及注解分析
java·开发语言