头等函数
Q7nl1s admin

什么是头等函数?

支持头等函数的语言允许将函数分配给变量,作为参数传递给其他函数,并从其他函数返回。Go支持头等函数。

在本教程中,我们将讨论头等函数的语法和各种使用情况。

匿名函数

让我们从一个简单的例子开始,它将一个函数分配给一个变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
"fmt"
)

func main() {
a := func() {
fmt.Println("hello world first class function")
}
a()
fmt.Printf("%T", a)
}

在操场上运行

在上面的程序中,我们在第8行将一个函数分配给了变量a。这就是将一个函数赋值给一个变量的语法。如果你仔细注意,分配给a的函数并没有名字。这类函数被称为匿名函数,因为它们没有名字。

调用这个函数的唯一方法是使用变量a,我们在下一行已经这样做了。a()调用了这个函数,并打印hello world first class function。在第12行,我们打印了变量a的类型,这将打印func()

运行这个程序将打印

1
2
hello world first class function  
func()

调用匿名函数而不把它赋值给一个变量也是可能的。让我们看看在下面的例子中是如何做到这一点的。

1
2
3
4
5
6
7
8
9
10
11
package main

import (
"fmt"
)

func main() {
func() {
fmt.Println("hello world first class function")
}()
}

在操场上运行

在上面的程序中,第8行定义了一个匿名函数,紧接着我们在第10行用()调用该函数。这个程序将输出:

1
hello world first class function  

也可以像其他函数一样,向匿名函数传递参数。

1
2
3
4
5
6
7
8
9
10
11
package main

import (
"fmt"
)

func main() {
func(n string) {
fmt.Println("Welcome", n)
}("Gophers")
}

在操场上运行

在上面的程序中,一个字符串参数被传递给第10行的匿名函数。运行这个程序将打印:

1
Welcome Gophers  

用户定义的函数类型

就像我们定义自己的结构类型一样,我们也可以定义自己的函数类型。

1
type add func(a int, b int) int  

上面的代码片段创建了一个新的函数类型add,它接受两个int参数并返回一个int。现在我们可以定义add类型的变量了。

让我们写一个程序,定义一个类型为add的变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"fmt"
)

type add func(a int, b int) int

func main() {
var a add = func(a int, b int) int {
return a + b
}
s := a(5, 6)
fmt.Println("Sum", s)
}

在操场上运行

在上面的程序中,在第10行,我们定义了一个add类型的变量a,并给它分配了一个签名与add类型相符的函数。我们在第13行调用该函数,并将结果赋给s。这个程序将打印:

1
Sum 11  

高阶函数

维基中对高阶函数的定义是:一个至少做了以下一件事的函数

  • 以一个或多个函数作为参数
  • 返回一个函数作为其结果

让我们来看看上述两种情况的一些简单例子。

将函数作为参数传递给其他函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import (
"fmt"
)

func simple(a func(a, b int) int) {
fmt.Println(a(60, 7))
}

func main() {
f := func(a, b int) int {
return a + b
}
simple(f)
}

在操场上运行

在上面的例子中,在第7行,我们定义了一个函数simple,它接收一个(接受两个int参数并返回一个int参数的函数)。在main函数的第12行中,我们创建了一个匿名函数f,其签名与函数simple的参数一致。我们在下一行调用simple并将f作为参数传递给它。这个程序打印出67作为输出。

从其他函数中返回函数

现在让我们重写上面的程序,从simple函数中返回一个函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"fmt"
)

func simple() func(a, b int) int {
f := func(a, b int) int {
return a + b
}
return f
}

func main() {
s := simple()
fmt.Println(s(60, 7))
}

在操场上运行

在上面的程序中,第7行的simple函数返回一个(接受两个int参数并返回一个int参数的函数)。

这个simple函数在第15行被调用。simple的返回值被分配给s,现在s包含simple函数返回的函数。我们在第16行调用s并将两个int参数传给它。这个程序输出67

闭包

闭包是匿名函数的一种特殊情况。闭包是匿名函数,它可以访问定义在函数主体之外的变量。

一个例子可以更加清楚说明这点。

1
2
3
4
5
6
7
8
9
10
11
12
package main

import (
"fmt"
)

func main() {
a := 5
func() {
fmt.Println("a =", a)
}()
}

在操场上运行

在上面的程序中,匿名函数访问了变量a,该变量存在于其主体之外的第10行中。因此,这个匿名函数是一个闭包。

每个闭包都与它自己周围的变量绑定。让我们通过一个简单的例子来理解这意味着什么。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
"fmt"
)

func appendStr() func(string) string {
t := "Hello"
c := func(b string) string {
t = t + " " + b
return t
}
return c
}

func main() {
a := appendStr()
b := appendStr()
fmt.Println(a("World"))
fmt.Println(b("Everyone"))

fmt.Println(a("Gopher"))
fmt.Println(b("!"))
}

在后台运行

在上面的程序中,函数appendStr返回一个闭包。这个闭包被绑定到变量t上。让我们来理解这意味着什么。

在第17、18行中声明的变量ab是闭包,它们被绑定到自己的t值上。

我们首先调用带有参数Worlda。现在at的值变成了Hello World

在第20行,我们用参数Everyone调用b。由于b被绑定到它自己的变量t上,b版本的t的初始值又是Hello。因此,在这个函数调用之后,bt版本的值变成了Hello Everyone。该程序的其余部分是不言自明的。

这个程序将打印。

1
2
3
4
Hello World  
Hello Everyone
Hello World Gopher
Hello Everyone !

头等函数的实际使用

到目前为止,我们已经定义了什么是头等函数,我们也看到了一些精心设计的例子来学习它们是如何工作的。现在让我们来写一个具体的程序来展示头等函数的实际用途。

我们将创建一个程序,根据一些标准来过滤student切片。让我们一步一步地来。

首先让我们定义student类型。

1
2
3
4
5
6
type student struct {  
firstName string
lastName string
grade string
country string
}

下一步是编写filter函数。这个函数以一个student切片和一个确定student是否符合filter标准的函数为参数。一旦我们写出这个函数,我们会更好地理解。让我们继续写吧。

1
2
3
4
5
6
7
8
9
func filter(s []student, f func(student) bool) []student {  
var r []student
for _, v := range s {
if f(v) == true {
r = append(r, v)
}
}
return r
}

在上面的函数中,filter的第二个参数是一个函数,它以一个student为参数,返回一个bool。这个函数决定了某个student是否符合某个标准。我们在第3行遍历student切片。如果该函数返回true,则意味着该学生通过了filter标准,并被添加到切片r中。你可能对该函数的真正用途有些困惑,但一旦我们完成程序就会清楚。我已经添加了main函数,并在下面提供了完整的程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package main

import (
"fmt"
)

type student struct {
firstName string
lastName string
grade string
country string
}

func filter(s []student, f func(student) bool) []student {
var r []student
for _, v := range s {
if f(v) == true {
r = append(r, v)
}
}
return r
}

func main() {
s1 := student{
firstName: "Naveen",
lastName: "Ramanathan",
grade: "A",
country: "India",
}
s2 := student{
firstName: "Samuel",
lastName: "Johnson",
grade: "B",
country: "USA",
}
s := []student{s1, s2}
f := filter(s, func(s student) bool {
if s.grade == "B" {
return true
}
return false
})
fmt.Println(f)
}

在操场上运行

在main函数中,我们首先创建了两个student s1s2,并将他们添加到切片s中。现在我们假设要找出所有成绩为Bstudent。我们在上述程序中通过传递一个函数来检查student是否有B成绩,如果有则返回true,作为第1行filter函数的参数。上面的程序会打印:

1
[{Samuel Johnson B USA}]

比方说,我们想找到所有来自Indiastudent。这可以通过改变filter函数的参数来轻松实现。
我在下面提供了这样的代码。

1
2
3
4
5
6
7
c := filter(s, func(s student) bool {  
if s.country == "India" {
return true
}
return false
})
fmt.Println(c)

请将其添加到main函数中并检查输出。

让我们通过再写一个程序来结束本节。这个程序将对一个切片的每个元素进行同样的操作,并返回结果。例如,如果我们想将一个切片中的所有整数乘以5,并返回输出结果,可以用头等函数轻松完成。这类对集合中每个元素进行操作的函数被称为映射(map)函数。我在下面提供了这个程序。它是不言自明的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
"fmt"
)

func iMap(s []int, f func(int) int) []int {
var r []int
for _, v := range s {
r = append(r, f(v))
}
return r
}
func main() {
a := []int{5, 6, 7, 8, 9}
r := iMap(a, func(n int) int {
return n * 5
})
fmt.Println(r)
}

在操场上运行

上面的程序会打印出来。

1
[25 30 35 40 45]

下面是对我们在本教程中所学内容的简单回顾。

  • 什么是头等函数?
  • 匿名函数
  • 用户定义的函数类型
  • 高阶函数
    • 将函数作为参数传递给其他函数
    • 从其他函数中返回函数
  • 闭包
  • 头等函数的实际使用

头等函数的内容就到此为止。祝你有个愉快的一天。

 Comments
Comment plugin failed to load
Loading comment plugin
Powered by Hexo & Theme Keep
Unique Visitor Page View