Go36-12-函数

栏目: Go · 发布时间: 7年前

内容简介:在Go语言中,函数是一等(first-class)公民,函数类型也是一等的数据类型。函数不但可以用于封装代码、分割功能、解耦逻辑,还可以化身为普通的值,在其他函数间传递、赋予变量、做类型判断和转换等等。函数值可以由此成为能够被随意传播的独立逻辑组件(或者说功能模块)。开头说的,函数是一等公民,函数类型是一等数据类型:

函数

在 Go 语言中,函数是一等(first-class)公民,函数类型也是一等的数据类型。

函数不但可以用于封装代码、分割功能、解耦逻辑,还可以化身为普通的值,在其他函数间传递、赋予变量、做类型判断和转换等等。函数值可以由此成为能够被随意传播的独立逻辑组件(或者说功能模块)。

一等公民

开头说的,函数是一等公民,函数类型是一等数据类型:

package main

import "fmt"

type calcFunc func(int, int) int

func add(x, y int) int {
    return x + y
}

func sub(x, y int) int {
    return x - y
}

func main() {
    var f1, f2 calcFunc
    f1, f2 = add, sub
    fmt.Println(f1(1, 2))
    fmt.Println(f2(100, 50))
}

这里,先声明了一个函数类型。在下面声明的两个函数的前面与caleFunc是一致的。因此都是caleFunc的一个实现。在main函数中,分别把两个函数赋值给caleFunc类型的变量f1和f2,然后可以调用它们。

这里书写 函数签名 的方式与函数声明的是一致的。只是紧挨在参数列表左边的不是函数名称,而是关键字func。这里函数名称和func互换了一下位置而已。

函数签名,就是函数的参数列表和结果列表的统称,它定义了可用来鉴别不同函数的那些特征,同时也定义了我们与函数交互的方式。

“函数是一等的公民”是函数式编程(functional programming)的重要特征。Go语言在语言层面支持了函数式编程。

高阶函数

高阶函数可能具有如下两个特点:

  • 接收其他的函数作为参数传入
  • 把其他的函数作为结果返回

函数只要满足了上面的任意一个特点,就可以说这个函数是一个高阶函数。高阶函数也是函数式编程中的重要概念和特征。

在上面的例子的基础上,写一个高阶函数,然后再main主函数里调用执行:

package main

import (
    "errors"
    "fmt"
)

type calcFunc func(int, int) int

func add(x, y int) int {
    return x + y
}

func sub(x, y int) int {
    return x - y
}

func calculate(x, y int, f calcFunc) (int, error) {
    if f == nil {
        return 0, errors.New("invalid calcFunc")
    }
    return f(x, y), nil
}

func main() {
    res, err := calculate(1, 2, add)
    if err != nil {
        fmt.Println("ERROR:", err)
    }
    fmt.Println("Result:", res)
}

上面的calculate就是一个高阶函数,“接收其他的函数作为参数传入”的这种高阶函数。下面的示例是另外一种高阶函数“把其他的函数作为结果返回”:

package main

import (
    "errors"
    "fmt"
)

type calcFunc func(int, int) int

func add(x, y int) int {
    return x + y
}

func sub(x, y int) int {
    return x - y
}

type resFunc func(int, int) (int, error)

func genCalculator(f calcFunc) resFunc {
    return func(x, y int) (int, error) {
        if f == nil {
            return 0, errors.New("invalid calcFunc")
        }
        return f(x, y), nil
    }
}

func main() {
    f := genCalculator(sub)
    res, _ := f(10, 6)
    fmt.Println("Result:", res)
}

闭包

自由变量,在一个函数中存在对外来标识符的引用。所谓的外来标识符,是既不代表当前函数的任何参数或结果,也不是函数内部声明的,它是直接从外边拿过来的。

上面的例子中的genCalculator函数内部,实际上就实现了一个闭包。而genCalculator函数也是一个高阶函数:

func genCalculator(f calcFunc) resFunc {
    return func(x, y int) (int, error) {
        if f == nil {
            return 0, errors.New("invalid calcFunc")
        }
        return f(x, y), nil
    }
}

genCalculator函数只做了一件事,那就是定义一个匿名的函数并把它作为结果值返回。而这个匿名的函数就是一个闭包函数。它里面使用的变量f既不代表它的任何参数或结果也不是它自己声明的,而是定义它的genCalculator函数的参数,所以是一个自由变量。而自由变量具体是什么,并不是在定义闭包的时候确定的,而是在genCalculator函数被调用的时候确定的。

函数是Go语言支持函数式编程的主要体现。我们可以通过“把函数传给函数”以及“让函数返回函数”来编写高阶函数,也可以用高阶函数来实现闭包,并以此做到部分程序逻辑的动态生成。

最后,还有一个闭包的典型例子:

package main

import "fmt"

func increment () func() {
    var x int
    return func () {
        x ++
        fmt.Println(x)
    }
}

func main() {
    f := increment()
    f()  // 打印1
    f()  // 打印2
    f()  // 打印3
}

主函数里,每次调用执行的结果都会变化。变量x属于闭包的一部分,但是是在闭包的函数外的,每次调用闭包后的x的状态都会保留下来。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

Don't Make Me Think

Don't Make Me Think

Steve Krug / New Riders Press / 18 August, 2005 / $35.00

Five years and more than 100,000 copies after it was first published, it's hard to imagine anyone working in Web design who hasn't read Steve Krug's "instant classic" on Web usability, but people are ......一起来看看 《Don't Make Me Think》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

SHA 加密
SHA 加密

SHA 加密工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具