函数式编程
函数变量
在go语言中,函数可以赋值给一个变量,使用变量调用函数方法
函数定义
package _case
import "errors"
func Sum(a, b int) (sum int, err error) {
if a <= 0 && b <= 0 {
err = errors.New("两数相加要求两数不能同时小于等于0")
return 0, err
}
sum = a + b
return
}
调用函数
f1 := _case.Sum //将Sum方法赋值给变量f1
//执行函数
fmt.Println(f1(2, 3)) //5 <nil>
实现中间件
将函数作为输入输出实现中间件
// SumFunc 将函数作为类型
type SumFunc func(a int, b int) (int, error) //SumFunc代表Sum方法
// LogMiddleware 将函数作为输入输出实现中间件
func LogMiddleware(in SumFunc) SumFunc { //in 是一个变量, 代表函数方法Sum
//返回函数为闭包函数,其中in为闭包函数使用的外部函数的内部变量
return func(a, b int) (int, error) {
log.Printf("日志中间件,记录操作数: a: %d, b : %d", a, b)
return in(a, b)
}
}
调用中间件
//将函数作为输入输出实现中间件
f2 := _case.LogMiddleware(_case.Sum)
fmt.Println(f2(1, 2))
/*2023/09/04 12:34:36 日志中间件,记录操作数: a: 1, b : 2
3 <nil>
*/
再次附加中间件可以打印两次日志,中间件可以叠加
//将函数作为输入输出实现中间件
f2 := _case.LogMiddleware(_case.Sum)
//再次附加中间件
f2 = _case.LogMiddleware(f2)
fmt.Println(f2(1, 2))
/*
2023/09/04 12:36:53 日志中间件,记录操作数: a: 1, b : 2
2023/09/04 12:36:53 日志中间件,记录操作数: a: 1, b : 2
3 <nil>
*/
使用函数类型作为对象实现方法
// Accumulation 将函数作为对象添加实现方法
func (sum SumFunc) Accumulation(list ...int) (int, error) { //将可变参数list中的数相加
s := 0
var err error
for _, i := range list { //遍历列表
s, err = sum(s, i) //使用s存储相加的值,调用SunFunc函数类型对象
if err != nil {
return s, err
}
}
return s, err
}
调用方法
f1 := _case.Sum //将Sum方法赋值给变量f1
f2 := _case.LogMiddleware(_case.Sum)
f3 := _case.SumFunc(f1) //f3 为Sum函数方法的对象
fmt.Println(f3.Accumulation(1, 2, 3, 4))
f3 = _case.SumFunc(f2) //f2 返回的也是SunFunc类型
fmt.Println(f2.Accumulation(1, 2, 3, 4, 5))
/*
2023/09/04 12:52:24 日志中间件,记录操作数: a: 0, b : 1
2023/09/04 12:52:24 日志中间件,记录操作数: a: 1, b : 2
2023/09/04 12:52:24 日志中间件,记录操作数: a: 3, b : 3
2023/09/04 12:52:24 日志中间件,记录操作数: a: 6, b : 4
2023/09/04 12:52:24 日志中间件,记录操作数: a: 10, b : 5
15 <nil>
*/
闭包
闭包在Go语言中是一种强大的特性,它可以定义匿名函数,并且可以访问其所在作用域的变量。当一个函数返回一个内部的匿名函数时,这个匿名函数可以访问并持有原函数的局部变量,这种方式就形成了闭包。
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
addFunc := adder()
fmt.Println(addFunc(1)) // 输出 1
fmt.Println(addFunc(2)) // 输出 3
fmt.Println(addFunc(3)) // 输出 6
}
在上面的示例中,adder 函数返回了一个匿名函数,这个匿名函数可以访问并修改 adder 函数中的局部变量 sum。每次调用 addFunc 函数时,都会将传入的值累加到 sum 上,并返回当前的累加结果。
最佳实现
使用闭包实现获取斐波那契数列第n个数
package _case
import "log"
func Fib(n int) int {
if n <= 2 {
log.Fatal("请选择大于2的位置,前两个位置已经有值")
}
t := tool()
var res int
for i := 0; i < n-2; i++ { //循环中闭包没有结束,t()中值没有释放,一直使用计算后的值
res = t() //t()为闭包方法
}
return res
}
// 斐波那契数列 X0 + X1 = X2 ...求Xn
func tool() func() int { //当闭包函数结束后变量才释放
var x0 = 1
var x1 = 1
var x2 = 0
return func() int {
x2 = x0 + x1 //x0, x1, x2 全部是匿名函数引用函数作用域外的变量,形成了闭包
x0 = x1
x1 = x2
return x2
}
}
闭包陷阱
闭包函数
func ClosureTrap() {
for i := 0; i < 10; i++ {
go func() {
fmt.Println(i) //内部匿名函数引用了外部函数的变量,形成了闭包
}()
}
}
函数调用
//闭包的陷阱
_case.ClosureTrap()
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
//使用signal.NotifyContext()函数创建一个上下文,并设置监听的操作系统信号为os.Interrupt和os.Kill,表示监听操作系统的中断信号。
//使用defer关键字和cancel()函数保证在函数执行完毕后会调用取消上下文的操作。
//使用<-ctx.Done()语句,会阻塞代码的执行,直到上下文被取消(即接收到操作系统中断信号),此时ctx.Done()的通道会有一个值发送。
defer cancel()
<-ctx.Done()
问题解决
func ClosureTrap() {
for i := 0; i < 10; i++ {
go func(num int) {
fmt.Println(num)
}(i)
}
}