1. 函数签名

函数类型也叫做函数签名,可以使用 fmt.Printf("%T") 格式化参数打印函数类型。

package mainimport "fmt"func sumTwo(a []int) (ret int) {for _, v := range a {ret += v}return
}
func main() {n := []int{1, 2, 3, 4, 5}result := sumTwo(n) fmt.Println("result is ", result) // result is  15fmt.Printf("sumTwo type is %T\n", sumTwo) // sumTwo type is func([]int) int}

两个函数类型相同的条件是:拥有相同的形参列表和返回值列表,其中列表元素的次序、个数和类型都相同,形参名称可以不相同。

Go 语言中,函数可是一等的(first-class)公民,函数类型也是一等的数据类型。Go 语言在语言层面支持了函数式编程。这是什么意思呢?

简单来说,这意味着函数不但可以用于封装代码、分割功能、解耦逻辑,还可以化身为普通的值,在其他函数间传递、赋予变量、做类型判断和转换等等,就像切片和字典的值那样。
而更深层次的含义就是:函数值可以由此成为能够被随意传播的独立逻辑组件(或者说功能模块)。

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

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

注意,各个参数和结果的名称不能算作函数签名的一部分,甚至对于结果声明来说,没有名称都可以。

只要两个函数的参数列表和结果列表中的元素顺序及其类型是一致的,我们就可以说它们是一样的函数,或者说是实现了同一个函数类型的函数。

严格来说,函数的名称也不能算作函数签名的一部分,它只是我们在调用函数时,需要给定的标识符而已。

package mainimport "fmt"type Printer func(contents string) (n int, err error)/*
函数printToStd的签名与Printer的是一致的,因此前者是后者的一个实现,即使它们的名称以及有的结果名称是不同的。
*/
func printToStd(contents string) (bytesNum int, err error) {return fmt.Println(contents)
}func main() {var p Printerp = printToStd // 将函数赋值给一个变量p("something")
}

2. 有名函数

有名函数是指函数有具体的名称,有名函数的函数名可以看作函数类型的常量,可以直接使用函数名调用函数,也可以直接赋值给函数类型变量,可以通过该变量来调用该函数。

package mainimport "fmt"func sum(a, b int) int {fmt.Println("I am in sum function")return a + b
}func main() {sum(3, 10)    // 直接调用result := sum(3, 4)fmt.Println("result is ", result)f := sum // 有名函数可以直接赋值给变量ret := f(3, 10)    // 通过该变量来调用该函数fmt.Println("ret is ", ret)
}

Go 语言中函数也是类型,可以作为参数传递给别的函数。

package main//定义一个函数类型,两个 int 参数,一个 int 返回值
type math func(int, int) int//定义一个函数 add,这个函数两个 int 参数一个 int 返回值,与 math 类型相符
func add(i int, j int) int {return i + j
}//再定义一个 multiply,这个函数同样符合 math 类型
func multiply(i, j int) int {return i * j
}//foo 函数,需要一个 math 类型的参数,用 math 类型的函数计算第 2 和第 3 个参数数字,并返回计算结果
//稍后在 main 中我们将 add 函数和 multiply 分别作为参数传递给它
func foo(m math, n1, n2 int) int {return m(1, 2)
}func main() {//传递 add 函数和两个数字,计算相加结果n := foo(add, 1, 2)println(n)//传递 multply 和两个数字,计算相乘结果n = foo(multiply, 1, 2)println(n)
}

输出结果:

3
2

3. 匿名函数

匿名函数是指不需要定义函数名的一种函数实现方式,由一个不带函数名的函数声明和函数体组成。

匿名函数顾名思义就是函数没有名称,匿名函数可以直接赋值给变量,可以当作实参,也可以作为返回值,还可以直接被调用。

匿名函数的定义格式如下:

func(参数列表)(返回参数列表){函数体
}

匿名函数的定义就是没有名字的普通函数定义。

3.1 在定义时调用匿名函数

匿名函数可以在声明后调用,例如:

func main() {func(data int) {fmt.Println("hello", data)}(100)  // }后的(100),表示对匿名函数进行调用,传递参数为 100。
}

或者

func main() {func() {sum := 0for i := 1; i <= 100; i++ {sum += i}fmt.Println("sum is ", sum)}()
}

3.2 将匿名函数赋值给变量

匿名函数可以被赋值,例如:

// 将匿名函数体保存到f()中
f := func(data int) {fmt.Println("hello", data)
}// 使用f()调用
f(100)

3.3 匿名函数用作回调函数

下面的代码实现对切片的遍历操作,遍历中访问每个元素的操作使用匿名函数来实现,用户传入不同的匿名函数体可以实现对元素不同的遍历操作,代码如下:

package mainimport ("fmt"
)// 遍历切片的每个元素, 通过给定函数进行元素访问
func visit(list []int, f func(int)) {for _, v := range list {f(v)}
}func main() {// 使用匿名函数打印切片内容,// 匿名函数作为实参visit([]int{1, 2, 3, 4}, func(v int) {fmt.Println(v)})
}

3.4 匿名函数实现操作封装

下面这段代码将匿名函数作为 map 的键值,通过命令行参数动态调用匿名函数,代码如下:

package mainimport ("flag""fmt"
)
//定义命令行参数 skill,从命令行输入 --skill 可以将 = 后的字符串传入 skillParam 指针变量。
var skillParam = flag.String("skill", "", "skill to perform")func main() {// 解析命令行参数,解析完成后,skillParam 指针变量将指向命令行传入的值。flag.Parse()// 定义一个从字符串映射到 func() 的 map,然后填充这个 mapvar skill = map[string]func(){"fire": func() {    // 初始化 map 的键值对,值为匿名函数。fmt.Println("chicken fire")},"run": func() {fmt.Println("soldier run")},"fly": func() {fmt.Println("angel fly")},}
/* skillParam 是一个 *string 类型的指针变量,
使用 *skillParam 获取到命令行传过来的值,并在 map 中查找对应命令行参数指定的字符串的函数。
*/if f, ok := skill[*skillParam]; ok {f()} else {fmt.Println("skill not found")}}

运行结果如下:

PS D:\code> go run main.go --skill=fly
angel fly
PS D:\code> go run main.go --skill=run
soldier run

3.5 匿名函数其它示例

package mainimport "fmt"// 匿名函数直接赋值给变量
var sum = func(a, b int) int {return a + b
}// 匿名函数作为返回值
func swap(str string) func(int, int) int {if str == "add" {return func(a, b int) int {return a + b}}if str == "sub" {return func(a, b int) int {return a - b}}return nil}func input(f func(int, int) int, a, b int) int {return f(a, b)
}
func main() {// 匿名函数作为实参z := input(func(x, y int) int {return x + y}, 10, 20)fmt.Println("z is ", z)fmt.Println("sum(1, 3) is ", sum(1, 3))fmt.Println("swap(1, 3) is ", swap("sub")(1, 10))}

输出:

z is  30
sum(1, 3) is  4
swap(1, 3) is  -9

匿名函数可赋值给变量,做为结构字段,或者在 channel 里传送。

package mainfunc main() {// --- function variable ---fn := func() { println("Hello, World!") }fn()// --- function collection ---fns := [](func(x int) int){func(x int) int { return x + 1 },func(x int) int { return x + 2 },}println(fns[0](100))// --- function as field ---d := struct {fn func() string}{fn: func() string { return "Hello, World!" },}println(d.fn())// --- channel of function ---fc := make(chan func() string, 2)fc <- func() string { return "Hello, World!" }println((<-fc)())
}

输出:

Hello, World!
101
Hello, World!
Hello, World!

Go 学习笔记(16)— 函数(02)[函数签名、有名函数、匿名函数、调用匿名函数、匿名函数赋值给变量、匿名函数做回调函数]相关推荐

  1. cocos2d-x学习笔记16:记录存储1:CCUserDefault

    cocos2d-x学习笔记16:记录存储1:CCUserDefault 一.简述 CCUserDefalt作为NSUserDefalt类的cocos2d-x实现版本,承担了cocos2d-x引擎的记录 ...

  2. Hadoop学习笔记—16.Pig框架学习

    Hadoop学习笔记-16.Pig框架学习 一.关于Pig:别以为猪不能干活 1.1 Pig的简介 Pig是一个基于Hadoop的大规模数据分析平台,它提供的SQL-LIKE语言叫Pig Latin, ...

  3. Linux 学习笔记16 信号量

    Linux 学习笔记16 信号量Semaphore 信号量概念 信号量(或信号灯)是一种用于提供不同进程间或一个给定进程的不同线程间同步手段的原语. 信号量是控制进程(或线程)同步(谁先执行,谁后执行 ...

  4. GAMES101-现代计算机图形学学习笔记(作业02)

    GAMES101-现代计算机图形学学习笔记(作业02) Assignment 02 GAMES101-现代计算机图形学学习笔记(作业02) 作业 作业描述 需要补充的函数 思路 结果 原课程视频链接以 ...

  5. 区块链学习笔记16——ETH交易树和收据树

    区块链学习笔记16--ETH交易树和收据树 学习视频:北京大学肖臻老师<区块链技术与应用> 笔记参考:北京大学肖臻老师<区块链技术与应用>公开课系列笔记--目录导航页 交易树和 ...

  6. Python学习笔记16:实操案例十三(编写程序实现乐手弹奏乐器,设计自定义类表达出租车和家用轿车信息)

    Python学习笔记16:实操案例十三(编写程序实现乐手弹奏乐器,设计自定义类表达出租车和家用轿车信息) 1.编写程序实现乐手弹奏乐器 注意Python的多态是"鸭子类型",只要有 ...

  7. SpringBoot学习笔记(16)----SpringBoot整合Swagger2

    Swagger 是一个规范和完整的框架,用于生成,描述,调用和可视化RESTful风格的web服务 http://swagger.io Springfox的前身是swagger-springmvc,是 ...

  8. 台大李宏毅Machine Learning 2017Fall学习笔记 (16)Unsupervised Learning:Neighbor Embedding

    台大李宏毅Machine Learning 2017Fall学习笔记 (16)Unsupervised Learning:Neighbor Embedding

  9. Netty网络框架学习笔记-16(心跳(heartbeat)服务源码分析)

    Netty网络框架学习笔记-16(心跳(heartbeat)服务源码分析_2020.06.25) 前言: Netty 作为一个网络框架,提供了诸多功能,比如编码解码等,Netty 还提供了非常重要的一 ...

最新文章

  1. Windows server 2008普通用户不能远程登录问题
  2. [转帖]Sqlcmd使用详解
  3. 主数据管理(MDM)的七个最佳实践
  4. html表格点击为编辑框,el-table表格内双击或单击单元格编辑输入框、日期等
  5. Yii2 主从 数据库
  6. 【机器学习】opencv-摄像头中的人脸采集
  7. GB28181开放流媒体服务平台LiveGBS实际测试时问题排查
  8. MongoDB的查询语法和SQL的SELECT语法做对比
  9. 突发!Log4j 爆“核弹级”漏洞,Flink、Kafka等至少十多个项目受影响,微博、京东、网易等大厂都发起应急响应...
  10. 计算机视觉 --基于opencv实现证件照换底色、翻转
  11. 生成QQ/MSN/旺旺/SKYPE等在线状态图标
  12. 各大主流编程语言比较,运用场景
  13. 方舟非主机服务器无限距离,方舟生存进化怎么调主机距离
  14. java coap_分布式项目(三)CoAp client and server
  15. 论文笔记之:Playing for Data: Ground Truth from Computer Games
  16. python pygame模块按键响应
  17. 常用的八款免费程序员喜欢的代码编辑器推荐「你用哪个」
  18. 串口的TXD、RXD、GND分别是什么意思?
  19. java练习题-显示人的年龄和姓名
  20. 小米wifi智能家居android,小米智能家居:米家智能插座的无线协议——Wi-Fi与Zigbee...

热门文章

  1. nginx介绍及常用功能
  2. MyBatis的插入后获得主键的方式
  3. 2022-2028年中国金融云行业市场研究及前瞻分析报告
  4. 2022-2028年中国水处理分离膜行业市场现状调研及市场需求潜力报告
  5. NumPy — 创建全零、全1、空、arange 数组,array 对象类型,astype 转换数据类型,数组和标量以及数组之间的运算,NumPy 数组共享内存
  6. 笔记本通过网线连接并控制工控机
  7. 【JavaScript总结】JavaScript语法基础:JS高级语法
  8. dataframe多列合并成一列
  9. pytorch 动态调整学习率 重点
  10. Tengine Web服务器概述