深入理解 Go 语言的一等函数及其应用

举报
宇宙之一粟 发表于 2022/10/27 13:51:09 2022/10/27
【摘要】 什么是 Go 中的一等函数任何一门编程语言都离不开函数,无论是命令式语言 C、多范式编程语言 C++,还是面向对象编程语言 Java、Ruby,动态语言 Python、JavaScript,函数这一语法元素都是当仁不让的核心。Go 语言没有面向对象语言的语法,比如类 、继承、对象,但 Go 语言中最重要的部分就是支持一等函数。在 Go 语言中,函数式唯一一种基于特定输入、实现特定任务并可反馈...

深入理解 Go 语言的一等函数及其应用

什么是 Go 中的一等函数

任何一门编程语言都离不开函数,无论是命令式语言 C、多范式编程语言 C++,还是面向对象编程语言 Java、Ruby,动态语言 Python、JavaScript,函数这一语法元素都是当仁不让的核心。


Go 语言没有面向对象语言的语法,比如类 、继承、对象,但 Go 语言中最重要的部分就是支持一等函数。


在 Go 语言中,函数式唯一一种基于特定输入、实现特定任务并可反馈任务执行结果的代码块。本质上 Go 程序就是一组函数的集合。


什么是一等函数


一等函数允许将函数分配给变量(将函数通过变量进行传递),作为参数传递给其他函数,并从其他函数返回。

匿名函数

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


package main

import(
    "fmt"
)

func main() {
    a := func() {
        fmt.Println("Learning first class Function")
    }
    a()

    fmt.Printf("%T", a)
}


在上面的程序中,我们利用 a := func() 给变量 a 分配了一个函数,这是将一个函数赋值给一个变量的语法。


然后我们分配给 a 的函数并没有名字,这类函数就被称为匿名函数


调用这个函数的唯一方法就是使用变量 a,所以在后面使用 a() 来调用这个函数,这就会打印出 Learning first class Function


然后我们打印变量 a 的类型,这将打印出 func()


运行结果:


Learning first class Function
func()


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


package main

import (
    "fmt"
)

func main() {
    func() {
        fmt.Println("Learing first class Function")
    }()
}


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


Learing first class Function


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


package main

import (
    "fmt"
)

func main() {
    func(n string) {
        fmt.Println("Welcome to", n)
    }("Gophers's World!")
}


我们在上面的代码中,向匿名函数中传入一个 n string 字符串参数,然后在调用时传入一个 "Gophers's World!" ,此时,运行程序将得到如下的结果:


Welcome to Gophers's World!

用户自定义的函数类型

就像我们自定义结构体类型一样,在 Go 语言中也支持自定义函数类型:


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
    }

    sum := a(2022, 10)
    fmt.Println("a + b = ", sum)
}


上面的程序中,我们定义了一个 add 类型的变量,并给它分配了一个签名与 add 类型相符的函数,接着通过 sum := a(2022,10) 调用并将结果赋给 sum,运行程序后得到如下的结果:


a + b =  2032

高阶函数

对高阶函数的定义是这个函数至少做到以下的某一项的功能:


  • 以一个或者多个函数作为参数

  • 返回一个函数作为其结果

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

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 函数,它接收两个 int 参数,并返回一个 int 参数,然后把匿名函数传给变量 f,然后把 f 作为参数传递给 simple 函数,最终这个程序将打印 67 输出:


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(2022, 60))
}


运行该程序,得到结果;


2082

闭包

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


代码如下:


package main

import (
    "fmt"
)

func main() {

    a := 2022

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


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


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 上,变量 ab 是闭包,被绑定到它们自己的值 t 上。


我们传递参数 Worlda,然后 a 的值变成了 Hello World


传递参数 Everyone 给 b,然后 b 的值变成了 Hello Everyone


Hello World  
Hello Everyone  
Hello World Gopher  
Hello Everyone !  


闭包通常也是支持嵌套和 defer 工作的方法。在下面的例子中,我们可以看到一个允许我们嵌套工作的函数闭包:


package main

import (
  "fmt"
  "sort"
)

func main() {
  input := []string{"foo", "bar", "baz"}
  var result []string
  // closure callback
  func() {
    result = append(input, "abc")
    result = append(result, "def")
    sort.Sort(sort.StringSlice(result))
  }()
  fmt.Println(result)
}


运行结果:


[abc bar baz def foo]

一等函数的实际应用

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


我们将创建一个程序,根据一些标准来过滤一部分学生。让我们一步一步地去做。


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


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 行遍历学生切片。如果该函数返回真,则意味着该学生通过了过滤标准,并被添加到切片 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。 上述程序将打印:


[{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]

总结

在本文中,介绍了什么是一等函数的概念和功能,匿名函数、用户自定义函数类型、高阶函数和闭包,最后给出了一等函数的实际应用例子,希望这篇文章对你有所帮助!

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。