# 第 07 课:函数 ## 学习目标 第一课已经见过函数,这一课正式学习怎样拆分逻辑:参数传入数据、return 返回结果、多返回值表达是否有效,以及可变参数。 先手敲 [main.go](./main.go),运行成功后再阅读配套的 [main_test.go](./main_test.go)。不要一开始就同时抄完测试代码。本课共享根目录的 `go.mod`,无需第三方依赖。 ## 1. 声明和调用 ```go func add(a, b int) int { return a + b } result := add(2, 3) // 这一行调用放在 main 等函数内部。 ``` - `func`:声明函数。 - `add`:函数名。 - `a, b int`:两个 int 参数,完整写法是 `a int, b int`。 - 参数列表后面的 `int`:返回值类型。 - `return`:结束当前函数,并把结果交给调用方;不是结束整个程序。 与 C 相比,Go 的类型写在名称后面,而且支持直接返回多个值。不需要为后面定义的普通函数先写函数原型,也不能直接在另一个函数内部声明具名函数;后续会学匿名函数。 函数定义的前后顺序不决定调用顺序:程序完成初始化后进入 main,按实际调用执行。普通函数不会因为写在文件里就自动运行。 ## 2. 参数与值传递 ```go func increase(value int) int { value++ return value } ``` 调用 `increase(count)` 时,参数接收的是值的副本;修改内部整数 value,不会改变调用方的 count。想保留新值,可以用 `count = increase(count)`。 Go 的参数都是按值传递。但切片和 map 的值包含对底层数据的引用,复制参数值不等于复制全部数据:函数内修改切片元素或 map 条目,可能影响调用方。不能理解为“传进函数的任何数据都完全独立”。指针和更深入的共享行为后续单独学习。 ## 3. 多返回值与提前返回 ```go func average(scores []int) (float64, bool) { if len(scores) == 0 { return 0, false } // 累加计算…… return 85, true // 此处仅示意返回形式,完整计算见 main.go。 } ``` 调用时按顺序接收: ```go avg, ok := average(scores) ``` 本课约定:ok 为 true 才表示平均值有效。空切片提前返回 false;真正的零分则返回 `(0, true)`,避免混淆。 不需要某个返回值,可以使用 `_`,例如 `_, ok = average(nil)`。这里使用 `=`,因为 ok 已在同一作用域声明。 该函数只检查是否为空,不验证成绩是否位于 0~100,也暂不处理极大整数累加溢出。后续可在学习 error 后扩展输入校验。 ## 4. 可变参数 ```go func sum(values ...int) int { total := 0 for _, value := range values { total += value } return total } ``` - 调用 `sum(1, 2, 3)` 可以传多个整数,`sum()` 也合法。 - 在函数内部,values 作为 `[]int` 使用。 - 已有切片时写 `sum(numbers...)`,将切片展开为参数。 - 如果有其他参数,可变参数必须放在最后。 先掌握常规函数即可,本课暂不引入递归、闭包、泛型和 defer。 ## 编译并运行 在**仓库根目录**执行: ```powershell New-Item -ItemType Directory -Force bin/07_functions | Out-Null go build -o ./bin/07_functions/functions.exe ./lessons/07_functions .\bin\07_functions\functions.exe ``` 修改源码后需要重新编译。快速编译并运行: ```powershell go run ./lessons/07_functions ``` 或者从根目录进入本课: ```powershell Set-Location lessons/07_functions go run . ``` ## 预期输出 ```text === 无参数函数 === 欢迎学习 Go 函数 === 参数与返回值 === 2 + 3 = 5 === 值传递 === 原变量:10,函数返回的新值:11 === 多返回值与提前返回 === 平均分:85.00 没有成绩,无法计算平均分 === 可变参数 === 直接传入多个整数:6 展开切片作为参数:60 不传参数:0 ``` ## 测试 在仓库根目录执行: ```powershell go test -v ./lessons/07_functions ``` 测试覆盖加法、整数值传递、正常平均值、小数平均值、零分、空切片和可变参数。这里的小数用例是可以精确表示的 80.5,因此直接比较;一般浮点计算测试可能需要容差判断。 ## 动手练习 - [ ] 把 add 的输入改成负数,观察结果。 - [ ] 用 `count = increase(count)` 更新调用方变量,比较前后区别。 - [ ] 修改成绩切片,核对平均分;再改为空切片,观察提前返回。 - [ ] 新写 `max(a, b int) int`,返回较大的值。 - [ ] 为 max 添加相等、正数、负数的测试用例。 - [ ] 使用 sum 计算一组新整数的总和。 若另行手敲,放到本课的 `practice/` 子目录,用 `go run ./lessons/07_functions/practice` 运行,不要在同一个包中重复声明 main 或其他同名函数。 下一课建议学习结构体,把姓名、年龄、成绩等不同类型的数据组合成一个对象。 [上一课:map 键值对](../06_maps/README.md) · [学习总览](../../README.md)