Golang开发之函数(function)
基础
普通函数的声明形式
func 函数名(参数列表)(返回值列表) {
函数体
}
参数类型的简写
func add(a, b int) int {
return a + b
}
函数的返回值
同一种类型返回值
使用 return 语句返回时,值列表的顺序需要与函数声明的返回值类型一致。
func typedTwoValues() (int, int) {
return 1, 2
}
func main() {
a, b := typedTwoValues()
fmt.Println(a, b)
}
带有变量名的返回值
Go 语言支持对返回值进行命名,这样返回值就和参数一样拥有参数变量名和类型。
命名的返回值变量的默认值为类型的默认值,即数值为0,字符串为空字符串,布尔为false、指针为nil等。
下面代码中的函数拥有两个整型返回值,函数声明时将返回值命名为a和b ,因此可以在函数体中直接对函数返回值进行赋值。
func namedRetValues() (a, b int){
a = 1
b = 2
return
}
下面代码的执行效果和上面代码的效果一样:
func namedRetValues() (a, b int){
a = 1
return a, 2
}
提示: 同一种类型返回值和命名返回值两种形式只能二选一, 混用时将会发生编译错误。
func namedRetValues() (a, b int, int)
示例:将“秒”解析为时间单位
在本例中,使用一个数值表示时间中的“秒”值, 然后使用 resolveTime() 函数将传入的秒数转换为天、小时和分钟等时间单位。
const (
// 定义每分钟的秒数
SecondsPerMinute = 60
// 定义每小时的秒数
SecodesPerHour = SecondsPerMinute * 60
// 定义每天的秒数
SecodesPerDay = SecodesPerHour * 24
)
func resolveTime(seconds int) (day int, hour int, minute int){
day = seconds / SecodesPerDay
hour = seconds / SecodesPerHour
minute = seconds / SecondsPerMinute
return
}
func main() {
// 将返回值作为打印参数
fmt.Println(resolveTime(1000))
// 只获取消息和分钟
_, hour, minute := resolveTime(18000)
fmt.Println(hour, minute)
// 只获取天
day, _, _ := resolveTime(90000)
fmt.Println(day)
}
匿名函数一一没青函数名字的函数
函数也是一种类型,因此可以定义一个函数类型的变量,格式:
func (参数列表) (返回参数列表){
函数体
}
在定义时调用匿名函数
func (data int){
fmt.Println("hello", data)
}(100)
// "}" 后的 "(100)",表示对匿名函数进行调用,传递参数为 10
匿名函数用作回调函数
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)
})
}
匿名函数作为回调函数的设计在 Go 语言的系统包中也比较常见 strings 包中就有如:
func TrimFunc(s string, f func(rune) bool) string {
return TrimRightFunc(TrimLeftFunc(s, f), f)
}
匿名函数的简单示例
示例1:
func test2() {
f1 := func(a, b int) int {
return a + b
}
fmt.Printf("f1类型是%T
", f1)
sum := f1(2, 3)
fmt.Println(sum)
}
func main() {
test2()
}
示例2:
func test3() {
var i int = 0
defer fmt.Printf("defer i=%d
", i)
i = 100
fmt.Println(i)
return
}
func main() {
test3()
}
示例3:
func test4() {
var i int = 0
defer func() {
fmt.Printf("defer i=%d
", i)
}()
i = 100
fmt.Printf("i=%d
", i)
return
}
func main() {
test3()
}
函数类型作为参数
func test1(a, b int, my func(int, int) int) int {
return my(a, b)
}
func sub(a, b int) int {
return a - b
}
func main() {
res := test1(30, 10, sub)
fmt.Println(res)
}
闭包( Closure )————引用了外部变量的匿名函数
闭包是引用了自由变量的函数,即使离开了自由变量的环境也不会被删除或释放,简单说:函数+引用环境=闭包
闭包示例
示例1:
func add() func(int) int {
var x int
return func(d int) int {
x += d
return x
}
}
func main() {
var f = add()
fmt.Println(f(10))
fmt.Println(f(10))
fmt.Println(f(10))
}
示例2:
func add(base int) func(int) int {
return func(i int) int {
base += i
return base
}
}
func main() {
tmp1 := add(10)
fmt.Println(tmp1(1), tmp1(2))
// 此时tmp1和tmp2不是一个实体了
tmp2 := add(100)
fmt.Println(tmp2(1), tmp2(2))
}
闭包实现生成器
// 创建一个玩家生成器,输入名称,输出生成器
func playerGen(name string) func() (string, int) {
// 血量一直为150
hp := 150
// 返回创建的闭包
return func() (string, int) {
// 将变量引用到闭包中
return name, hp
}
}
func main() {
// 创建一个玩家生成器
generator := playerGen("high noon")
// 返回玩家的名字和血量
name, hp := generator()
// 打印值
fmt.Println(name, hp)
}
多闭包返回
func test01(base int) (func(int) int, func(int) int) {
add := func(i int) int {
base += i
return base
}
sub := func(i int) int {
base -= i
return base
}
return add, sub
}
func main() {
f1, f2 := test01(10)
fmt.Println(f1(1), f2(2))
fmt.Println(f1(3), f2(4))
}
延迟执行语句(defer)
示例1:
func main() {
fmt.Println("defer begin")
// 将defer放入延迟调用栈
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
fmt.Println("defer end")
}
示例2:
func main() {
var i int = 0
defer fmt.Printf("defer i=%d
", i)
i = 1000
fmt.Printf("i=%d
", i)
}
defer使用案例
使用defer解锁
在下面的例子中会在函数中并发使用 map ,为防止竞态问题,使用 sync.Mutex 进行加锁,示例:
var (
// 一个演示用的映射
valueByKey = make(map[string]int)
// 保证使用映射时的并发安全的互斥锁
// map 默认不是并发安全的,准备一个 sync.Mutex 互斥量保护 map 的访问
valueByKeyGuard sync.Mutex
)
// 根据键读取值
// readValue() 函数给定,从 map 获得值后返回,该函数会在并发环境中使用,需要保证并发安全
func readValue(key string) int {
valueByKeyGuard.Lock()
// defer后面的语句不会马上调用,而是延迟到函数结束时调用
// 该语句不会马上执行,而是等 readValue() 返回时才会被执行。
defer valueByKeyGuard.Unlock()
return valueByKey[key]
}
释放文件旬柄
func fileSize(filename string) int64 {
f, err := os.Open(filename)
if err != nil {
return 0
}
defer f.Close()
info, err := f.Stat()
if err != nil {
return 0
}
size := info.Size()
return size
}