• Go中第一类函数


    什么是第一类函数?

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

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

    匿名函数

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

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	a := func() {
    		fmt.Println("hello world first class function")
    	}
    	a()
    	fmt.Printf("%T", a)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Run in playground

    在上面的程序中,我们为第 8 行中的变量分配了一个函数。这是将函数分配给变量的语法。如果您仔细观察,分配的函数没有名称。**这些类型的函数称为匿名函数,因为它们没有名称。

    调用此函数的唯一方法是使用变量 。我们在下一行中已经完成了此操作。 调用该函数,我们打印变量的类型。

    运行此程序将打印

    hello world first class function
    func()
    
    • 1
    • 2

    也可以调用匿名函数而不将其分配给变量。让我们看看在下面的示例中是如何完成的。

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	func() {
    		fmt.Println("hello world first class function")
    	}()
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    Run in playground

    在上面的程序中,在第 8 行中定义了一个匿名函数,在函数定义之后,我们立即在第 10 行中调用该函数。该程序将输出

    hello world first class function
    
    • 1

    It is also possible to pass arguments to anonymous functions just like any other function.

     1package main
     2
     3import (
     4	"fmt"
     5)
     6
     7func main() {
     8	func(n string) {
     9		fmt.Println("Welcome", n)
    10	}("Gophers")
    11}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    GO

    Run in playground

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

    Welcome Gophers
    
    • 1

    用户自定义函数类型

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

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

    创建了一个新的函数类型,它接受两个整数参数并返回一个整数。

    让我们编写一个程序

    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)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    Run in playground

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

    Sum 11
    
    • 1

    高阶函数

    高阶函数的定义是一个至少完成以下一项的函数

    • 接受一个或多个函数作为参数
    • 返回一个函数作为结果

    让我们看一下上面两个场景的一些简单示例。

    将函数作为参数传递给其他函数
    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)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    Run in playground

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

    67
    
    • 1

    现在让我们重写上面的程序并从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))
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    Run in playground

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

    这个简单的函数是从第 15 行调用的。simple 的返回值分配给s 。现在s包含 function 返回的函数simple。我们s在第 1 6行调用并传递两个 int 参数。该程序输出67

    闭包

    闭包是匿名函数的一种特殊情况。闭包是访问函数体外部定义的变量的匿名函数。

    举个例子会让事情变得更清楚。

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	a := 5
    	func() {
    		fmt.Println("a =", a)
    	}()
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Run in playground

    在上面的程序中,匿名函数访问第 10 行中存在于其主体外部的变量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("!"))
    }
    
    
    • 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

    Run in playground

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

    变量ab在17, 18行号中声明。 是闭包,它们与自己的值 绑定t

    我们首先a使用World参数进行调用。a现在的版本的值t变为Hello World

    在20 行号中。我们bEveryone参数调用。由于b绑定到它自己的变量t,因此 b的版本再次t具有初始值Hello。因此,在此函数调用之后, t 的版本的值变为Hello Everyone。该程序的其余部分是不言自明的。

    该程序将打印,

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

    一类函数的实际应用

    到目前为止,我们已经定义了什么是第一类函数,并且我们已经看到了一些人为的例子来了解它们是如何工作的。现在让我们编写一个具体的程序来展示第一类函数的实际用法。

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

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

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

    下一步是编写函数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
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在上面的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)
    }
    
    
    • 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
    • 46

    Run in playground

    main 函数中,我们首先创建两个学生s1并将s2他们添加到 slice 中s。现在假设我们想要找出所有有 Grade 的学生B。我们在上面的程序中通过传递一个函数来建立这一点,该函数检查学生是否有成绩B,如果有则返回 true。上面的程序将打印

    [{Samuel Johnson B USA}]
    
    • 1

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

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

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

    让我们再编写一个程序来结束本节。该程序将对切片的每个元素执行相同的操作并返回结果。例如,如果我们想要将切片中的所有整数乘以 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)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    Run in playground

    上面的程序会打印,

    [25 30 35 40 45]
    
    • 1
  • 相关阅读:
    什么是高可用,如何实现高可用
    2022年前端技术发展趋势
    YOLO V3 详解
    计算机毕业设计Java个性化穿搭推荐系统(源码+系统+mysql数据库+Lw文档)
    【华为OD机试真题 JS】根据某条件聚类最少交换次数
    “停车费”用英语怎么说?千万不要说Stop car money!柯桥BEC商务英语学习
    第六章:接口
    linux下ftp服务器的简单的搭建
    计算两坐标系轴上有对应三点的 旋转变换矩阵(Python)
    订单管理是客户关系管理的有效延伸,那么订单管理系统对于企业的作用有哪些呢?
  • 原文地址:https://blog.csdn.net/qq497811258/article/details/134242252