Golang中闭包与常用场景详解
作者:jefffff
在很多的开源项目里,经常看到闭包的运用,在 Go语言中,函数类型是一种特殊的类型,函数类型可以像其他类型一样被声明、赋值给变量、作为参数传递。进而有了匿名函数、闭包。本文将简要记录闭包的概念和一些常用的场景。
什么是闭包
Go函数可以是闭包。闭包是一个函数值,它从函数体外部引用变量。函数可以访问被引用的变量并对其赋值;从这个意义上说,函数被“绑定”到变量上。-- a Tour of Go
一个最简单的例子: 1~10的加法
package main import "fmt" func adder() func(int) int { sum := 0 return func(x int) int { sum += x return sum } } func main() { pos := adder() for i := 0; i < 10; i++ { fmt.Println( pos(i), ) } } //OUTPUT //0 //1 //3 //6 //10 //15 //21 //28 //36 //45
上例中,adder函数返回一个闭包。在闭包内部,它引用了外部的变量sum
。
闭包和匿名函数的区别
匿名函数是指在代码中没有明确命名的函数,闭包是一个函数值,它可以访问其词法环境中捕获的变量。匿名函数可以是闭包,也可以不是闭包。匿名函数引用了外部作用域中的变量,它就成为闭包。
func main() { x := 10 // 匿名函数,不是闭包 anonymousFunc := func() { fmt.Println("匿名函数:", x) } anonymousFunc() // 闭包 closureFunc := func() { fmt.Println("闭包:", x) } closureFunc() }
常见的应用场景
闭包在实际编程中具有广泛的应用。以下是几个常见的应用场景:
- 保存状态: 通过捕获外部变量,闭包可以在函数调用之间保留状态信息,如迭代器
- 函数工厂: 根据不同的配置参数来动态创建函数,
- 回调函数:将一个函数作为参数传递给另一个函数,通过闭包,捕获一些上下文信息并执行该函数
- 并发编程:可以安全地在多个goroutine中共享和修改变量,一种简洁的方式
保存状态 -- 迭代器
package main import "fmt" func Iterator(values []int) func() (int, bool) { index := 0 return func() (int, bool) { if index >= len(values) { return 0, false } value := values[index] index++ return value, true } } func main() { numbers := []int{1, 2, 3, 4, 5} iterate := Iterator(numbers) for { value, ok := iterate() if !ok { break } fmt.Println(value) } } //OUTPUT: //1 //2 //3 //4 //5
上例中,index为闭包的共享状态。Iterator创建一个闭包函数,这个闭包函数用于迭代切片中的元素。其中,闭包每次执行,index会递增,直到循环结束。比如在很多开源框架的中间件中,会使用迭代器模式实现。
函数工厂
package main import "fmt" func FunctionFactory(operation string) func(int, int) int { switch operation { case "add": return func(a, b int) int { return a + b } case "subtract": return func(a, b int) int { return a - b } case "multiply": return func(a, b int) int { return a * b } case "divide": return func(a, b int) int { if b != 0 { return a / b } return 0 } default: return nil } } func main() { addFunc := FunctionFactory("add") subtractFunc := FunctionFactory("subtract") multiplyFunc := FunctionFactory("multiply") divideFunc := FunctionFactory("divide") fmt.Println(addFunc(5, 3)) fmt.Println(subtractFunc(10, 2)) fmt.Println(multiplyFunc(4, 5)) fmt.Println(divideFunc(10, 2)) fmt.Println(divideFunc(10, 0)) } //OUTPUT: 8 8 20 5 0
上例中,FunctionFactory分别提供加法、减法、乘法和除法的闭包函数,并封装为函数工厂,使得函数的创建更加灵活和可定制。
回调函数
package main import ( "fmt" "time" ) func PerformOperationAsync(input int, callback func(int)) { go func() { time.Sleep(2 * time.Second) result := input * 2 callback(result) }() } func main() { callback := func(result int) { fmt.Println("操作结果:", result) } PerformOperationAsync(5, callback) time.Sleep(3 * time.Second) } //OUTPUT //操作结果:10
上例中,使用匿名函数创建了一个闭包callback
,即回调函数。在执行异步操作时,它会将计算结果result
传递给回调函数。比如,我们需要等待某个长时间的操作或者某个事件触发之后的场景。
并发编程
package main import ( "fmt" "sync" "time" ) func ConcurrentTask(tasks []func() int) []int { results := make([]int, len(tasks)) wg := sync.WaitGroup{} for i, task := range tasks { wg.Add(1) go func(i int, task func() int) { defer wg.Done() results[i] = task() }(i, task) } wg.Wait() return results } func main() { tasks := []func() int{ func() int { time.Sleep(2 * time.Second) return 1 }, func() int { time.Sleep(1 * time.Second) return 2 }, func() int { time.Sleep(3 * time.Second) return 3 }, } results := ConcurrentTask(tasks) fmt.Println(results) // 输出:[1 2 3] } //OUTPUT //[1 2 3]
上例中,for循环为每个任务创建一个匿名函数。这些匿名函数使用闭包来捕获循环变量i
和任务函数task
。在每个匿名函数内部,我们调用任务函数,并将结果存储在相应的位置。
总结
本文主要学习记录Go语言中一个强大、极具表现力的特性:闭包。包括闭包的基础概念,和匿名函数的区别,以及一些常见的编程场景。闭包因为其灵活性,可塑性强,在开源库里广泛运用,对提升代码的可维护性、拓展性上有比较大的帮助。
以上就是Golang中闭包与常用场景详解的详细内容,更多关于Go闭包的资料请关注脚本之家其它相关文章!