Golang中闭包与常用场景详解

 更新时间:2023年11月14日 10:44:39   作者: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闭包的资料请关注脚本之家其它相关文章!

相关文章

  • golang实现对docker容器心跳监控功能

    golang实现对docker容器心跳监控功能

    这篇文章主要介绍了golang实现对docker容器心跳监控功能,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-09-09
  • go语言日志实现详解(打印日志、日志写入文件和日志切割)

    go语言日志实现详解(打印日志、日志写入文件和日志切割)

    golang内置了log包,实现简单的日志服务,下面这篇文章主要给大家介绍了关于go语言日志实现(打印日志、日志写入文件和日志切割)的相关资料,需要的朋友可以参考下
    2022-10-10
  • golang标准库time时间包的使用

    golang标准库time时间包的使用

    时间和日期是我们编程中经常会用到的,本文主要介绍了golang标准库time时间包的使用,具有一定的参考价值,感兴趣的可以了解一下
    2023-10-10
  • GO语言协程创建使用并通过channel解决资源竞争

    GO语言协程创建使用并通过channel解决资源竞争

    这篇文章主要为大家介绍了GO语言协程创建使用并通过channel解决资源竞争,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-04-04
  • Go内存分配之结构体优化技巧

    Go内存分配之结构体优化技巧

    这篇文章主要为大家详细介绍了Go语言内存分配之结构体优化技巧的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-11-11
  • golang实现http服务器处理静态文件示例

    golang实现http服务器处理静态文件示例

    这篇文章主要介绍了golang实现http服务器处理静态文件的方法,涉及Go语言基于http协议处理文件的相关技巧,需要的朋友可以参考下
    2016-07-07
  • 用go写的五子棋预测算法的实现

    用go写的五子棋预测算法的实现

    这篇文章主要介绍了用go写的五子棋预测算法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • Go语言题解LeetCode561数组拆分

    Go语言题解LeetCode561数组拆分

    这篇文章主要为大家介绍了Go语言题解LeetCode561数组拆分示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • go使用net/url包来解析URL提取主机部分

    go使用net/url包来解析URL提取主机部分

    这篇文章主要为大家介绍了go使用net/url包来解析URL提取主机部分实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • 详解Golang中单元测试的使用

    详解Golang中单元测试的使用

    单元测试是检测你写的一个函数是否具备安全性的一次检测,这篇文章主要为大家详细介绍了Golang中单元测试的具体使用,希望对大家有所帮助
    2023-07-07

最新评论