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

1. 函数签名

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

package main

import "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  15

	fmt.Printf("sumTwo type is %T\n", sumTwo) // sumTwo type is func([]int) int

}

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

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

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

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

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

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

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

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

package main

import "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 Printer
	p = printToStd // 将函数赋值给一个变量
	p("something")
}

2. 有名函数

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

package main

import "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 := 0
		for 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 main

import (
	"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 main

import (
	"flag"
	"fmt"
)
//定义命令行参数 skill,从命令行输入 --skill 可以将 = 后的字符串传入 skillParam 指针变量。
var skillParam = flag.String("skill", "", "skill to perform")

func main() {
	// 解析命令行参数,解析完成后,skillParam 指针变量将指向命令行传入的值。
	flag.Parse()
	// 定义一个从字符串映射到 func() 的 map,然后填充这个 map
	var 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 main

import "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 main

func 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!
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值