最全Golang中的闭包详解_golang闭包,2024年最新王者笔记

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

data := []int{1, 2, 3, 4, 5}

processData(data, func(value int) {
    fmt.Println(value \* 2)
})

}


在上述代码中,我们定义了一个`processData`函数,它接受一个整数切片和一个回调函数作为参数。在`processData`函数内部,我们遍历切片中的每个值,并将其传递给回调函数进行处理。通过使用闭包,我们可以直接在函数调用的地方定义匿名函数作为回调函数。


#### 延迟执行


在Golang中,我们可以使用`defer`关键字来延迟一个函数的执行。延迟执行的函数会在所属函数返回之前被调用,这在资源释放和错误处理中非常有用。闭包与`defer`一起使用时,可以方便地在函数返回之前保存一些状态。



func main() {
file, err := os.Open(“data.txt”)
if err != nil {
fmt.Println(“Failed to open file.”)
return
}
defer func() {
file.Close()
fmt.Println(“File closed.”)
}()

// 读取文件内容
// ...

}


在上述代码中,我们使用`os.Open`函数打开一个文件,并定义一个匿名函数作为`defer`的参数。这个匿名函数会在所属函数(在这种情况下是`main`函数)返回之前被调用,无论是正常返回还是发生了错误。在匿名函数中,我们首先关闭打开的文件,然后打印一条信息表示文件已关闭。


这种结构非常有用,因为无论在函数中的哪个位置发生了错误,我们都可以确保文件被关闭,并且可以在延迟函数中处理其他清理任务。


#### 高阶函数


闭包还可以用于创建高阶函数,即接受一个或多个函数作为参数,或返回一个函数的函数。通过使用闭包,我们可以更方便地创建和使用高阶函数。



func performOperation(a, b int, operation func(int, int) int) {
result := operation(a, b)
fmt.Println(“Result:”, result)
}

func main() {
add := func(a, b int) int {
return a + b
}
subtract := func(a, b int) int {
return a - b
}

performOperation(5, 3, add)      // 输出: Result: 8
performOperation(5, 3, subtract) // 输出: Result: 2

}


在上述代码中,我们定义了一个`performOperation`函数,它接受两个整数和一个操作函数作为参数,并将操作函数应用于给定的两个整数。在`main`函数中,我们定义了两个操作函数`add`和`subtract`,然后将它们作为参数传递给`performOperation`函数。


通过使用闭包,我们可以轻松地实现高阶函数,并将其用于各种场景,如函数组合、函数柯里化等。


### 闭包的最佳实践


尽管闭包在Golang中非常有用,但使用不当可能会导致一些问题。以下是一些使用闭包的最佳实践:


1. 避免修改捕获的变量:在闭包函数内部尽量避免修改被捕获的变量,因为这可能会引发意料之外的结果。如果必须修改变量的值,请使用局部变量而不是捕获的变量。
2. 减少闭包的依赖:在创建闭包时,尽量减少其依赖的外部变量。过多依赖外部变量可能会导致闭包的逻辑复杂化,使代码难以维护和理解。
3. 注意闭包的生命周期:由于闭包捕获了外部变量的引用,可能会导致某些变量无法被垃圾回收。因此,在使用闭包时需要仔细考虑其生命周期,避免内存泄漏。
4. 使用闭包的副本:如果闭包需要修改变量的值,最好使用变量的副本而不是捕获的变量本身。这样可以确保在闭包函数执行后,外部变量仍然保持其原始值。


### 案例


#### 闭包案例一:计数器



func counter() func() {
count := 0

return func() {
    count++
    fmt.Println("Count:", count)
}

}

func main() {
increment := counter()
increment() // 输出: Count: 1
increment() // 输出: Count: 2

decrement := counter()
decrement() // 输出: Count: 1

}


在上述代码中,我们定义了一个计数器函数`counter`,它返回一个闭包函数。该闭包函数可以递增并打印一个计数器的当前值。通过多次调用`counter`函数,我们可以创建多个独立的计数器实例。


在主函数中,我们首先创建一个计数器实例`increment`,并连续调用两次,每次都会递增计数器并打印结果。然后,我们创建另一个计数器实例`decrement`,并调用一次,它会重新初始化计数器并打印结果。


#### 闭包案例二:缓存函数



func memoize(f func(string) int) func(string) int {
cache := make(map[string]int)

return func(input string) int {
    if value, ok := cache[input]; ok {
        return value // 从缓存中返回结果
    }

    result := f(input)
    cache[input] = result // 缓存计算结果
    return result
}

}

func slowFunction(input string) int {
// 模拟耗时的计算
time.Sleep(2 * time.Second)
return len(input)
}

func main() {
memoizedFunction := memoize(slowFunction)
fmt.Println(memoizedFunction(“Hello”)) // 输出: 5,耗时2秒
fmt.Println(memoizedFunction(“World”)) // 输出: 5,直接从缓存中返回结果,耗时几乎为0
fmt.Println(memoizedFunction(“Golang”)) // 输出: 6,耗时2秒
}


在上述代码中,我们定义了一个缓存函数`memoize`,它接受一个输入为字符串的函数`f`作为参数,并返回一个具有相同输入和输出类型的缓存函数。`memoize`函数使用一个map作为缓存,它将输入作为键,将`f`函数计算的结果作为值。


在主函数中,我们首先通过调用`memoize`函数来创建一个缓存函数`memoizedFunction`,并将耗时的函数`slowFunction`作为参数传递给它。然后,我们使用`memoizedFunction`多次调用`slowFunction`,第一次调用会耗费2秒,之后的调用将直接从缓存中返回结果,耗时几乎为0。


通过使用闭包以及缓存技术,我们可以提高对于耗时函数的执行效率,并避免重复计算相同输入的结果。


#### 闭包案例三:函数柯里化



func curry(f func(int, int) int) func(int) func(int) int {
return func(x int) func(int) int {
return func(y int) int {
return f(x, y)
}
}
}

func add(x, y int) int {
return x + y
}

func main() {
curriedAdd := curry(add)

add10 := curriedAdd(10)
fmt.Println(add10(5))  // 输出: 15

add20 := curriedAdd(20)
fmt.Println(add20(7))  // 输出: 27

}


在上述代码中,我们定义了一个函数柯里化的辅助函数`curry`,它接受一个接受两个整数参数并返回一个整数的二元函数`f`作为参数,并返回一个函数,该函数接受第一个整数参数`x`,并返回一个函数,该函数接受第二个整数参数`y`,并最终返回`f(x, y)`的结果。


在主函数中,我们首先通过调用`curry`函数来创建一个柯里化的函数`curriedAdd`,并将加法函数`add`作为参数传递给它。然后,我们使用`curriedAdd`创建两个局部函数`add10`和`add20`,它们分别接受一个整数参数,并返回通过在此参数上执行柯里化的加法函数。


通过使用闭包和柯里化技术,我们可以轻松地实现函数的复用和定制化,提高代码的灵活性和重用性。



![img](https://img-blog.csdnimg.cn/img_convert/f0d0a96b34361d559e6b1de368f7c7f1.png)
![img](https://img-blog.csdnimg.cn/img_convert/71edea0354ba98d952b804df87cfe9b6.png)
![img](https://img-blog.csdnimg.cn/img_convert/a3a31b9a72a43eef21080bd2791097d5.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618658159)**

556027273)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618658159)**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值