什么是头等函数?
支持头等函数的语言允许将函数分配给变量,作为参数传递给其他函数,并从其他函数返回。Go支持头等函数。
在本教程中,我们将讨论头等函数的语法和各种使用情况。
匿名函数
让我们从一个简单的例子开始,它将一个函数分配给一个变量。
1 | package main |
在上面的程序中,我们在第8行将一个函数分配给了变量a。这就是将一个函数赋值给一个变量的语法。如果你仔细注意,分配给a的函数并没有名字。这类函数被称为匿名函数,因为它们没有名字。
调用这个函数的唯一方法是使用变量a,我们在下一行已经这样做了。a()调用了这个函数,并打印hello world first class function。在第12行,我们打印了变量a的类型,这将打印func()。
运行这个程序将打印
1 | hello world first class function |
调用匿名函数而不把它赋值给一个变量也是可能的。让我们看看在下面的例子中是如何做到这一点的。
1 | package main |
在上面的程序中,第8行定义了一个匿名函数,紧接着我们在第10行用()调用该函数。这个程序将输出:
1 | hello world first class function |
也可以像其他函数一样,向匿名函数传递参数。
1 | package main |
在上面的程序中,一个字符串参数被传递给第10行的匿名函数。运行这个程序将打印:
1 | Welcome Gophers |
用户定义的函数类型
就像我们定义自己的结构类型一样,我们也可以定义自己的函数类型。
1 | type add func(a int, b int) int |
上面的代码片段创建了一个新的函数类型add,它接受两个int参数并返回一个int。现在我们可以定义add类型的变量了。
让我们写一个程序,定义一个类型为add的变量。
1 | package main |
在上面的程序中,在第10行,我们定义了一个add类型的变量a,并给它分配了一个签名与add类型相符的函数。我们在第13行调用该函数,并将结果赋给s。这个程序将打印:
1 | Sum 11 |
高阶函数
维基中对高阶函数的定义是:一个至少做了以下一件事的函数
- 以一个或多个函数作为参数
- 返回一个函数作为其结果
让我们来看看上述两种情况的一些简单例子。
将函数作为参数传递给其他函数
1 | package main |
在上面的例子中,在第7行,我们定义了一个函数simple,它接收一个(接受两个int参数并返回一个int参数的函数)。在main函数的第12行中,我们创建了一个匿名函数f,其签名与函数simple的参数一致。我们在下一行调用simple并将f作为参数传递给它。这个程序打印出67作为输出。
从其他函数中返回函数
现在让我们重写上面的程序,从simple函数中返回一个函数。
1 | package main |
在上面的程序中,第7行的simple函数返回一个(接受两个int参数并返回一个int参数的函数)。
这个simple函数在第15行被调用。simple的返回值被分配给s,现在s包含simple函数返回的函数。我们在第16行调用s并将两个int参数传给它。这个程序输出67。
闭包
闭包是匿名函数的一种特殊情况。闭包是匿名函数,它可以访问定义在函数主体之外的变量。
一个例子可以更加清楚说明这点。
1 | package main |
在上面的程序中,匿名函数访问了变量a,该变量存在于其主体之外的第10行中。因此,这个匿名函数是一个闭包。
每个闭包都与它自己周围的变量绑定。让我们通过一个简单的例子来理解这意味着什么。
1 | package main |
在上面的程序中,函数appendStr返回一个闭包。这个闭包被绑定到变量t上。让我们来理解这意味着什么。
在第17、18行中声明的变量a和b是闭包,它们被绑定到自己的t值上。
我们首先调用带有参数World的a。现在a的t的值变成了Hello World。
在第20行,我们用参数Everyone调用b。由于b被绑定到它自己的变量t上,b版本的t的初始值又是Hello。因此,在这个函数调用之后,b的t版本的值变成了Hello Everyone。该程序的其余部分是不言自明的。
这个程序将打印。
1 | Hello World |
头等函数的实际使用
到目前为止,我们已经定义了什么是头等函数,我们也看到了一些精心设计的例子来学习它们是如何工作的。现在让我们来写一个具体的程序来展示头等函数的实际用途。
我们将创建一个程序,根据一些标准来过滤student切片。让我们一步一步地来。
首先让我们定义student类型。
1 | type student struct { |
下一步是编写filter函数。这个函数以一个student切片和一个确定student是否符合filter标准的函数为参数。一旦我们写出这个函数,我们会更好地理解。让我们继续写吧。
1 | func filter(s []student, f func(student) bool) []student { |
在上面的函数中,filter的第二个参数是一个函数,它以一个student为参数,返回一个bool。这个函数决定了某个student是否符合某个标准。我们在第3行遍历student切片。如果该函数返回true,则意味着该学生通过了filter标准,并被添加到切片r中。你可能对该函数的真正用途有些困惑,但一旦我们完成程序就会清楚。我已经添加了main函数,并在下面提供了完整的程序。
1 | package main |
在main函数中,我们首先创建了两个student s1和s2,并将他们添加到切片s中。现在我们假设要找出所有成绩为B的student。我们在上述程序中通过传递一个函数来检查student是否有B成绩,如果有则返回true,作为第1行filter函数的参数。上面的程序会打印:
1 | [{Samuel Johnson B USA}] |
比方说,我们想找到所有来自India的student。这可以通过改变filter函数的参数来轻松实现。
我在下面提供了这样的代码。
1 | c := filter(s, func(s student) bool { |
请将其添加到main函数中并检查输出。
让我们通过再写一个程序来结束本节。这个程序将对一个切片的每个元素进行同样的操作,并返回结果。例如,如果我们想将一个切片中的所有整数乘以5,并返回输出结果,可以用头等函数轻松完成。这类对集合中每个元素进行操作的函数被称为映射(map)函数。我在下面提供了这个程序。它是不言自明的。
1 | package main |
上面的程序会打印出来。
1 | [25 30 35 40 45] |
下面是对我们在本教程中所学内容的简单回顾。
- 什么是头等函数?
- 匿名函数
- 用户定义的函数类型
- 高阶函数
- 将函数作为参数传递给其他函数
- 从其他函数中返回函数
- 闭包
- 头等函数的实际使用
头等函数的内容就到此为止。祝你有个愉快的一天。