函数
函数是代码复用的基础。Go的函数有一些独特的特性,比如多返回值,超级好用!我记得当年学Java的时候,要返回多个值还得封装成对象,特别麻烦。Go直接支持多返回值,这个设计真的太贴心了!
函数定义
基本语法
func 函数名(参数列表) 返回值类型 {
// 函数体
return 返回值
}
// 无参数,无返回值
func sayHello() {
fmt.Println("Hello!")
}
// 有参数,有返回值
func add(a int, b int) int {
return a + b
}
// 参数类型相同可以简写
func add(a, b int) int {
return a + b
}
对比其他语言
// Java
public int add(int a, int b) {
return a + b;
}
// JavaScript
function add(a, b) {
return a + b;
}
// Go - 类型在后面,更简洁
func add(a, b int) int {
return a + b
}
多返回值
Go函数可以返回多个值,这是Go的一大特色!
// 返回两个值
func divide(a, b int) (int, int) {
quotient := a / b
remainder := a % b
return quotient, remainder
}
func main() {
q, r := divide(17, 5)
fmt.Printf("17 ÷ 5 = %d 余 %d\n", q, r) // 17 ÷ 5 = 3 余 2
}
命名返回值
func divide(a, b int) (quotient, remainder int) {
quotient = a / b
remainder = a % b
return // 直接return,自动返回命名的变量
}
忽略返回值
q, _ := divide(17, 5) // 只要商,忽略余数
Java程序员注意
Java返回多个值要封装成对象,Go直接返回多个值,省事!
// Java需要创建一个类
public class DivideResult {
int quotient;
int remainder;
}
错误处理模式
Go没有异常,用多返回值处理错误:
func readFile(filename string) (string, error) {
data, err := os.ReadFile(filename)
if err != nil {
return "", err // 返回错误
}
return string(data), nil // nil表示没有错误
}
func main() {
content, err := readFile("test.txt")
if err != nil {
fmt.Println("读取失败:", err)
return
}
fmt.Println(content)
}
这是Go最常见的代码模式,你会写很多 if err != nil!
可变参数
// ...int 表示可以接收任意数量的int参数
func sum(nums ...int) int {
total := 0
for _, num := range nums {
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, 5}
fmt.Println(sum(nums...)) // 15
}
函数是一等公民
Go的函数可以:
- 赋值给变量
- 作为参数传递
- 作为返回值
函数变量
// 函数赋值给变量
add := func(a, b int) int {
return a + b
}
fmt.Println(add(1, 2)) // 3
// 函数类型
type Calculator func(int, int) int
var calc Calculator = add
fmt.Println(calc(3, 4)) // 7
函数作为参数
// 高阶函数
func apply(nums []int, fn func(int) int) []int {
result := make([]int, len(nums))
for i, n := range nums {
result[i] = fn(n)
}
return result
}
func main() {
nums := []int{1, 2, 3, 4, 5}
// 传入函数
doubled := apply(nums, func(n int) int {
return n * 2
})
fmt.Println(doubled) // [2 4 6 8 10]
squared := apply(nums, func(n int) int {
return n * n
})
fmt.Println(squared) // [1 4 9 16 25]
}
对比JavaScript
// JavaScript
const doubled = nums.map(n => n * 2);
// Go
doubled := apply(nums, func(n int) int { return n * 2 })
思想是一样的,就是语法不同!
匿名函数和闭包
匿名函数
// 立即执行的匿名函数
func() {
fmt.Println("我是匿名函数")
}()
// 带参数
func(name string) {
fmt.Println("Hello,", name)
}("Go")
闭包
闭包可以捕获外部变量:
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
c := counter()
fmt.Println(c()) // 1
fmt.Println(c()) // 2
fmt.Println(c()) // 3
c2 := counter() // 新的计数器
fmt.Println(c2()) // 1
}
闭包的坑
// 错误示范
funcs := []func(){}
for i := 0; i < 3; i++ {
funcs = append(funcs, func() {
fmt.Println(i)
})
}
for _, f := range funcs {
f() // 输出: 3 3 3(都是3!)
}
// 正确做法
funcs := []func(){}
for i := 0; i < 3; i++ {
i := i // 创建新的变量
funcs = append(funcs, func() {
fmt.Println(i)
})
}
for _, f := range funcs {
f() // 输出: 0 1 2
}
递归函数
// 阶乘
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 main() {
fmt.Println(factorial(5)) // 120
fmt.Println(fibonacci(10)) // 55
}
init 函数
init 函数在 main 之前自动执行,用于初始化:
package main
import "fmt"
var name string
func init() {
fmt.Println("init函数执行")
name = "Go"
}
func main() {
fmt.Println("main函数执行")
fmt.Println("name =", name)
}
// 输出:
// init函数执行
// main函数执行
// name = Go
执行顺序:导入包 → 包级别变量 → init函数 → main函数
实战案例:简易计算器
package main
import "fmt"
// 定义运算函数类型
type Operation func(a, b float64) float64
// 运算函数
func add(a, b float64) float64 { return a + b }
func subtract(a, b float64) float64 { return a - b }
func multiply(a, b float64) float64 { return a * b }
func divide(a, b float64) float64 {
if b == 0 {
fmt.Println("错误: 除数不能为0")
return 0
}
return a / b
}
// 计算函数
func calculate(a, b float64, op Operation) float64 {
return op(a, b)
}
func main() {
a, b := 10.0, 3.0
fmt.Printf("%.1f + %.1f = %.1f\n", a, b, calculate(a, b, add))
fmt.Printf("%.1f - %.1f = %.1f\n", a, b, calculate(a, b, subtract))
fmt.Printf("%.1f × %.1f = %.1f\n", a, b, calculate(a, b, multiply))
fmt.Printf("%.1f ÷ %.1f = %.2f\n", a, b, calculate(a, b, divide))
}
输出:
10.0 + 3.0 = 13.0
10.0 - 3.0 = 7.0
10.0 × 3.0 = 30.0
10.0 ÷ 3.0 = 3.33
实战案例:字符串工具函数
package main
import (
"fmt"
"strings"
)
// 判断字符串是否为空
func isEmpty(s string) bool {
return len(strings.TrimSpace(s)) == 0
}
// 首字母大写
func capitalize(s string) string {
if len(s) == 0 {
return s
}
return strings.ToUpper(s[:1]) + s[1:]
}
// 字符串重复
func repeat(s string, n int) string {
return strings.Repeat(s, n)
}
// 反转字符串
func reverse(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
func main() {
fmt.Println(isEmpty("")) // true
fmt.Println(isEmpty(" ")) // true
fmt.Println(isEmpty("hello")) // false
fmt.Println(capitalize("hello")) // Hello
fmt.Println(repeat("Go ", 3)) // Go Go Go
fmt.Println(reverse("Hello世界")) // 界世olleH
}
练习
- 写一个函数,接收两个数,返回它们的最大值和最小值
- 写一个函数,计算切片中所有数字的平均值
- 使用闭包实现一个累加器
参考答案
package main
import "fmt"
// 1. 返回最大值和最小值
func minMax(a, b int) (min, max int) {
if a < b {
return a, b
}
return b, a
}
// 2. 计算平均值
func average(nums []float64) float64 {
if len(nums) == 0 {
return 0
}
sum := 0.0
for _, n := range nums {
sum += n
}
return sum / float64(len(nums))
}
// 3. 累加器闭包
func accumulator(initial int) func(int) int {
sum := initial
return func(n int) int {
sum += n
return sum
}
}
func main() {
// 1. 测试minMax
min, max := minMax(5, 3)
fmt.Printf("min=%d, max=%d\n", min, max)
// 2. 测试average
nums := []float64{1, 2, 3, 4, 5}
fmt.Printf("平均值: %.2f\n", average(nums))
// 3. 测试累加器
acc := accumulator(0)
fmt.Println(acc(10)) // 10
fmt.Println(acc(20)) // 30
fmt.Println(acc(30)) // 60
}
函数是Go的核心,下一节学习数组与切片!
