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 }