上篇:Go函数的骚包玩法有哪些?

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 上篇:Go函数的骚包玩法有哪些?

「1. 用type关键字可以定义函数类型,函数类型变量可以作为函数的参数或返回值。」

package main
import "fmt"
func add(a, b int) int {
 return a + b
}
func sub(a, b int) int {
 return a - b
}
type Task func(int, int) int
func exec(t Task, a, b int) int {
 return t(a, b)
}
func main() {
 a := exec(add, 10, 20)
 fmt.Println(a)
 b := exec(sub, 100, 95)
 fmt.Println(b)
}

解析:type Task func(int, int) int,这句是说,使用type关键字定义1个类型为func的Task,这个func有2个int形参、1个int返回值。再看exec这个函数,它有3个形参,形参t的类型是刚定义的函数类型Task,另外两个你懂的,我就不说了。

「2. 匿名函数的玩法是真的骚,看看骚在哪里」

  • 栗子1:匿名函数可以直接赋给变量
func main() {
 var aaa = func(a, b int) int {
  return a + b
 }
 ret := aaa(89, 78)
 fmt.Println(ret)
}

输出:

167
  • 栗子2:匿名函数作为函数入参

这个例子感觉跟刚才使用type关键字定义函数类型的例子有点相同的赶脚,仔细一看,好像也只是在使用的方式上是一样,定义函数的套路又是不同的。

func work(f func(int, int) int, a, b int) int {
 return f(a, b)
}
func add(a, b int) int {
 return a + b
}
func main() {
 a := work(add, 100, 200)
 fmt.Println(a)
}

输出:

300

解析:形参f的类型就是匿名函数,继续看func(int, int) int,这个匿名函数接收两个int形参,返回值也是int类型。另外两个形参a和b,也是int,想必你懂了,我就不废话了。

  • 栗子3:匿名函数作为函数出参

匿名函数作为函数出参(作为函数返回值),经不断调测,有3种骚包玩法,爽死了。

  1. 骚包玩法1
// 先来个简单的
func work() func() int {
 return func() int {
  return 10 + 20
 }
}
func main() {
 f := work()
 ret := f()
 fmt.Println(ret)
}

输出:

30
  1. 骚包玩法2
// 再改造下
func work(a, b int) func() int {
 return func() int {
  return a + b
 }
}
func main() {
 f := work(500, 20)
 ret := f()
 fmt.Println(ret)
}

输出:

520
  1. 骚包玩法3
// 再次改造
func work() func(int, int) int {
 return func(a, b int) int {
  return a + b
 }
}
func main() {
 f := work()
 ret := f(600, 50)
 fmt.Println(ret)
}

输出:

650
  1. 对3种骚包玩法的简单解析:

再这里就解析一下第3种玩法,能搞懂这个玩法,前面2个玩法,你就自然懂了。work函数没有入参,但是有出参(也可以说是返回值),出参是匿名函数func(int, int) int,这个定义在返回值里的匿名函数有2个int类型的形参(入参)和1个int类型的返回值。再看看函数体内部,没干很复杂活儿,而是直接return了匿名函数,函数体里的这个匿名函数是和定义在返回值里的类型保持一致的,也是接收了2个int的形参a和b,返回值也是int,啥也没干,就做了个相加。好郁闷!不知道你看懂了没?再看看是如何使用work函数的,关键点就在这里,调用work()赋给了变量f,这时候,f它就是一个函数了,再调用f(600, 50),想必你已经知道了为啥要传入2个int值,这下搞定!不知道把你绕晕没,这玩法确实很骚。

「3. 匿名函数和延迟调用」

延迟调用的规则是:按照「先进后出」的顺序,也就是说函数返回前是会被执行的,而且是按照先进后出的顺序。如何起到延迟的效果,是需要注册的,可通过defer关键字进行注册。那么什么场景下需要用到延迟调用呢?比如常见的场景:当一个函数被即将执行完后,也就是完成任务的最后一刻,需要回收和释放某些资源。

延迟调用的机制可以配合匿名函数来使用,这样就可以让匿名函数被直接调用,只能说是真的骚。

先看个小栗子:

package main
import "fmt"
func work() bool {
 fmt.Println("函数开始工作...")
 defer func() {
  fmt.Println("回收相关资源工作开始!")
 }()
 defer func() {
  fmt.Println("清理工作开始!")
 }()
 fmt.Println("函数正在工作...")
 fmt.Println("函数工作完毕...")
 return true
}
func main() {
 status := work()
 fmt.Println(status)
}

输出:

函数开始工作...
函数正在工作...
函数工作完毕...
清理工作开始!
回收相关资源工作开始!
true

在上面的栗子中,定义了个work函数,先看看输出的结果,感受下。在work函数中,注册了两个延迟调用,work函数从开始->正在->完毕,结束后才执行defer注册的匿名函数,这里要着重注意延迟调用规则:「先进后出」,也就是先注册后执行。“回收相关资源工作”是先注册的,他的执行顺序排在了“清理工作”的后面。感受到了吗?

最后说明一下:defer关键字让匿名函数实现了可直接调用,那么使用defer注册延迟调用时要注意,defer关键字的后面一定是要函数或方法的调用,不能直接写语句哦。

相关文章
|
1月前
|
JSON 安全 网络协议
go语言使用内置函数和标准库
【10月更文挑战第18天】
20 3
|
7月前
|
存储 算法 Go
go语言中的延迟执行函数
【5月更文挑战第13天】`defer`是Go语言中用于延迟执行函数的关键字,尤其适用于资源管理,如文件关闭和锁的释放。它在函数返回前按照LIFO顺序执行,确保资源在任何返回路径下都能正确释放。`defer`可以拦截`panic`并在函数返回前执行,但无法阻止某些致命的`panic`。此外,`defer`可用于修改返回值、输出调试信息和还原变量值。尽管在某些场景下可能影响性能,但Go的优化使得其在多数情况下性能表现良好,特别是在资源清理方面。在Go 1.20及以后的版本,`defer`的性能已显著提升,尤其是在高计算量的场景下。
280 2
|
3月前
|
Go
go函数
go函数
35 10
|
3月前
|
编译器 Go C++
Go to Learn Go之函数
Go to Learn Go之函数
32 0
|
3月前
|
编译器 Go 索引
Go数组、多维数组和切片(动态数组),及常用函数len(),cap(),copy(),append()在切片中的使用
本文介绍了Go语言中数组、多维数组和切片(动态数组)的基本概念和操作,包括数组的定义、初始化、访问,多维数组的定义和访问,以及切片的创建、使用和扩容。同时,还讲解了切片中常用的函数len()、cap()、copy()和append()的使用方法。
|
4月前
|
Prometheus Cloud Native Go
Go 1.22 标准库 slices 新增函数和一些旧函数增加新特性
Go 1.22 标准库 slices 新增函数和一些旧函数增加新特性
|
4月前
|
安全 编译器 Go
Go 1.21: 泛型函数的全面回顾
Go 1.21: 泛型函数的全面回顾
|
4月前
|
Go
深入理解 Go 中的 new() 和 make() 函数
深入理解 Go 中的 new() 和 make() 函数
|
4月前
|
设计模式 Java 数据库连接
|
4月前
|
Go 开发者