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
}
以上例子,Calculate
的 f
参数类型为 CalculateType
, add
和 mul
函数具有和 CalculateType
函数类型相同的参数和返回值,因此可以将 add
和 mul
函数作为参数传入 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
类型的一个实例一样。换句话说,每个函数都和整型值、字符串值等一等公民一样,拥有自己的类型,也就是我们讲过的函数类型。
下面代码中的 HandlerFunc
、visitFunc
就是 Go
标准库中,基于函数类型进行自定义的类型:
// $GOROOT/src/net/http/server.go
type HandlerFunc func(ResponseWriter, *Request)// $GOROOT/src/sort/genzfunc.go
type 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()
}
输出结果:
6
8
这是函数类型
这是函数类型
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")
}
输出结果:
类型是: func
hello 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)
}
输出结果:
类型是: func
hello func type
exec
函数接收一个函数类型的参数,那么是不是只要是函数,就可以被当做参数传入到 exec
中吗?
答案是:不行。从 exec
函数的参数列表可知,exec
接收一个函数类型参数,这个函数类型参数接收一个字符串类型的参数。
Go 学习笔记(61)— Go 高阶函数、函数作为一等公民(函数作为输入参数、返回值、变量)的写法相关推荐
- B站台湾大学郭彦甫|MATLAB 学习笔记|06 高阶绘图 Advanced Plot
MATLAB学习笔记(06 高阶绘图 Advanced Plot) 如果想获得更好浏览体验的朋友可以转到下面链接 06 1. 对数图 (Logarithm Plots) x = logspace(-1 ...
- 学习笔记:MySQL高阶知识体系(下)——索引、锁、日志、隔离级别与MVCC
转载自https://www.ydlclass.com/doc21xnv/database/mysqladvance/mysqlAdvance2.html MySQL高阶知识体系(下) 6. 索引 6 ...
- 【台大郭彦甫】Matlab入门教程超详细学习笔记六:高阶绘图(附PPT链接)
高阶绘图 前言 一.进阶二维绘图 1. 对数图 2.一图双y轴 3. 直方图 4. 条形图 5. 饼状图 6. 极坐标图 7. 阶梯图与取样图 8. 箱线图以及误差线图 9. 填充图 二.配色 1.R ...
- python函数调用位置_python函数定义,调用,传参,位置参数及关键字参数,返回值
使用函数是真正开始编程的第一步,函数y=f(x)我们并不陌生,对x进行一顿操作得到一个值y.给不同的x,进行相同的操作,得到相应的y值. 程序层面函数是执行特定任务的一段代码,将一段代码定义成函数并为 ...
- 学习笔记:C++初阶【C++入门、类和对象、C/C++内存管理、模板初阶、STL简介、string、vector、list、stack、queueu、模板进阶、C++的IO流】
文章目录 前言 一.C++入门 1. C++关键字 2.命名空间 2.1 C语言缺点之一,没办法很好地解决命名冲突问题 2.2 C++提出了一个新语法--命名空间 2.2.1 命名空间概念 2.2.2 ...
- GD32学习笔记1(高难度工程,点亮一个LED灯)
系列文章目录 第一章 GD32学习笔记1(高难度工程,点亮一个LED灯) 文章目录 系列文章目录 前言 一.工作流程 二.新建工程的准备工作 三.新建工程 四.工程目录管理 五.代码实现 1.初始化 ...
- HiveQL学习笔记(二):Hive基础语法与常用函数
本系列是本人对Hive的学习进行一个整理,主要包括以下内容: 1.HiveQL学习笔记(一):Hive安装及Hadoop,Hive原理简介 2.HiveQL学习笔记(二):Hive基础语法与常用函数 ...
- python 一等公民_Python中一等公民——函数
Python中"一等公民"--函数 Python的函数是"一等公民". 你可以将它们分配给变量,将它们存储在数据结构中,将它们作为参数传递给其他函数,甚至将它们 ...
- 学习大数据的第13天——Java面向对象(接口、分析参数返回值的类型不同时如何解决、包以及访问权限修饰符(public、protected、默认、private))
学习大数据的第13天--Java面向对象(接口.分析参数返回值的类型不同时如何解决.包以及访问权限修饰符(public.protected.默认.private)) 接口 接口的基本定义: 1.1.语 ...
- python变量作用域图解_python笔记--作用域、高阶函数、闭包
作用域 python中被赋值的变量的位置不同,限制了能访问到变量的范围也不同,换句话说就是"变量的作用域是由其在代码中的位置所决定的". 1.LEGB规则 L(local):局部作 ...
最新文章
- 对比丨深度学习库大排名:TensorFlow、Keras名列一二,Sonnet增长最快
- PMP之路 – 第2天 (做模拟题)
- 使用bc45编译ucos-II的配置过程
- C++Builder函数集(文件操作、获取时间、类型转换等)
- 8255编程c语言程序,51单片机8255驱动C程序
- 手机psp模拟器哪个好_功能强大,手机微信群控系统和云控哪个好?
- 深度学习福利入门到精通第二讲——AlexNet模型
- 运行catia_浅谈CATIA开发——CAA简介
- UVa 674 - Coin Change
- java 项目开发日报_CSDN日报191114:Java开发干货分享
- java中正则验证邮箱手机格式
- php公众号关注自动回复内容,微信公众号自动回复内容大全集锦
- 英语介绍嵌入式计算机,计算机专业的英文自我介绍
- oracle stdevp函数,适用于sql初学,学习sql语句的一些整理,其中大多是oracle的
- 现代化个人博客系统 ModStartBlog v5.7.0 简约纯白主题,富文本大升级
- 利用springMVC实现购物车结算功能
- 差分进化算法_想用遗传算法?来看看这些已为你做好的开源优化框架
- Linux驱动学习--wifi驱动(rtl88xx系列网卡芯片)源码分析
- 访问者模式Visitor
- 什么是模块化?为什么要模块化