美文网首页
Go教程第二十六篇:First Class Function

Go教程第二十六篇:First Class Function

作者: 大风过岗 | 来源:发表于2020-05-20 10:20 被阅读0次

    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!

    备注
    本文系翻译之作原文博客地址

    相关文章

      网友评论

          本文标题:Go教程第二十六篇:First Class Function

          本文链接:https://www.haomeiwen.com/subject/mwlgohtx.html