当前位置: 首页 > article >正文

3.5 Go(特殊函数)

目录

一、匿名函数

1、匿名函数的特点:

2、匿名函数代码示例

2、匿名函数的类型

二、递归函数

1. 递推公式版本

2. 循环改递归

三、嵌套函数

1、嵌套函数用途

2、代码示例

3、作用域 & 变量生存周期

四、闭包

1、闭包使用场景

2、代码示例

五、Defer

1、defer 关键特点

2、defer 代码示例

3、defer 在 return 之后执行

4、defer 绑定的是“实参值”,执行顺序遵循 LIFO


一、匿名函数

匿名函数是没有名称的函数,通常在定义时直接使用其功能,而不需要为其命名。匿名函数的定义一般是在代码中动态地创建并立即使用。

1、匿名函数的特点:

无名称:与普通函数不同,匿名函数不需要指定名字。

即时使用:常用于需要临时函数的场景,尤其是作为参数传递给其他函数。

应用场景

匿名函数的应用广泛,尤其是在以下两种情况下:

1. 回调函数:匿名函数常作为回调函数使用,尤其是在处理异步操作时。例如,JavaScript中的事件监听、Go中的channel处理等。

2、匿名函数代码示例

  • 匿名函数被作为回调来处理异步任务。
 func() {
    fmt.Println("异步操作完成!")
}()

钩子函数(Hook):钩子函数允许在特定事件发生时执行额外的代码。匿名函数可以作为钩子函数,方便在特定逻辑中插入自定义操作。

type Hook func()

func RegisterHook(h Hook) {
    h()
}

func main() {
    RegisterHook(func() {
        fmt.Println("执行钩子函数!")
    })
}

2、匿名函数的类型

匿名函数与命名函数一样,也有自己的输入参数和输出参数,并且可以像普通函数一样进行类型定义。匿名函数的类型由其参数类型和返回值类型来决定。

入参和出参一致的函数:如果两个匿名函数的输入参数和返回值类型完全相同,我们称这两个函数为相同类型的匿名函数。即使它们的具体实现不同,只要入参和出参相同,它们就可以互换使用。

函数体不同:类型相同的匿名函数不代表它们的具体实现逻辑必须一样。即使两个匿名函数的入参、出参相同,它们在处理逻辑上的实现可以完全不同。

二、递归函数

递归(Recursion)是一种 函数自己调用自己 的编程技巧,通常用于解决 分解问题重复子问题 的场景,比如 阶乘、斐波那契数列、树遍历 等。

递归的关键点

1.边界条件(Base Case): 防止无限递归,确保递归能终止。

2.递归前进(Recursive Case): 递归调用自身,逐步逼近边界条件。

3.递归返回(Return Phase): 当达到边界条件时,返回结果,逐层回溯。

递归实现方式
 

1. 递推公式版本
 

适用于具有明显的数学递推公式的问题,如 阶乘斐波那契数列 等。

计算 n 的阶乘(n!)

阶乘公式:

n! = n \times (n-1)!, 当 n > 1 时

0! = 1, 递归终止条件

package main

import "fmt"

// 递归实现阶乘
func factorial(n int) int {
	// 递归终止条件
	if n == 0 {
		return 1
	}
	// 递归前进:n * (n-1)!
	return n * factorial(n-1)
}

func main() {
	fmt.Println("5 的阶乘是:", factorial(5)) // 输出 120
}

2. 循环改递归

有些递归实现的算法可以改用循环来提高效率,比如斐波那契数列。

package main

import "fmt"

// 斐波那契数列(循环版)
func fibonacciLoop(n int) int {
	if n == 0 {
		return 0
	}
	if n == 1 {
		return 1
	}
	a, b := 0, 1
	for i := 2; i <= n; i++ {
		a, b = b, a+b
	}
	return b
}

func main() {
	fmt.Println("斐波那契数列前10项(循环版):")
	for i := 0; i < 10; i++ {
		fmt.Print(fibonacciLoop(i), " ")
	}
}

三、嵌套函数

1、嵌套函数用途

逻辑封装,避免代码重复

当某些操作只在特定函数内部使用,并且不会被外部调用时,可以使用嵌套函数来减少代码重复,提升代码可读性。

2、代码示例

函数中定义另外一个函数,
函数嵌套形成嵌套作用域
package main

import "fmt"

// 函数嵌套形成嵌套作用域
func outer() {
	c := 99
	var inner = func() {
		c = 100                         //修改outer的c值,修改完成后会覆盖outer,其实修改的outer 上的变量内容
		fmt.Println("inner c==", c, &c) //对outer的c进行赋值修改
		c := c + 1                      //inner的局部变量,外部无法调用。
		fmt.Println("inner c==", c, &c)
	}
	inner()
	fmt.Println("outer c==", c, &c)

}

func main() {
	outer() //先执行inner函数

}

3、作用域 & 变量生存周期

在 Go 语言中,变量的作用域决定了它的 生存周期可访问性

1. 全局作用域:在 package 级别定义的变量,整个包都能访问。

2. 函数作用域:在函数内定义的变量,只有这个函数可以访问。

3. 嵌套作用域

  • inner() 可以访问 outer() 的 c(因为 Go 支持闭包)。
  • 但 inner() 不能修改 outer()c 的地址,只能修改其值。

四、闭包

1、闭包使用场景

闭包使用的原因,解决栈帧消亡后值不能消失。变量从栈中逃逸到堆上,本次调用的栈消亡,堆不消亡

2、代码示例

func outer() func() {
    c := 99 // 变量 c 在 outer 作用域内
    var inner = func() {
        fmt.Println("1  inner c==", c, &c) // 这里 c 仍然是 outer 作用域的变量
    }
    fmt.Println("2   outer c==", c, &c) // 这里打印 outer 内的 c

    return inner // 返回 inner 函数,但并不调用它
}

outer() 里定义了 c,然后 inner 函数捕获了 c,但 inner 并没有被执行,而是作为返回值返回。

• outer() 先执行,打印 c 的值和地址。

• main() 里 fn := outer(),将 inner 赋值给 fn,但 inner 还未执行,所以 c 依然没有被打印或修改。

• fmt.Println(fn) 只是打印 fn 这个函数本身的地址,而 inner 仍然未执行。

• inner 形成闭包,捕获 c,并且 c 不会因为 outer 结束而消失。

• 只有当 fn() 被执行时,inner 才会访问 c 并打印它。

• 由于 c 被 inner 捕获,它存活的时间超出了 outer 的生命周期(变量逃逸到堆上)。

五、Defer

defer 关键字用于推迟执行某个函数,直到**当前函数返回(正常 return 或 panic 发生时)**才会执行。

1、defer 关键特点

1. 延迟执行:defer 语句不会立即执行,而是在当前函数返回时执行。

2. 执行顺序LIFO(后进先出),即多个 defer 按照注册顺序的反向顺序执行。

3. 保证执行:无论是正常返回(return)还是异常崩溃(panic),defer 代码块都会执行。

4. 不执行的情况:os.Exit()log.Fatal() 会导致程序立即退出,使 defer 代码块无法执行。

2、defer 代码示例

package main

import "fmt"

func main() {
	defer fmt.Println("1") // 最后执行
	defer fmt.Println("2") // 倒数第二执行
	defer fmt.Println("3") // 倒数第三执行

	fmt.Println("start")
	fmt.Println("stop")
}

代码输出结果:

start

stop

3

2

1

fmt.Println("start") 立即执行

• fmt.Println("stop") 立即执行

• defer 语句按照 后进先出 顺序执行

3、defer 在 return 之后执行

package main

import "fmt"

func test() {
	defer fmt.Println("defer 执行")
	fmt.Println("函数内执行")
	return
}

func main() {
	test()
	fmt.Println("函数返回后执行")
}

输出结果:

函数内执行
defer 执行
函数返回后执行

4、defer 绑定的是“实参值”,执行顺序遵循 LIFO

package main

import (
	"fmt"
)

func main() {
	a := 1024
	fmt.Println("start ~~~~~~~~~~~~~~")

	defer fmt.Println(a) // defer 1: 绑定值 1024
	a++                  // a = 1025

	defer fmt.Println(a) // defer 2: 绑定值 1025
	a++                  // a = 1026

	defer fmt.Println(a) // defer 3: 绑定值 1026
	a++                  // a = 1027

	fmt.Println("stop~~~~~~~~~~~~~~~~")
}

输出结果

start ~~~~~~~~~~~~~~
stop~~~~~~~~~~~~~~~~
1026
1025
1024

代码执行时,defer 语句在注册时就会确定参数的值,不会受到后续 a 值变化的影响。

defer 语句执行时,实参值已经确定

• defer fmt.Println(a) 注册时,参数 a 当前的值会被存下来,后续 a 的变化不会影响 defer 绑定的值。

多个 defer 语句按 LIFO 顺序执行后进先出)。


http://www.kler.cn/a/535335.html

相关文章:

  • “卫星-无人机-地面”遥感数据快速使用及地物含量计算的实现方法
  • Spring Bean生命周期
  • spy-debugger + Charles 调试移动端/内嵌小程序H5
  • vs code 使用教程
  • MongoDB 查询文档
  • 【免费】2007-2019年各省科技支出占一般公共预算支出的比重数据
  • 计算机毕业设计hadoop+spark+hive民宿推荐系统 酒店推荐系统 民宿价格预测 酒店价预测 机器学习 深度学习 Python爬虫 HDFS集群
  • 嵌入式硬件篇---OpenMV基本使用自动增益\曝光\白平衡
  • Unity VideoPlayer播放视屏不清晰的一种情况
  • 网络安全风险量化值 网络安全风险控制
  • C# OpenCV机器视觉:利用TrashNet实现垃圾分类
  • Google地图瓦片爬虫——进阶版
  • 计算机网络之物理层通信基础(电路交换、报文交换与分组交换)
  • go函数详解
  • ONLYOFFICE 文档 8.3 已发布:PDF 图章、合并形状、更多格式支持等
  • 【电商数据分析项目经验分享】数据采集——数据清洗——数据分析与可视化——数据决策”
  • Pixflow - CL-DJI Drone LUTs 120个大疆Drone无人机相机航拍电影级镜头LUT调色预设
  • DVWA靶场
  • AIGC个性化与定制化内容生成:技术与应用的前沿探索
  • 使用conda创建自己的python虚拟环境,与其他python版本独立区分
  • 将Deepseek接入pycharm 进行AI编程
  • 数据库并发策略
  • 【测试用例翔实 栈】P8815 [CSP-J 2022] 逻辑表达式
  • Tailwind CSS:现代化的实用优先CSS框架
  • HTML应用指南:利用GET请求获取全国盒马门店位置信息
  • The Sandbox 收购 QED,业务扩展至罗马尼亚