什么是头等函数?
支持头等函数的语言允许将函数分配给变量,作为参数传递给其他函数,并从其他函数返回。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] |
下面是对我们在本教程中所学内容的简单回顾。
- 什么是头等函数?
- 匿名函数
- 用户定义的函数类型
- 高阶函数
- 将函数作为参数传递给其他函数
- 从其他函数中返回函数
- 闭包
- 头等函数的实际使用
头等函数的内容就到此为止。祝你有个愉快的一天。