GO语言基础教程(68)Go函数之函数定义和调用:Go函数修炼手册:从青铜到王者的必备秘籍,看完秒变代码大神!

一、函数?不就是代码的“乐高积木”嘛!

想象一下:你要组装一台炫酷的变形金刚,如果直接把上万个零件胡乱粘在一起,估计最后会变成一根抽象派艺术棍子。但如果你先把零件分类成“手臂模块”“头部模块”“喷火器模块”,组装起来不就轻松多了?

Go语言的函数就是这样的代码模块!它把零散的代码逻辑打包成独立功能块,让你可以像搭乐高一样构建复杂程序。比如处理用户登录、计算快递运费、生成验证码——每个功能都能封装成函数,随时调用永不掉链子。

来看个灵魂手绘级别的例子:

func 做饭(菜名 string) string {
    return "香喷喷的" + 菜名 + "出锅啦!"
}

当你调用做饭("红烧肉")时,程序就会乖乖端回"香喷喷的红烧肉出锅啦!"。这就是函数最直白的魅力——写一次代码,到处反复使用

二、函数定义解剖课:来看看Go函数的"身份证"

Go函数的基本结构简单到令人发指:

func 函数名(参数列表) 返回值类型 {
    // 函数体(具体执行的代码)
}

但细节里藏着小魔鬼,咱们逐个击破:

1. 函数名——霸气侧漏的起名艺术
Go官方建议使用驼峰命名法。比如计算工资的函数,别用calc_salary()这种蛇形命名,要用calcSalary()。如果是公开函数(能被其他包调用),首字母必须大写,比如CalculateSalary()

2. 参数列表——函数的"吃货指南"
参数就是函数要处理的原材料。比如点奶茶时你得说“我要一杯珍珠奶茶,加冰,三分糖”,这里的“珍珠奶茶”“加冰”“三分糖”就是参数:

func 点奶茶(品类 string, 加冰 bool, 糖分 int) {
    fmt.Printf("制作一杯%s,加冰:%t,糖度:%d分糖\n", 品类, 加冰, 糖分)
}

3. 返回值——函数的"成果汇报"
在Go里,返回值可以像这样直接声明在函数签名里:

func 计算年终奖(月薪 int, 绩效系数 float64) float64 {
    return float64(月薪) * 绩效系数 * 12
}

但更骚的是Go支持多返回值——这是处理错误的神器!

func 除法(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("兄弟,除数不能是零啊!")
    }
    return a / b, nil
}

三、函数调用实战:让代码动起来!

定义了函数不调用,就像买了奶茶不喝——纯属浪费!调用格式超级简单:函数名(参数)

基础调用三连击:

package main

import "fmt"

// 场景1:无参数无返回值
func 打招呼() {
    fmt.Println("嘿!今天代码写完了吗?")
}

// 场景2:带参数无返回值  
func 夸夸(名字 string, 优点 string) {
    fmt.Printf("%s同学,你的%s太棒了!\n", 名字, 优点)
}

// 场景3:带参数带返回值
func 加薪计算器(当前薪资 int, 涨幅百分比 float64) int {
    return int(float64(当前薪资) * (1 + 涨幅百分比))
}

func main() {
    // 调用1
    打招呼() // 输出:嘿!今天代码写完了吗?
    
    // 调用2
    夸夸("张三", "学习态度") // 输出:张三同学,你的学习态度太棒了!
    
    // 调用3
    新工资 := 加薪计算器(10000, 0.2)
    fmt.Printf("涨薪后每月拿%d,美滋滋!\n", 新工资) 
}

多返回值接收的两种姿势:

姿势一:变量全接收

结果, 错误信息 := 除法(9, 3)
if 错误信息 != nil {
    fmt.Println("出错啦:", 错误信息)
} else {
    fmt.Println("计算结果:", 结果) // 输出:计算结果: 3
}

姿势二:用下划线忽略不需要的值

结果, _ := 除法(9, 3) // 不关心错误信息,只想要结果
fmt.Println("结果是:", 结果)

四、进阶玩法:Go函数的黑科技工具箱

1. 变长参数——吃货的必胜客自助餐
当你不确定参数个数时,可以用...语法

func 点套餐(主食 string, 配菜 ...string) {
    fmt.Printf("主食:%s\n", 主食)
    fmt.Println("配菜:")
    for _, 菜 := range 配菜 {
        fmt.Println("  -", 菜)
    }
}

// 调用
点套餐("牛排", "沙拉", "薯条", "可乐") // 配菜想吃多少传多少!

2. 命名返回值——给返回值提前发名片
直接在函数签名里给返回值起名字,函数体内直接操作:

func 计算年终奖(月薪 int, 绩效系数 float64) (总奖金 float64, 评价 string) {
    // 直接使用预定义的返回值变量
    总奖金 = float64(月薪) * 绩效系数 * 12
    
    if 绩效系数 > 1.5 {
        评价 = "绩效王者!"
    } else {
        评价 = "再接再厉!"
    }
    
    return // 相当于 return 总奖金, 评价
}

3. 匿名函数——神秘的特工代号
不需要名字的函数,随用随定义:

func main() {
    // 方式1:直接调用匿名函数
    func(名字 string) {
        fmt.Println("你好,", 名字)
    }("张三")

    // 方式2:赋值给变量
    打招呼 := func(名字 string) string {
        return "嘿," + 名字 + "!"
    }
    
    fmt.Println(打招呼("李四")) // 输出:嘿,李四!
}

4. 闭包——函数的"记忆面包"
闭包就是能记住外部变量的函数,像多啦A梦的记忆面包:


func 计数器() func() int {
    计数 := 0
    return func() int {
        计数++
        return 计数
    }
}

func main() {
    我的计数器 := 计数器()
    fmt.Println(我的计数器()) // 1
    fmt.Println(我的计数器()) // 2 - 还记得上次的值
    fmt.Println(我的计数器()) // 3
}

五、错误处理最佳实践:避开代码路上的坑

Go函数处理错误的经典模式就是返回(结果, error),使用时一定要检查error!

func 办理信用卡(年龄 int, 年收入 float64) (string, error) {
    if 年龄 < 18 {
        return "", errors.New("未成年无法办理")
    }
    if 年收入 < 50000 {
        return "", errors.New("年收入不足5万")
    }
    return "白金信用卡", nil
}

func main() {
    卡类型, 错误 := 办理信用卡(25, 80000)
    if 错误 != nil {
        fmt.Println("办卡失败:", 错误)
        return
    }
    fmt.Println("恭喜获得:", 卡类型)
}

六、完整实战:打造个人理财小助手

现在我们来个综合应用,实现一个迷你理财系统:

package main

import (
    "errors"
    "fmt"
)

// 定义账户结构
type 账户 struct {
    余额 float64
}

// 存款函数
func (acc *账户) 存款(金额 float64) (float64, error) {
    if 金额 <= 0 {
        return acc.余额, errors.New("存款金额必须大于0")
    }
    acc.余额 += 金额
    return acc.余额, nil
}

// 取款函数  
func (acc *账户) 取款(金额 float64) (float64, error) {
    if 金额 <= 0 {
        return acc.余额, errors.New("取款金额必须大于0")
    }
    if 金额 > acc.余额 {
        return acc.余额, errors.New("余额不足")
    }
    acc.余额 -= 金额
    return acc.余额, nil
}

// 查询余额
func (acc *账户) 查询() float64 {
    return acc.余额
}

// 转账函数 - 使用多返回值处理错误
func 转账(来源 *账户, 目标 *账户, 金额 float64) (bool, error) {
    if _, 错误 := 来源.取款(金额); 错误 != nil {
        return false, fmt.Errorf("转账失败: %v", 错误)
    }
    
    if _, 错误 := 目标.存款(金额); 错误 != nil {
        // 取款成功但存款失败,需要回滚
        来源.存款(金额)
        return false, fmt.Errorf("转账回滚: %v", 错误)
    }
    
    return true, nil
}

func main() {
    // 初始化两个账户
    我的账户 := &账户{余额: 1000}
    朋友账户 := &账户{余额: 500}
    
    fmt.Printf("初始状态:我有%.2f元,朋友有%.2f元\n", 
        我的账户.查询(), 朋友账户.查询())
    
    // 测试存款
    if 新余额, 错误 := 我的账户.存款(500); 错误 != nil {
        fmt.Println("存款失败:", 错误)
    } else {
        fmt.Printf("存款成功,当前余额: %.2f\n", 新余额)
    }
    
    // 测试转账
    if 成功, 错误 := 转账(我的账户, 朋友账户, 300); 错误 != nil {
        fmt.Println("转账失败:", 错误)
    } else {
        fmt.Println("转账成功!")
    }
    
    fmt.Printf("最终状态:我有%.2f元,朋友有%.2f元\n", 
        我的账户.查询(), 朋友账户.查询())
}

这个示例涵盖了:

  • 带错误处理的函数设计
  • 多返回值的实际应用
  • 结构体方法的定义
  • 函数间的协作调用

七、避坑指南 & 性能优化小贴士

新手常见坑:

  1. 忘了处理错误结果, _ := 危险操作()——这个下划线可能让你debug到凌晨三点!
  2. 值传递陷阱:Go默认是值传递,修改结构体时记得用指针func (a *Account)
  3. 变量遮蔽:函数参数与全局变量重名时,函数内只会操作参数

性能优化技巧:

  1. 小结构体直接传值,大结构体用指针
  2. 频繁调用的简单函数会被编译器内联优化
  3. 避免在循环内部定义匿名函数(可能导致不必要的内存分配)

结语

函数就像编程世界里的超能力——一旦掌握,你就能把复杂问题拆解成小块,然后像搭积木一样优雅地解决它们。从最简单的无参函数到复杂的闭包应用,Go语言的函数系统既简洁又强大。

记住:好的函数应该像一段好文章——功能单一、命名清晰、逻辑自洽。现在就去重构你的代码吧,让每个函数都成为你代码库里的精品模块!


思考题:你最近写的代码里,有没有哪个函数超过了50行?试试看能不能把它拆分成几个更专注的小函数,你会发现代码瞬间变得清爽多了!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

值引力

持续创作,多谢支持!

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

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

打赏作者

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

抵扣说明:

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

余额充值