Golang函数
一、函数定义
函数声明包括函数名、形式参数列表、返回值列表(可省略)以及函数体。
func name(parameter-list) (result-list) {
body
}
func add(x int, y int) int {return x + y}
func sub(x, y int) (z int) { z = x - y; return}
func first(x int, _ int) int { return x }
func zero(int, int) int { return 0 }
fmt.Printf("%T\n", add) // "func(int, int) int"
fmt.Printf("%T\n", sub) // "func(int, int) int"
fmt.Printf("%T\n", first) // "func(int, int) int"
fmt.Printf("%T\n", zero) // "func(int, int) int"
二、递归
函数可以调用函数自身。
func f(n int) int {
if n == 1 || n == 2 {
return 1
}
return f(n-1) + f(n-2)
}
三、多返回值函数
Go可以返回多个值给调用者
一般返回数据信息和错误信息
func calc(x, y int) (int, int) {
return x + y, x * y
}
四、错误
函数执行时难免产生错误。比如通常IO操作很容易产生错误。
- 错误处理策略
(1) 传播错误:将错误传给上层调用者处理
func ...{
resp, err := http.Get(url)
if err != nil{
return nill, err
}
}
(2) 偶然性的错误,采用尝试有限次数的重试策略
// WaitForServer attempts to contact the server of a URL.
// It tries for one minute using exponential back-off.
// It reports an error if all attempts fail.
func WaitForServer(url string) error {
const timeout = 1 * time.Minute
deadline := time.Now().Add(timeout)
for tries := 0; time.Now().Before(deadline); tries++ {
_, err := http.Head(url)
if err == nil {
return nil // success
}
log.Printf("server not responding (%s);retrying…", err)
time.Sleep(time.Second << uint(tries)) // exponential back-off
}
return fmt.Errorf("server %s failed to respond after %s", url, timeout)
}
(3) 输出错误信息,结束程序。一般在main函数中执行的策略。
func main() {
fmt.Println("before log fatal")
log.Fatalf("Site is down\n") //exit
fmt.Println("after log fatal")
}
(4) 只输出错误信息,不做其它处理
if err := Ping(); err != nil {
log.Printf("ping failed: %v; networking disabled",err)
}
(5) 忽略掉错误
- 文件结尾错误(EOF)
in := bufio.NewReader(os.Stdin)
for {
r, _, err := in.ReadRune()
if err == io.EOF {
break // finished reading
}
if err != nil {
return fmt.Errorf("read failed:%v", err)
}
// ...use r…
}
五、函数值
在Go中,函数被看作第一类值(first-class values):函数像其他值一样,拥有类型, 可以被赋值给其他变量,传递给函数,从函数返回。对函数值(function value)的调用 类似函数调用。 函数值不可以作map的key。 函数值的默认零值为nil。
func square(n int) int { return n * n }
func negative(n int) int { return -n }
func product(m, n int) int { return m * n }
f := square
fmt.Println(f(3)) // "9"
f = negative
fmt.Println(f(3)) // "-3"
fmt.Printf("%T\n", f) // "func(int) int"
f = product // compile error: can't assign func(int, int) int to func(int) int
六、匿名函数
函数值字面量是一种表达式,它的值被称为匿名函数(anonymous function)。
// squares返回一个匿名函数。
// 该匿名函数每次被调用时都会返回下一个数的平方。
func squares() func() int {
var x int
return func() int {
x++
return x * x
}
}
func main() {
f := squares()
fmt.Println(f()) // "1"
fmt.Println(f()) // "4"
fmt.Println(f()) // "9"
fmt.Println(f()) // "16"
}
//解析: f := squares()执行后,
// f = func() int { x++; return x * x }
// 函数 + 引用环境 = 闭包
七、可变参数
参数数量可变的函数称为为可变参数函数。 在参数列表的最后一个参数类型之前加上省略符号“…”,这表示 该函数会接收任意数量的该类型参数。
func sum(vals...int) int {
total := 0
for _, val := range vals {
total += val
}
return total
}
// 在函数体中,vals被 "看作" 是类型为[]int的切片
fmt.Println(sum()) // "0"
fmt.Println(sum(3)) // "3"
fmt.Println(sum(1, 2, 3, 4)) // "10"
// 若传入参数已经为[]int 切片,只需
// 在最后一个参数后加上省略符。
values := []int{1, 2, 3, 4}
fmt.Println(sum(values...)) // "10"
//实际上切片参数函数和可变参数函数是不同的函数
func f(...int) {}
func g([]int) {}
fmt.Printf("%T\n", f) // "func(...int)"
fmt.Printf("%T\n", g) // "func([]int)"
八、Deferred函数
- defer 在函数执行完毕时,才执行 defer后面的语句或函数
- defer多用于成对的操作,如打开和关闭、连接和断开、加锁和解锁等
- 即使defer作用域中语句执行异常,defer后面的语句也会被执行
var mu sync.Mutex
var m = make(map[string]int)
func lookup(key string) int {
mu.Lock()
defer mu.Unlock()
return m[key]
}
九、Panic异常
Panic 用于处理严重的错误 任何能够被预料的错误,都应该被优雅的处理,而不用Panic
func Reset(x *Buffer) {
if x == nil {
panic("x is nil") // unnecessary!
}
x.elements = nil
}
十、Recover捕获异常
通常来说,不应该对panic异常做任何处理,但有时,也许我们可以从异常中恢复,至少我们 可以在程序崩溃前,做一些操作。
func Parse(input string) (s *Syntax, err error) {
defer func() {
if p := recover(); p != nil {
err = fmt.Errorf("internal error: %v", p)
}
}()
// ...parser...
}