【Go语言基础【13】】函数、闭包、方法

零、概述

函数核心特性速查

特性说明
多返回值支持同时返回结果和错误(如(int, error)
可变参数通过...Type定义,本质为切片([]Type
函数类型可定义函数类型(如type FuncType func(int) bool
闭包匿名函数捕获外部变量,保持状态
方法绑定到结构体的函数,通过接收者参数(func (t Type) Method())定义
内置函数lenmakeappend等预定义函数,直接使用

最佳实践

  1. 错误处理
    • 多返回值中优先返回错误(如func() (Result, error))。
    • 使用if err != nil判断错误,避免忽略重要异常。
  2. 减少值拷贝
    • 传递大结构体时使用指针(*T),避免性能损耗。
    • 切片、map等引用类型默认传递指针,无需额外处理。
  3. 函数职责单一
    • 每个函数专注完成一个独立功能,符合单一职责原则。
    • 避免函数过长(建议不超过50行),复杂逻辑拆分为子函数。

 

一、函数基础

1、函数基础概念

Go语言中的函数可赋值给变量、作为参数传递或作为返回值,极大提升了编程灵活性。

1. 函数定义语法

func 函数名(参数列表) (返回值列表) {
    函数体
    return 返回值
}
  • 参数列表:参数类型需后置,连续相同类型可合并声明。
    func sum(x, y int) int { // x, y 均为 int 类型
        return x + y
    }
    
  • 返回值列表:可无返回值或多返回值(用括号包裹),支持命名返回值。
    func div(x, y int) (int, error) { // 多返回值
        if y == 0 {
            return 0, errors.New("除数不能为0")
        }
        return x / y, nil
    }
    

 

2、参数传递机制

1.1. 值传递

  • 机制:传递参数的副本,函数内修改不影响原始值。
  • 适用场景:小数据类型(如intstring)或无需修改原始值的场景。
    func modifyValue(x int) {
        x = 100 // 仅修改副本
    }
    func main() {
        a := 20
        modifyValue(a) // a 仍为 20
    }
    

1.2. 引用传递

  • 机制:传递参数的地址(通过指针*T),函数内可修改原始值。
  • 适用场景:大数据类型(如结构体)或需修改原始值的场景。
    func modifyPointer(x *int) {
        *x = 100 // 修改原始值
    }
    func main() {
        a := 20
        modifyPointer(&a) // a 变为 100
    }
    

1.3. 可变参数
语法:通过...Type定义可变参数,本质为切片。

func sum(nums ...int) int { // nums 类型为 []int
    total := 0
    for _, num := range nums {
        total += num
    }
    return total
}
func main() {
    sum(1, 2, 3) // 等价于 sum([]int{1,2,3})
}

 

3、返回值特性

3.1. 多返回值

用途:同时返回结果和错误(Go语言的惯用模式)。

func readFile(path string) ([]byte, error) {
    data, err := os.ReadFile(path)
    return data, err
}

3.2. 命名返回值

语法:为返回值命名,函数体可直接使用(类似变量声明)。

func calculate(x, y int) (sum, product int) { // 命名返回值
    sum = x + y
    product = x * y
    return // 隐式返回 sum, product
}

3.3. 错误处理

丢弃返回值:用_忽略不关心的返回值。

data, _ := readFile("data.txt") // 忽略错误

 

二、函数类型与高阶函数

1. 函数类型定义

在Go语言中,函数类型定义是指为一种特定的函数签名创建一个自定义类型名称。这样可以让具有相同签名的函数共享同一个类型(有点面向接口开发的感觉)。

type Calculator func(int, int) int // 定义函数类型

// 这些函数都符合Calculator类型的签名
func add(x, y int) int { return x + y }
func subtract(x, y int) int { return x - y }
func multiply(x, y int) int { return x * y }
func divide(x, y int) int { return x / y }

 

例题:

package main

import "fmt"

type Calculator func(int, int) int

func add(x, y int) int      { return x + y }
func subtract(x, y int) int { return x - y }

// 使用函数类型作为参数
func calculate(calc Calculator, a, b int) int {
	return calc(a, b)
}

// 使用函数类型作为变量
func main() {
	var myCalc Calculator

	myCalc = add
	fmt.Println(myCalc(5, 3)) // 输出: 8

	myCalc = subtract
	fmt.Println(myCalc(5, 3)) // 输出: 2

	// 作为参数传递
	result := calculate(add, 10, 5)
	fmt.Println(result) // 输出: 15
}

 

2. 高阶函数(函数作为参数、返回值)

函数作为参数

func operate(x, y int, fn Calculator) int { // 接收函数作为参数
// 作为参数的函数,来处理参数
    return fn(x, y)
}
func main() {
    result := operate(10, 5, add) // 调用 add 函数
}

 
函数作为返回值
在Go语言中,函数可以作为返回值,这使得我们可以创建闭包。闭包是一个函数,它可以捕获并记住其所在环境中的变量。

func makeAdder(n int) Calculator { // 返回函数
    return func(x int) int {
        return x + n
    }
}
func main() {
    add5 := makeAdder(5) // add5(3) 返回 8
}

 

三、匿名函数与闭包

1. 匿名函数(Lambda函数)

  • 无名称函数,可直接定义和调用
 // 赋值给变量
 greet := func() {
     fmt.Println("Hello, Go!")
 }
 greet() // 调用匿名函数

1. 定义:func() { ... }是一个匿名函数,因为它没有名称。
2. 赋值:我们将这个匿名函数赋值给变量greet。
3. 调用:通过greet()来调用这个匿名函数。


 // 立即执行函数表达式(IIFE)
 result := func(x, y int) int {
     return x * y
 }(3, 4) 

1. 定义并执行:func(x, y int) int { return x * y }是一个匿名函数。
2. 立即执行:在定义后面紧跟(3, 4),这表示立即用参数34调用这个函数。
3. 结果:函数返回3 * 4的结果12,并将其赋值给变量result。

 

2. 闭包(Closure)

定义:匿名函数捕获外部变量形成闭包,变量在闭包内保持状态。

闭包能够捕获并记住其外部环境中的变量,即使在函数执行完毕后,这些变量仍然可以被访问和修改。这使得闭包可以在不同的调用之间保持状态,例如计数器、缓存等。

package main

import "fmt"

// adder 返回一个闭包函数,该函数捕获了外部变量 sum
func adder() func(int) int {
   sum := 0 // 这是被闭包捕获的外部变量
   return func(x int) int {
       sum += x // 每次调用时,更新并返回 sum
       return sum
   }
}

func main() {
   pos := adder() // 创建一个新的闭包
   fmt.Println(pos(1)) // 输出: 1
   fmt.Println(pos(2)) // 输出: 3
   fmt.Println(pos(3)) // 输出: 6

   // 创建另一个独立的闭包
   neg := adder()
   fmt.Println(neg(-1)) // 输出: -1
   fmt.Println(neg(-2)) // 输出: -3
}

 

四、内置函数

Go语言预定义了一组内置函数,无需导入包即可使用:

函数名功能描述
len()获取切片、字符串、通道等的长度
new()分配零值内存,返回指针(如new(int)返回*int
make()创建引用类型(切片、map、通道)并初始化
append()向切片追加元素,返回新切片
panic()触发运行时恐慌,用于错误处理
recover()defer中恢复恐慌,避免程序崩溃

 

五、方法(让go有了面向对象的特性)

在Go语言中,方法和函数的主要区别在于方法是绑定到特定类型的,而函数则是独立的。

package main

import (
    "fmt"
    "math"
)

type Circle struct {
    radius float64
}

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

func (c *Circle) updateRadius(r float64) {
    c.radius = r
}

func calculateArea(radius float64) float64 {
    return math.Pi * radius * radius
}

func main() {
    c := Circle{radius: 5}

    // 调用方法
    fmt.Println("Area using method:", c.area()) // 使用方法计算面积

    // 更新半径
    c.updateRadius(10)
    fmt.Println("Updated area using method:", c.area())

    // 调用函数
    fmt.Println("Area using function:", calculateArea(5)) // 使用函数计算面积
}

通过方法,Go语言实现了面向对象编程的特性,使得类型可以拥有自己的行为。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

roman_日积跬步-终至千里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值