【Go】匿名函数与闭包

server/2024/9/24 15:14:23/

目录

一、匿名函数

基础介绍

形式

示例程序 1: 直接调用

示例程序 2: 作为变量赋值

示例程序 3: 作为函数参数

示例程序 4: 使用匿名函数进行排序

示例程序 5: 匿名函数用于延迟执行

示例程序 6: 通过匿名函数实现迭代器

二、闭包用法

基础知识

示例程序 1: 状态保持器

示例程序 2: 生成器函数

示例程序 3: 事件处理

示例程序 4: 资源清理


一、匿名函数

        在Go语言中,匿名函数是没有名字的函数,可以在需要的地方直接定义和使用。匿名函数的一个重要用途是实现闭包。在Go中,匿名函数可以被赋值给变量,作为参数传递,或直接在其它函数内调用。这提供了极大的灵活性和代码的简洁性。下面,我将介绍匿名函数的基本概念,并提供三个示例程序来展示其使用方法。

基础介绍

形式

        匿名函数通常定义的形式是:

func(参数列表) 返回类型 {// 函数体
}

        可以将其赋值给一个变量,或者直接在需要的地方调用。

示例程序 1: 直接调用

        这个示例展示了如何直接调用一个匿名函数,无需先赋值给变量。

package mainimport "fmt"func main() {// 直接定义并调用匿名函数func(msg string) {fmt.Println(msg)}("Hello, Go!")
}

        在这个例子中,匿名函数被直接调用,并传递了一个字符串参数。

示例程序 2: 作为变量赋值

        这个示例展示了如何将匿名函数赋值给变量,并在后续调用这个变量。

package mainimport "fmt"func main() {// 将匿名函数赋值给变量 greetgreet := func(name string) {fmt.Printf("Hello, %s!\n", name)}// 调用函数greet("World")greet("Go")
}

        这里,匿名函数赋值给变量 greet,然后通过变量名调用该函数。

示例程序 3: 作为函数参数

        匿名函数可以作为参数传递给其他函数。这是在Go语言中实现回调或者定制行为的一种常用方式。

package mainimport "fmt"// 定义一个函数,接受另一个函数作为参数
func process(f func(string), value string) {f(value)
}func main() {// 调用 process,传递匿名函数和字符串参数process(func(s string) {fmt.Println(s)}, "Go is fun!")
}

        在这个例子中,process 函数接受一个函数作为参数。我们传递了一个匿名函数,它接受一个字符串参数并打印它。

示例程序 4: 使用匿名函数进行排序

        Go的sort.Slice函数允许使用匿名函数来定义排序逻辑。这是匿名函数作为功能参数的一个常见应用。

package mainimport ("fmt""sort"
)func main() {people := []struct {Name stringAge  int}{{"Alice", 23},{"Eve", 2},{"Bob", 25},}// 使用匿名函数进行排序,按年龄排序sort.Slice(people, func(i, j int) bool {return people[i].Age < people[j].Age})fmt.Println("Sorted by age:", people)
}

示例程序 5: 匿名函数用于延迟执行

        在Go中,defer语句常与匿名函数一起使用,以确保在函数返回前执行某些清理工作。

package mainimport "fmt"func main() {// 使用 defer 语句调用匿名函数defer func() {fmt.Println("Cleaning up!")}()fmt.Println("Doing some work...")// 当 main 函数返回时,会执行匿名函数中的打印语句
}

        这个示例展示了如何使用匿名函数进行资源清理或执行其他结束工作,这些工作会在当前函数结束时自动执行。

示例程序 6: 通过匿名函数实现迭代器

        匿名函数可以用来创建一个简单的迭代器,封装特定的迭代逻辑。

package mainimport "fmt"func newCounter() func() int {count := 0return func() int {count++return count}
}func main() {counter := newCounter()fmt.Println(counter()) // 输出 1fmt.Println(counter()) // 输出 2fmt.Println(counter()) // 输出 3// 创建另一个独立的计数器anotherCounter := newCounter()fmt.Println(anotherCounter()) // 输出 1
}

        在这个例子中,每次调用newCounter都会创建一个新的闭包,这个闭包维护自己的count状态,独立于其他闭包。

二、闭包用法

        Go语言中,闭包是一种函数,能够引用在其外部作用域定义的变量。由于闭包可以访问并操作这些变量,即使在它们的原始作用域已经结束后,它们被广泛用于创建具有私有状态的函数、实现回调函数和迭代器等。下面我将详细解释Go中闭包的基础知识,并提供几个实际项目中可能用到的示例。

基础知识

        在Go中,闭包通常是在一个函数内部创建的匿名函数,这个匿名函数访问外部函数的局部变量。这些变量随着匿名函数的存在而持续存在,即使外部函数的执行已经结束。

示例程序 1: 状态保持器

        使用闭包实现一个简单的状态保持器,这可以在需要对状态进行封装和管理时非常有用。

package mainimport "fmt"func stateHolder(initial int) func() int {state := initialreturn func() int {state++return state}
}func main() {counter := stateHolder(10)fmt.Println(counter()) // 输出 11fmt.Println(counter()) // 输出 12anotherCounter := stateHolder(20)fmt.Println(anotherCounter()) // 输出 21fmt.Println(anotherCounter()) // 输出 22
}

        在这个例子中,每个闭包都保持了自己的状态,互不影响。

示例程序 2: 生成器函数

        闭包可以用来创建生成器,例如生成连续的斐波那契数列。

package mainimport "fmt"func fibonacci() func() int {a, b := 0, 1return func() int {ret := aa, b = b, a+breturn ret}
}func main() {fib := fibonacci()for i := 0; i < 10; i++ {fmt.Println(fib())}
}

        这个生成器函数每次被调用时,都会返回序列中的下一个斐波那契数。

示例程序 3: 事件处理

        闭包可以用来处理事件或回调,使得状态和逻辑封装在一个单独的函数中。

package mainimport "fmt"type Button struct {onClick func()
}func NewButton() *Button {return &Button{onClick: func() {}}
}func main() {button := NewButton()count := 0button.onClick = func() {count++fmt.Printf("Button clicked %d times\n", count)}// 模拟点击事件button.onClick()button.onClick()button.onClick()
}

        这个示例中的闭包捕获了点击计数,并在每次点击时更新。

示例程序 4: 资源清理

        利用闭包在函数结束时进行资源清理。

package mainimport "fmt"func cleanup(resource string) func() {return func() {fmt.Printf("Cleaning up %s\n", resource)}
}func main() {defer cleanup("resourceA")()fmt.Println("Doing some work with resourceA")
}


http://www.ppmy.cn/server/18351.html

相关文章

【k8s】(九)kubernetes1.29.4离线部署之-Token过期后加入节点

&#xff08;一&#xff09;kubernetes1.29.4离线部署之-安装文件准备 &#xff08;二&#xff09;kubernetes1.29.4离线部署之-镜像文件准备 &#xff08;三&#xff09;kubernetes1.29.4离线部署之-环境初始化 &#xff08;四&#xff09;kubernetes1.29.4离线部署之-组件安装…

hbase MultiRowRangeFilter的原理、作用和实例

MultiRowRangeFilter是HBase中的一个过滤器&#xff0c;用于在扫描操作中过滤多个行键范围 原理 MultiRowRangeFilter的原理是将多个行键范围组合成一个过滤器&#xff0c;然后在扫描操作中应用这个过滤器。当扫描器遍历HBase表的行时&#xff0c;它会检查每一行的行键是否在…

mysql8.0免安装版windows

1.下载 MySQL下载链接 2.解压与新建my.ini文件 解压的路径最好不要有中文路径在\mysql-8.0.36-winx64文件夹下新建my.ini文件&#xff0c;不建data文件夹(会自动生成) [mysqld] # 设置3306端口 port3306 # 设置mysql的安装目录(尽量用双斜杠\\,单斜杠\可能会报错) basedirD:\…

数据结构——二叉树练习(深搜广搜)

数据结构——二叉树练习 路径之和深度优先算法和广度优先算法二叉搜索树判断一棵二叉树是否为搜索二叉树和完全二叉树 我们今天来看二叉树的习题&#xff1a; 路径之和 https://leetcode.cn/problems/path-sum-ii/ 这是一个典型的回溯&#xff0c;深度优先算法的题&#xff0c…

数据结构-二叉树-堆(二)

一、建堆的时间复杂度问题 1、除了向上调整建堆&#xff0c;我们还可以向下调整建堆。不能在根上直接开始向下调整。这里的条件就是左右子树必须都是大堆或者小堆。我们可以倒着往前走&#xff0c;可以从最后一个叶子开始调整。但是从叶子开始调整没有意义。所以我们可以从倒数…

Django用户注册并自动关联到某数据表条目

例如&#xff0c;当一个新用户注册并且你想要自动关联到特定的Box条目&#xff08;假设其ID为1&#xff09;时&#xff0c;以下是完整的实现流程和步骤&#xff1a; 确保有一个默认的Box实例&#xff1a; 在你的数据库中创建一个Box实例&#xff0c;其ID为1。你可以通过Django管…

Electron vue 进程间消息通行

在 Electron 应用中&#xff0c;IPC&#xff08;Inter-Process Communication&#xff0c;进程间通信&#xff09;是一种允许主进程&#xff08;main process&#xff09;和渲染进程&#xff08;renderer process&#xff09;之间交换数据的方式。 ipcRenderer.send 在渲染进程…

Hadoop之路

hadoop更适合在liunx环境下运行&#xff0c;会节省后期很多麻烦&#xff0c;而用虚拟器就太占主机内存了&#xff0c;因此后面我们将把hadoop安装到wsl后进行学习,后续学习的环境是Ubuntu-16.04 &#xff08;windows上如何安装wsl&#xff09; 千万强调&#xff0c;有的命令一…