GO_函数_2

一、带多返回值函数(有参数 + 多命名返回值)

go

运行

Go 复制代码
package main

import "fmt"

// 有参数、多命名返回值函数:求两个数最大最小值
func MaxAndMin(a, b int) (max, min int) {
	if a > b {
		max = a
		min = b
	} else {
		max = b
		min = a
	}
	return // 命名返回值可直接空return,自动返回变量值
}

func main() {
	// 接收全部返回值
	max, min := MaxAndMin(10, 20)
	fmt.Printf("max = %d, min = %d\n", max, min)

	// 匿名变量 _ 丢弃不需要的返回值
	a, _ := MaxAndMin(10, 20)
	fmt.Printf("最大值a = %d\n", a)
}

核心知识点

  1. 命名返回值:函数头部直接定义返回变量,函数内可直接赋值,return 无需写值;
  2. 多返回值接收:变量个数必须和返回值匹配;
  3. 匿名变量 _:用来舍弃不需要的返回值,避免变量未使用报错。

二、普通函数调用(命名返回值函数)

go

运行

Go 复制代码
package main

import "fmt"

// 命名返回值a
func funca() (a int) {
	a = 111
	fmt.Println("funca a =", a)
	return
}

func main() {
	a := funca()
	fmt.Printf("main a = %d", a)
}

三、递归函数(函数调用自身)

核心规则:递归必须设置终止条件,否则无限递归栈溢出

示例 1:递归打印数字(理解执行顺序)

go

运行

Go 复制代码
package main

import "fmt"

func test(a int) {
	// 终止条件
	if a == 1 {
		fmt.Println("a =", a)
		return
	}
	// 递归调用自身
	test(a - 1)
	// 递归回溯时执行
	fmt.Println("a = ", a)
}

func main() {
	test(3)
	fmt.Println("main")
}
执行流程拆解 test (3)
  1. test (3) → 调用 test (2)
  2. test (2) → 调用 test (1)
  3. test (1):触发终止条件,打印a=1,return
  4. 回溯执行 test (2) 剩余代码:打印a=2
  5. 回溯执行 test (3) 剩余代码:打印a=3
  6. 回到 main 打印main输出结果:

plaintext

Go 复制代码
a = 1
a =  2
a =  3
main

示例 2:递归实现 1~100 累加求和

go

运行

Go 复制代码
package main

import "fmt"

// 循环实现累加(对比递归)
func test01() (sum int) {
	for i := 1; i <= 100; i++ {
		sum += i
	}
	return
}

// 递归实现累加
func test02(i int) int {
	// 递归终止条件
	if i == 1 {
		return 1
	}
	// 递推公式:当前值 + 前面所有数的和
	return i + test02(i-1)
}

func main() {
	sum := test02(100)
	fmt.Println("1~100累加 sum = ", sum)
}

递归公式推导:sum(n) = n + sum(n-1),边界 sum(1)=1

四、函数类型(函数是一等数据类型)

知识点

  1. Go 中函数属于独立数据类型,可以定义变量、赋值、传参;
  2. type 自定义函数类型别名,统一规范同类函数签名;

go

运行

Go 复制代码
package main

import "fmt"

// 普通加减函数
func Add(a, b int) int {
	return a + b
}
func Minus(a, b int) int {
	return a - b
}

// 自定义函数类型:接收两个int,返回int
type FuncType func(int, int) int

func main() {
	// 方式1:直接调用函数
	result := Add(1, 1)
	fmt.Println("result = ", result)

	// 方式2:函数类型变量,接收函数赋值
	var fTest FuncType
	fTest = Add
	result = fTest(10, 20)
	fmt.Println("result2 = ", result)

	// 更换函数实现,同一个变量调用不同逻辑(多态)
	fTest = Minus
	result = fTest(10, 5)
	fmt.Println("result3 = ", result)
}

五、回调函数(参数为函数类型)

核心概念

回调函数:函数的参数是另一个函数类型,主函数内部调用传入的外部函数;优势:多态特性,同一套调用逻辑,传入不同函数实现不同功能,业务解耦。

go

运行

Go 复制代码
package main

import "fmt"

type FuncType func(int, int) int

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

// Calc 回调函数:第二个参数为函数,外部传入具体运算逻辑
func Calc(a, b int, fTest FuncType) (result int) {
	fmt.Println("Calc 统一计算入口")
	result = fTest(a, b) // 执行外部传入的函数
	return
}

func main() {
	// 将Add函数作为参数传入Calc
	a := Calc(1, 1, Add)
	fmt.Println("计算结果a = ", a)
}

六、匿名函数 & 闭包

1. 基础概念

  • 匿名函数:没有函数名的函数,可直接定义立即执行,或赋值给变量;
  • 闭包:匿名函数捕获外层同作用域变量 / 常量,即使脱离外层作用域,变量依然保留状态。

2. 匿名函数基础用法

(1)定义后立即执行 func(){}(参数)

go

运行

Go 复制代码
package main

import "fmt"

func main() {
	a := 10
	str := "mike"

	// 无参无返回匿名函数,立即执行
	func() {
		fmt.Printf("无参匿名:a=%d, str=%s\n", a, str)
	}()

	// 带参数匿名函数,立即传参调用
	func(i, j int) {
		fmt.Printf("带参匿名 i=%d, j=%d\n", i, j)
	}(10, 20)

	// 有参有返回值匿名函数
	max, min := func(i, j int) (max, min int) {
		if i > j {
			max = i
			min = j
		} else {
			max = j
			min = i
		}
		return
	}(10, 20)
	fmt.Printf("匿名函数最值 max=%d min=%d\n", max, min)
}
(2)匿名函数赋值给变量

go

运行

Go 复制代码
func main() {
	a := 10
	str := "mike"
	// 匿名函数赋值变量,形成闭包捕获外部变量
	f1 := func() {
		fmt.Printf("捕获外部变量 a=%d str=%s\n", a, str)
	}
	f1() // 调用
}

3. 闭包修改外部变量(共享引用)

闭包捕获的是变量引用,内部修改会同步改变外层变量:

go

运行

Go 复制代码
func main() {
	a := 10
	str := "mike"
	// 闭包修改外部变量
	func() {
		a = 666
		str = "go"
		fmt.Printf("闭包内部:a = %d, str = %s\n", a, str)
	}()
	fmt.Printf("外层变量:a = %d, str = %s\n", a, str)
}

4. 闭包核心特性:延长局部变量生命周期

普通函数局部变量每次调用都会重置;闭包会持有外层局部变量,多次调用保留变量状态

go

运行

Go 复制代码
package main

import "fmt"

// 普通函数:局部变量x每次调用重新初始化
func test01() int {
	var x int
	x++
	return x * x
}

// 返回闭包函数,捕获局部变量x
func test02() func() int {
	var x int
	// 返回匿名闭包
	return func() int {
		x++
		return x * x
	}
}

func main() {
	fmt.Println("普通函数调用:")
	fmt.Println(test01()) //1
	fmt.Println(test01()) //1
	fmt.Println(test01()) //1

	fmt.Println("闭包调用:")
	f := test02() // f持有变量x,x不会销毁
	fmt.Println(f()) //1
	fmt.Println(f()) //4
	fmt.Println(f()) //9
	fmt.Println(f()) //16
	fmt.Println(f()) //25
}

原理:只要闭包变量f还在使用,捕获的局部变量x就不会被 GC 回收,持续保存修改后的数值。

七、defer 延迟调用

1. 基础特性

  1. defer 只能写在函数内部;
  2. 作用:延迟函数调用,当前函数执行完毕、return 之前执行;
  3. 多个 defer 遵循后进先出(栈结构),后定义先执行。

示例 1:单个 defer

go

运行

Go 复制代码
package main

import "fmt"

func main() {
	defer fmt.Println("延迟打印 bbbbbbbbbbbbbb")
	fmt.Println("先执行 aaaaaaaaaaaa")
}

输出顺序:

plaintext

Go 复制代码
aaaaaaaaaaaa
bbbbbbbbbbbbbb

示例 2:多个 defer 后进先出

go

运行

Go 复制代码
func main() {
	defer fmt.Println("aaaaaaa")
	defer fmt.Println("bbbbbbb")
	defer fmt.Println("ccccccccc")
}

执行顺序:ccccccccc → bbbbbbb → aaaaaaa

2. defer + 匿名闭包组合使用

重点:defer 定义时立刻拷贝参数值,延迟执行时不会读取后续修改

go

运行

Go 复制代码
package main

import "fmt"

func main() {
	a := 10
	b := 20
	// 定义defer时直接传入10、20,参数值已固定
	defer func(x, y int) {
		fmt.Printf("defer内部:a = %d, b = %d\n", x, y)
	}(a, b)

	// 后续修改变量不影响defer内打印
	a = 111
	b = 222
	fmt.Printf("外部打印:a = %d, b = %d\n", a, b)
}

输出结果:

plaintext

复制代码
外部打印:a = 111, b = 222
defer内部:a = 10, b = 20

完整知识总结(复习速记)

  1. 多返回值函数 :支持命名返回值,_匿名变量丢弃返回值;
  2. 递归:必须终止条件,先递推再回溯;
  3. 函数类型:函数是一等类型,type 自定义签名,可赋值变量;
  4. 回调函数:参数传入函数,实现多态、逻辑解耦;
  5. 匿名函数:无名称,支持定义后立即执行;
  6. 闭包:捕获外层变量引用,延长局部变量生命周期;
  7. defer 延迟:函数退出前执行,多个 defer 后进先出,定义时固化参数。
相关推荐
apihz2 小时前
经纬度制作高清卫星图片免费 API 接口详解
开发语言·ios·swift
娇气的红酒2 小时前
微软对PHP支持的改进,及其它一些胡言乱语
开发语言·microsoft·php
霸道流氓气质2 小时前
Harness Engineering 模块化指令实战:告别 600 行巨型 AGENTS.md
开发语言·人工智能·python
吠品2 小时前
Mac定时关机、开机自动化:用shutdown和pmset就够了
android·java·开发语言
asdzx672 小时前
C# 通过模板生成 Word 文档:文本与图片占位符替换完整攻略
开发语言·c#·word
wbs_scy2 小时前
仿 muduo 高并发服务器项目:实现轻量级日志宏,方便后续网络库调试
开发语言·php
Huangjin007_2 小时前
【C++11篇(四)】新的类功能、lambda 与包装器详解
开发语言·c++
2zcode2 小时前
基于MATLAB图像处理的啤酒瓶口缺陷检测系统设计与实现
开发语言·图像处理·matlab
进击的程序猿~2 小时前
Go Zero源码阅读2
开发语言·后端·golang