200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > Go 学习笔记(61)— Go 高阶函数 函数作为一等公民(函数作为输入参数 返回值

Go 学习笔记(61)— Go 高阶函数 函数作为一等公民(函数作为输入参数 返回值

时间:2018-09-30 14:56:44

相关推荐

Go 学习笔记(61)— Go 高阶函数 函数作为一等公民(函数作为输入参数 返回值

函数在Go语言中属于“一等公民(First-Class Citizen)”拥有“一等公民”待遇的语法元素可以如下使用

可以存储在变量中;可以作为参数传递给函数;可以在函数内部创建并可以作为返回值从函数返回;

1. 函数可以存储在变量中

var (myFprintf = func(w io.Writer, format string, a ...interface{}) (int, error) {return fmt.Fprintf(w, format, a...)})func main() {fmt.Printf("%T\n", myFprintf) // func(io.Writer, string, ...interface {}) (int, error)myFprintf(os.Stdout, "%s\n", "Hello, Go") // 输出Hello,Go}

2. 作为参数传入函数

标准库time包的AfterFunc函数,就是一个接受函数类型参数的典型例子。你可以看看下面这行代码,这里通过AfterFunc函数设置了一个 2 秒的定时器,并传入了时间到了后要执行的函数。这里传入的就是一个匿名函数:

time.AfterFunc(time.Second*2, func() {println("timer fired") })

package mainimport "fmt"type CalculateType func(int, int) intfunc add(a, b int) int {return (a + b)}func mul(a, b int) int {return (a * b)}func Calculate(a, b int, f CalculateType) int {return f(a, b)}func main() {a, b := 2, 4fmt.Println(Calculate(a, b, add))// 6fmt.Println(Calculate(a, b, mul))// 8}

以上例子,Calculatef参数类型为CalculateTypeaddmul函数具有和CalculateType函数类型相同的参数和返回值,因此可以将addmul函数作为参数传入Calculate函数中。

3. 支持在函数内创建并通过返回值返回

func setup(task string) func() {println("do some setup stuff for", task)return func() {println("do some teardown stuff for", task)}}func main() {teardown := setup("demo")defer teardown()println("do some bussiness stuff")}

4. 拥有自己的类型

在前面讲解函数声明时,我们曾得到过这样一个结论:每个函数声明定义的函数仅仅是对应的函数类型的一个实例,就像

var a int = 13

这个变量声明语句中的a,只是int类型的一个实例一样。换句话说,每个函数都和整型值、字符串值等一等公民一样,拥有自己的类型,也就是我们讲过的函数类型。

下面代码中的HandlerFuncvisitFunc就是Go标准库中,基于函数类型进行自定义的类型:

// $GOROOT/src/net/http/server.gotype HandlerFunc func(ResponseWriter, *Request)// $GOROOT/src/sort/genzfunc.gotype visitFunc func(ast.Node) ast.Visitor

Go语言中,可以把函数作为一种变量,用type去定义它,那么这个函数类型就可以作为值传递,甚至可以实现方法,我们可以利用这一特性进行类型转换。

作为值传递的条件是类型具有相同的参数以及相同的返回值。Go语言的类型转换基本格式如下:

type_name(expression)

代码实现

package mainimport "fmt"type CalculateType func(int, int)func (c *CalculateType) Server() {fmt.Println("这是函数类型")}func add(a, b int) {fmt.Println(a + b)}func mul(a, b int) {fmt.Println(a * b)}func main() {a := CalculateType(add)b := CalculateType(mul)a(2, 4)b(2, 4)a.Server()b.Server()}

输出结果:

68这是函数类型这是函数类型

5. 高阶函数

先来说说什么是高阶函数?简单地说,高阶函数可以满足下面的两个条件之一:

接受其他的函数作为参数把其他的函数作为结果返回

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

高阶函数和闭包

所谓闭包就是一个函数体内部引用了一个外部的变量高阶函数和函数式编程的特点就是 支持函数作为参数或者返回值

示例:

package mainimport ("errors""fmt")type operate func(x, y int) int// 方案1。func calculate(x int, y int, op operate) (int, error) {// 函数类型属于引用类型,它的值可以为nil,而这种类型的零值恰恰就是nil。if op == nil {return 0, errors.New("invalid operation")}return op(x, y), nil}// 方案2。type calculateFunc func(x int, y int) (int, error)func genCalculator(op operate) calculateFunc {/*闭包定义:在一个函数中存在对外来标识符的引用。所谓的外来标识符,既不代表当前函数的任何参数或结果,也不是函数内部声明的,它是直接从外边拿过来的。*//*此处 return 函数是个闭包,它里面使用的变量op既不代表它的任何参数或结果也不是它自己声明的,而是定义它的genCalculator函数的参数,所以是一个自由变量。*/return func(x int, y int) (int, error) {if op == nil {return 0, errors.New("invalid operation")}return op(x, y), nil}}func main() {// 方案1。x, y := 12, 23op := func(x, y int) int {return x + y}result, err := calculate(x, y, op)fmt.Printf("The result: %d (error: %v)\n",result, err)result, err = calculate(x, y, nil)fmt.Printf("The result: %d (error: %v)\n",result, err)// 方案2。x, y = 56, 78add := genCalculator(op)result, err = add(x, y)fmt.Printf("The result: %d (error: %v)\n",result, err)}

Go可以定义函数类型的变量,函数类型的变量可以被调用。定义函数类型变量示例代码:

package mainimport ("fmt""reflect")func main() {var f = func(str string) {fmt.Println("hello", str)}fmt.Println("类型是:", reflect.ValueOf(f).Kind())f("func type")}

输出结果:

类型是: funchello func type

等价于

package mainimport ("fmt""reflect")func demo(str string) {fmt.Println("hello", str)}func main() {var f func(str string)f = demofmt.Println("类型是:", reflect.ValueOf(f).Kind())f("func type")}

函数类型变量也可以当做参数传递给另一个函数,然后在另一个函数中执行,示例代码如下:

package mainimport ("fmt""reflect")func exec(f func(str string)) {f("func type")}func main() {var f = func(str string) {fmt.Println("hello", str)}fmt.Println("类型是:", reflect.ValueOf(f).Kind())exec(f)}

输出结果:

类型是: funchello func type

exec函数接收一个函数类型的参数,那么是不是只要是函数,就可以被当做参数传入到exec中吗?

答案是:不行。从exec函数的参数列表可知,exec接收一个函数类型参数,这个函数类型参数接收一个字符串类型的参数。

Go 学习笔记(61)— Go 高阶函数 函数作为一等公民(函数作为输入参数 返回值 变量)的写法

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。