First Class Function
本文是《Go系列教程》的第二十六篇文章。
what are first class functions ?
如果一个语言支持first class function的话,那就是说,你可以把函数赋值给变量,把函数作为参数传递给其他函数,并从其他函数中返回。对Go来说的话,它是支持first class functions的。我们在这篇教程中,将讨论first class function的语法以及各种不同的使用场景。
匿名函数
我们来写一个简单的程序,把函数赋值给一个变量。
package main
import (
"fmt"
)
func main() {
a := func() {
fmt.Println("hello world first class function")
}
a()
fmt.Printf("%T", a)
}
在上面的程序中,我们把一个函数赋值给了变量a。这就是把函数赋值给变量的语法。如果你仔细观察的话,你会发现被赋值给变量a的函数并没有名字。这种类型的函数,我们称之为匿名函数,因为它们没有名字。
调用这种函数的方法就是使用变量a。a()即是调用函数,打印出“hello world first class function”。之后,我们又打印出了变量a的类型,输出的是func()。
运行此程序,将输出如下:
hello world first class function
func()
没有把匿名函数赋值给变量的情况下,也可以调用匿名函数。我们在下面这个例子中将会看到。
package main
import (
"fmt"
)
func main() {
func() {
fmt.Println("hello world first class function")
}()
}
上面的这个程序,我们在第8行定义了一个匿名函数。并且在定义完之后,立即就调用了此函数。我们直接使用()进行调用。程序的输出如下:
hello world first class function
同时,我们还能向匿名函数传递参数,就像其他普通函数一样。
package main
import (
"fmt"
)
func main() {
func(n string) {
fmt.Println("Welcome", n)
}("Gophers")
}
在上面的程序中,我们把一个字符串参数传递给了一个匿名函数。运行此程序将输出如下:
Welcome Gophers
User defined function types
就像我们定义自己的结构体类型一样,我们同样也可以定义自己的函数类型
type add func(a int, b int) int
上面的代码片段,创建了一个新函数类型add。其接受俩个整型参数,并返回一个整型值。现在,我们可以定义一个add类型的变量。
我们来写段程序。
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)
}
在上面的程序中,我们顶一个了一个add类型的变量a。并为它赋值一个函数,此函数的签名也是add类型的。之后,我们调用了此函数,并把函数的结果赋值给s。程序的输出如下:
Sum 11
Higher-order functions
wiki上对Higher-order function的定义是有下面这些特征之一:
-
可以接收一个或多个函数作为参数
-
把函数作为结果返回
关于上面俩个场景,我们来看几个简单的例子。
Passing functions as arguments to other functions
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)
}
在上面的例子中,我们定义了一个simple函数,它可以接收一个函数作为参数。在main函数的内部,我们创建了一个匿名函数f,它的方法签名满足函数simple的方法签名。之后,我们调用了simple函数,并把f作为参数传递给它。程序的输出67。
Returning functions from other functions
现在,我们再重写上面的程序,让simple函数的返回结果也是一个函数。
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))
}
在上面的程序中,simple函数返回了一个函数,此函数接收2个int参数,并返回一个int值。我们又在15行调用了simple函数,并把函数的返回值赋值给s。现在s就包含了由simple所返回的函数。我们调用s,并传递给它俩个int参数。程序的输出是67。
Closures
Closure是匿名函数的一种特例。Closure也是匿名函数,不过它能访问定义在函数体外的变量。
一个简单的例子,就可以让你更清楚地理解它。
package main
import (
"fmt"
)
func main() {
a := 5
func() {
fmt.Println("a =", a)
}()
}
在上面的程序中,匿名函数访问了变量a。而变量a是在函数体之外定义的。因此这种匿名函数就是一个closure。
每一个closure都和它自己周围的变量绑定在一起。我们通过个简单的例子来理解一下,这是什么意思。
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的返回结果是一个closure。这个closure和变量t绑定在一起。这是什么意思呢。变量a和b都是closure,他们都和自己的t的值绑定。
我们首先调用了a,给它传递了一个参数“world”,此时a的t值就是"hello world"。
之后,我们又调用了b,传递的参数是“Everyone”。由于b也是和它自己的变量t绑定在一起。所以b的t值初始值还是"Hello"。因此,在函数调用之后,b的t值变为了“Hello Everyone”。程序的其余部分都不言自明。此程序的输出如下:
Hello World
Hello Everyone
Hello World Gopher
Hello Everyone !
Practical use of first class functions
到现在为止,我们已经知道了什么是first class function,我们还通过几个例子了解到他们的工作机制。
现在,我们来写一个具体的程序,展示一下实际中如何使用first class function。
我们来写一个程序,这个程序能根据一些条件,对一个学生分片进行过滤。我们来一步步地实现它。
首先,我们先定义一个学生类。
type student struct {
firstName string
lastName string
grade string
country string
}
下一步就是写一个filter函数,这个函数会接收一个学生数组,以及一个函数作为参数。
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的第二个参数是一个函数,这个函数决定了某个学生是否满足条件。我们迭代这个学生数组,并把每个学生作为参数传递给函数f。
如果返回true的话,就以为着此学生满足过滤条件,就会把它添加到返回结果中。在下面,我们增加了main函数并补充了完整的程序。
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函数中,我们创建了俩个学生s1和s2。并把他们添加到数组s中。现在我们要找到所有grade为B的学生。
我们在上面的程序中,传递了一个函数作为参数,这个函数会检查当前学生的grade是否为B。如果为B的话,就返回true。程序的输出如下:
[{Samuel Johnson B USA}]
现在,我们要查找所有来自印度的学生。这时,我们只需要稍微修改一下函数即可。
c := filter(s, func(s student) bool {
if s.country == "India" {
return true
}
return false
})
fmt.Println(c)
把上面这个代码添加到main函数中,并检查输出结果。我们再写一个例子。
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)
}
运行此程序,输出如下:
[25 30 35 40 45]
以上这些就是关于first class function的所有内容。
感谢您的阅读,请留下您珍贵的反馈和评论。Have a good Day!
备注
本文系翻译之作原文博客地址
网友评论