2.5 函数定义、参数传递、返回值处理¶
学习目标¶
- 函数声明与定义
- 参数传递机制
- 多返回值设计模式
- 可变参数函数
- 匿名函数与闭包
函数声明与定义¶
函数是 Go 语言的基本构建块,通过 func 关键字声明。一个完整的函数包括函数名、参数列表、返回值列表和函数体。
基本语法¶
示例代码¶
package main
import "fmt"
// 无参数无返回值的函数
func sayHello() {
fmt.Println("Hello, World!")
}
// 带参数无返回值的函数
func greet(name string) {
fmt.Printf("Hello, %s!\n", name)
}
// 带参数和返回值的函数
func add(a int, b int) int {
return a + b
}
// 参数类型相同时可简写
func multiply(a, b int) int {
return a * b
}
func main() {
sayHello() // Hello, World!
greet("Alice") // Hello, Alice!
result := add(3, 5) // 8
product := multiply(4, 6) // 24
fmt.Printf("Addition result: %d\n", result)
fmt.Printf("Multiplication result: %d\n", product)
}
参数传递机制¶
Go 语言中所有参数都是值传递,即函数接收的是参数的副本。对于引用类型(切片、映射、通道、指针等),虽然传递的是值的副本,但这个副本指向相同的内存地址。
值类型参数示例¶
package main
import "fmt"
// 修改值类型参数(不影响原值)
func modifyValue(x int) {
x = x * 2
fmt.Println("Inside modifyValue:", x) // 20
}
func main() {
num := 10
modifyValue(num)
fmt.Println("After modifyValue:", num) // 10 (未改变)
}
引用类型参数示例¶
package main
import "fmt"
// 修改切片元素(会影响原切片)
func modifySlice(s []int) {
s[0] = 100
fmt.Println("Inside modifySlice:", s) // [100 2 3]
}
// 修改映射(会影响原映射)
func modifyMap(m map[string]int) {
m["key"] = 200
fmt.Println("Inside modifyMap:", m) // map[key:200]
}
func main() {
// 切片示例
slice := []int{1, 2, 3}
modifySlice(slice)
fmt.Println("After modifySlice:", slice) // [100 2 3]
// 映射示例
mapping := map[string]int{"key": 100}
modifyMap(mapping)
fmt.Println("After modifyMap:", mapping) // map[key:200]
}
指针参数示例¶
package main
import "fmt"
// 使用指针修改原值
func modifyWithPointer(x *int) {
*x = *x * 2
fmt.Println("Inside modifyWithPointer:", *x) // 20
}
func main() {
num := 10
modifyWithPointer(&num)
fmt.Println("After modifyWithPointer:", num) // 20 (已改变)
}
多返回值设计模式¶
Go 语言支持函数返回多个值,这是处理错误和返回额外信息的常用模式。
基本多返回值¶
package main
import (
"errors"
"fmt"
)
// 返回两个值:结果和错误信息
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
// 命名返回值
func calculate(a, b int) (sum int, product int, difference int) {
sum = a + b
product = a * b
difference = a - b
return // 裸返回,自动返回命名返回值
}
func main() {
// 错误处理示例
result, err := divide(10, 2)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Printf("Division result: %.2f\n", result) // 5.00
}
// 多返回值接收
s, p, d := calculate(5, 3)
fmt.Printf("Sum: %d, Product: %d, Difference: %d\n", s, p, d) // 8, 15, 2
// 忽略某些返回值
sumOnly, _, _ := calculate(10, 5)
fmt.Println("Sum only:", sumOnly) // 15
}
可变参数函数¶
可变参数函数可以接受数量可变的参数,使用 ... 语法。
基本可变参数¶
package main
import "fmt"
// 接收任意数量的int参数
func sum(numbers ...int) int {
total := 0
for _, num := range numbers {
total += num
}
return total
}
// 混合固定参数和可变参数
func greetAndSum(prefix string, numbers ...int) {
total := sum(numbers...)
fmt.Printf("%s: The sum is %d\n", prefix, total)
}
func main() {
// 可变参数调用
fmt.Println("Sum:", sum(1, 2, 3)) // 6
fmt.Println("Sum:", sum(1, 2, 3, 4, 5)) // 15
// 传递切片到可变参数
numbers := []int{10, 20, 30}
fmt.Println("Sum from slice:", sum(numbers...)) // 60
// 混合参数调用
greetAndSum("Result", 1, 2, 3, 4) // Result: The sum is 10
}
匿名函数与闭包¶
匿名函数是没有名称的函数,可以直接定义并使用。闭包是能够捕获外部变量的匿名函数。
匿名函数示例¶
package main
import "fmt"
func main() {
// 立即执行的匿名函数
func() {
fmt.Println("I'm an anonymous function!")
}()
// 将匿名函数赋值给变量
add := func(a, b int) int {
return a + b
}
result := add(3, 4)
fmt.Println("3 + 4 =", result) // 7
// 作为参数传递
operate := func(a, b int, operation func(int, int) int) int {
return operation(a, b)
}
multiply := func(x, y int) int {
return x * y
}
fmt.Println("5 * 6 =", operate(5, 6, multiply)) // 30
}
闭包示例¶
package main
import "fmt"
// 返回一个闭包
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
// 闭包实现生成器
func sequenceGenerator(start, step int) func() int {
current := start
return func() int {
value := current
current += step
return value
}
}
func main() {
// 计数器闭包
myCounter := counter()
fmt.Println(myCounter()) // 1
fmt.Println(myCounter()) // 2
fmt.Println(myCounter()) // 3
// 另一个独立的计数器
anotherCounter := counter()
fmt.Println(anotherCounter()) // 1
fmt.Println(anotherCounter()) // 2
// 生成器闭包
evenGenerator := sequenceGenerator(0, 2)
fmt.Println(evenGenerator()) // 0
fmt.Println(evenGenerator()) // 2
fmt.Println(evenGenerator()) // 4
oddGenerator := sequenceGenerator(1, 2)
fmt.Println(oddGenerator()) // 1
fmt.Println(oddGenerator()) // 3
fmt.Println(oddGenerator()) // 5
}
实际应用:中间件模式¶
package main
import "fmt"
// 中间件函数类型
type Middleware func(string) string
// 基础函数
func baseFunction(name string) string {
return "Hello, " + name
}
// 装饰器函数(接收函数,返回函数)
func decorate(middleware Middleware) Middleware {
return func(name string) string {
// 在执行前添加功能
fmt.Println("Before execution")
// 执行原函数
result := middleware(name)
// 在执行后添加功能
fmt.Println("After execution")
return result
}
}
func main() {
// 创建装饰后的函数
decoratedHello := decorate(baseFunction)
// 调用装饰后的函数
result := decoratedHello("Gopher")
fmt.Println("Result:", result)
/* 输出:
Before execution
After execution
Result: Hello, Gopher
*/
}
总结¶
通过本小节的学习,你应该掌握了:
- 函数声明与定义:使用
func关键字定义函数,理解参数和返回值的语法 - 参数传递机制:Go 语言的值传递特性,以及如何通过指针修改原值
- 多返回值设计模式:利用多返回值处理错误和返回额外信息
- 可变参数函数:使用
...语法定义和调用可变参数函数 - 匿名函数与闭包:创建和使用匿名函数,理解闭包的概念和应用
这些概念是 Go 语言编程的基础,熟练掌握它们将帮助你编写更加灵活和强大的代码。在实际开发中,多思考如何合理使用这些特性来构建清晰、可维护的程序结构。
上一节:2.4 指针与内存管理
下一节:2.6 结构体与方法