Go 语言大白话入门 7 - 函数:把重复代码收起来

函数:把重复代码收起来

本章目标:把重复逻辑收进函数里,做到三件事就算过关:会写函数、会传参数、会接返回值。

常见坑位

  • 代码还没重复,就提前拆一堆函数,最后自己都看不懂。
  • 函数写好了,但忘了在 main 里调用,运行后啥都没发生。
  • 参数和返回值方向搞反:把"输入"写成"输出"。
  • 看见多返回值就紧张,其实就是"一个函数一次带回多个结果"。
  • 误以为函数里改了变量,外面一定会变。Go 默认是值传递,不一定会变。

先跑一个最小示例

先跑起来,再理解细节,这是最省时间的学习方式。

go 复制代码
package main

import "fmt"

func sayHello() {
    fmt.Println("你好,欢迎学习 Go")
}

func main() {
    sayHello()
    sayHello()
}

看到两行输出就说明你已经会"定义 + 调用"函数了。

带参数的函数

参数可以理解成"你喂给函数的数据"。

go 复制代码
package main

import "fmt"

func sayHi(name string) {
    fmt.Println("你好,", name)
}

func main() {
    sayHi("小张")
    sayHi("小刘")
}

这里 name string 的意思是:这个函数接收一个字符串类型的名字。

带返回值的函数

返回值就是"函数做完活后交回来的结果"。

go 复制代码
package main

import "fmt"

func add(a int, b int) int {
    return a + b
}

func main() {
    result := add(3, 5)
    fmt.Println(result) // 8
}

你可以把 add 理解成一个小工具:给它两个数,它回你一个和。

多返回值:Go 的高频写法

Go 很常见的一个特点是:一个函数可以返回多个值。

go 复制代码
package main

import "fmt"

func divide(a int, b int) (int, string) {
    if b == 0 {
        return 0, "不能除以零"
    }
    return a / b, ""
}

func main() {
    result, msg := divide(10, 2)
    fmt.Println(result, msg) // 5

    result2, msg2 := divide(10, 0)
    fmt.Println(result2, msg2) // 0 不能除以零
}

后面你会大量见到这种形式:

go 复制代码
value, err := someFunc()

前面是结果,后面是错误信息。下一章我们专门讲这个模式。

变长参数:参数个数不固定

当你不确定要传几个参数时,用 ...

go 复制代码
package main

import "fmt"

func sum(nums ...int) int {
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}

func main() {
    fmt.Println(sum(1, 2, 3))         // 6
    fmt.Println(sum(10, 20, 30, 40))  // 100
}

nums ...int 在函数内部本质上就是 []int,这点后面学切片时会越用越顺。

匿名函数:临时用一次,不想起名

匿名函数就是"没有名字的函数"。

go 复制代码
package main

import "fmt"

func main() {
    greet := func(name string) {
        fmt.Println("你好,", name)
    }

    greet("小张")
    greet("小李")

    func() {
        fmt.Println("这段逻辑只执行一次")
    }()
}

以后在排序、HTTP 处理、goroutine 里,你会反复看到匿名函数。

闭包:匿名函数能记住外面的变量

go 复制代码
package main

import "fmt"

func counter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

func main() {
    next := counter()
    fmt.Println(next()) // 1
    fmt.Println(next()) // 2
    fmt.Println(next()) // 3
}

这里返回的匿名函数记住了 count,这就是闭包。

初学先记两点:

  • 闭包可以访问外层变量。
  • 闭包会让外层变量活得更久,别长期挂大对象。

函数还能当参数传来传去

Go 里函数也是"值",可以传给别的函数。

go 复制代码
package main

import "fmt"

func apply(nums []int, fn func(int) int) []int {
    result := make([]int, 0, len(nums))
    for _, n := range nums {
        result = append(result, fn(n))
    }
    return result
}

func main() {
    doubled := apply([]int{1, 2, 3}, func(n int) int {
        return n * 2
    })
    fmt.Println(doubled) // [2 4 6]
}

fn func(int) int 的意思是:这个参数本身是一个函数,输入 int,输出 int

值传递和引用类型,一次讲清

Go 只有值传递。

  • intstring、结构体:函数里改变量,外面通常不变。
  • slicemap:拷贝的是结构头,但底层数据共享,改元素外面能看到。

所以"函数里改了,外面会不会变",要看你传入的具体类型。

过关标准

你可以按这份清单自检:

  • 能自己写出并调用一个无参数函数。
  • 能写一个带参数函数,并传入不同数据。
  • 能写一个带返回值函数,并正确接收结果。
  • 看到 value, err := xxx() 不再慌。
  • 能说清"值传递"和"slice/map 的表现差异"。

练习任务

  1. 写一个 welcome(),打印一句欢迎语。
  2. 写一个 sayHi(name string),分别给两个名字打招呼。
  3. 写一个 max(a, b int) int,返回较大的那个数。
  4. 写一个 sum(nums ...int) int,输入任意个整数并求和。

下一章衔接

下一章我们就接着讲 Go 里最常见的错误处理模式:value, err := ... 到底该怎么写才稳。

本文章属于《Go语言大白话入门》零基础全套教程,持续更新、全程实操练手。 微信公众号【码栈研说】超前更新,目前比掘金领先 30 章内容,完整连载、无删减、持续日更,需要完整系统学习的读者可自行前往查看。

文中部分内容借助 AI 辅助梳理,全文人工校验、实操落地。

相关推荐
明月_清风39 分钟前
显存即正义:不同显存容量能训多大的模型?一文说清硬件边界与训练策略
前端·后端·ai编程
阿kun要赚马内1 小时前
工具在langchain agent中的调用
人工智能·后端·python
IT_陈寒1 小时前
Vite的HMR在我项目上突然失效,排查三天找到离谱原因
前端·人工智能·后端
AINative软件工程1 小时前
LLM 应用的依赖注入工程实践:解耦 Client、Prompt 和 Tool Registry,让 AI 系统真正可测试可替换
后端·llm·前端工程化
腾渊信息科技公司1 小时前
Spring Boot集成TDengine实战:工业时序数据存储选型与迁移方案
spring boot·后端·tdengine
zoyation2 小时前
Spring Boot集成OAuth2授权码模式的三种登录策略
java·spring boot·后端
codeGoogle10 小时前
自研 IM 还是选择第三方 SDK?企业开发者应该如何权衡?
前端·后端·程序员
DLYSB_13 小时前
API 网关流量洪峰与突发 CC 攻击:我用 Go 写了个“现场物理防御哨兵”,把故障响应压缩到秒级
开发语言·后端·golang·报警灯
markinmarkin13 小时前
Spring 中Bean 的作用域有哪些?
java·后端·spring
橙子家14 小时前
使用方法 ToDictionary() 来优化查询时间复杂度:O(N*M) -> O(1*M)【C# 基础】
后端