33. 头等函数(first-class function)

原文:https://golangbot.com/first-class-functions/


欢迎访问 Golang 教程33教程系列.

什么是头等函数?

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

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

匿名函数

让我们从一个将函数赋给变量的简单示例开始。

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()。

运行此程序将输出

hello world first class function  
func()  

也可以调用匿名函数而不将其赋给变量。让我们看看下面的示例中如何完成此操作。

package main

import (  
    "fmt"
)

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

在线运行

在上面的程序中, 匿名函数是在第8行定义的, 在函数定义之后, 我们在第10行使用()来调用函数。这个程序将输出:

hello world first class function  

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

package main

import (  
    "fmt"
)

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

在线运行

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

Welcome Gophers  

用户定义的函数类型

就像我们自定义struct类型一样, 我们也可以自定义函数类型。

type add func(a int, b int) int  

上面的代码段创建了一个新的函数类型add, 它接受两个整数参数,并返回一个整数。现在, 我们可以定义类型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)
}

在线运行

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

Sum 11  

高阶函数

从维基百科中关于高阶函数的定义我们可以知道,高阶函数它至少有以下的一个功能

  • 接受一个或多个函数作为输入
  • 输出一个函数

让我们看一下以上两种情况的一些简单示例。

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

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 。在第12行我们创建一个匿名函数f,其签名与函数simple的参数是匹配的。我们调用simple并在下一行中将f作为参数传递给它。 该程序打印输出:

67

从其他函数返回函数

现在让我们重写上面的程序并从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))
}

在线运行

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

simple函数从第15行调用。simple函数的返回值分配给s。现在s是由simple函数返回的函数。在第16行,我们调用函数s并传递它两个 int 参数。该程序输出:67

闭包

闭包是一个特殊的匿名函数。 它是一个可以访问函数体外的变量的匿名函数。

举个例子:

package main

import (  
    "fmt"
)

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

在线运行

在上面的程序中, 匿名函数访问的变量a在其正文的第10行之外。因此, 这个匿名函数是个闭包

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

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。让我们理解这意味着什么。

第16、17行中声明的变量ab是闭包, 它们受自己的t值约束。

我们首先使用参数World调用a。 现在, a版本的t变成了 Hello World

在第20行, 我们用参数Everyone调用b 。 由于b绑定到自己的变量t,因此bt版本的初始值为Hello 。 因此在这个函数调用之后, bt版本的值变为Hello Everyone 。 其余的程序是自我解释。

此程序将打印:

Hello World  
Hello Everyone  
Hello World Gopher  
Hello Everyone !  

头等函数的实际使用

到目前为止, 我们已经定义了什么是头等函数, 以及举例了一些简单的demo来了解它们是如何工作的。现在, 我们编写一个具体的程序, 来看下它的实际使用场景。

我们将创建一个程序,根据某些标准过滤一部分学生。 让我们一步一步地解决这个问题。

首先让我们定义学生类型。

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的第二个参数是一个函数, 它以student为参数, 返回bool。此函数确定特定学生是否符合条件。我们遍历3行的学生切片, 并将每个学生作为参数传递给函数f。如果此值返回true, 则表示学生已通过筛选条件, 并将其添加到结果切片r中。您可能对此函数的实际使用有点迷惑, 但一旦完成程序, 就会很清楚。我已经添加了主要功能, 并提供了下面的完整程序。

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)
}

在操场上奔跑

在主要功能上, 我们首先创建两个学生s1和s2 , 并添加到切片s。现在假设我们想找出所有有B级的学生。我们在上面的程序中建立了这个功能, 通过一个函数来检查学生是否有B级, 然后返回 true, 作为参数到38行的filter函数。上述程序将打印,

[{Samuel Johnson B USA}]

假设我们想找到所有来自印度的学生。这可以通过将函数参数更改为滤镜函数来轻松完成。
我提供了下面的代码,

c := filter(s, func(s student) bool {  
    if s.country == "India" {
        return true
    }
    return false
})
fmt.Println(c)  

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

让我们通过编写一个程序来结束本节。此程序将对切片的每个元素执行相同的操作, 并返回结果。例如, 如果我们要将切片中的所有整数相乘5并返回输出, 则可以使用第一类函数轻松地完成。在集合的每个元素上操作的这些函数称为map函数。我已经提供了下面的程序。这是自我解释。

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]

下面是我们对本教程中学到的内容做一个简要回顾

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

这就是所有关于头等函数的内容。祝你今天开心。

你可能感兴趣的:(Golang,tutorial,series)