免费注册网站免登录,全网营销的公司,怎样做软件网站建设,小程序代理哪家好Go语言程序设计-第5章–函数
5.1 函数声明
每个函数声明都包含一个名字、一个形参列表、一个可选的返回列表以及函数体:
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 f…Go语言程序设计-第5章–函数
5.1 函数声明
每个函数声明都包含一个名字、一个形参列表、一个可选的返回列表以及函数体:
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, add) // func(int, int) int
fmt.Printf(%T\n, zero) // func(int, int) int函数的类型称为函数签名。
实参是按值传递的。如果提供的实参包含引用类型比如指针、slice、map、函数或者通道那么当函数使用形参变量时就有可能间接地修改实参变量。
有些函数的声明没有函数体说明这个函数使用除了 Go 以外的语言实现。
package math
func Sin(x float64) float64 // 使用汇编语言实现
5.2 递归
5.3 多返回值
函数可以有多个返回值。一个函数如果有命名的返回值可以省略 return 语句的操作数称为裸返回。
5.4 错误
Go 程序使用通常的控制流机制比如if 和 return语句应对错误。
5.4.1 错误处理策略
5.4.2 文件结束标识
EOF 定义
package io
import errorsvar EOF errors.New(EOF)使用示例
in : bufio.NewReader(os.Stdin)
for {r, _, err : in.ReadRune()if err io.EOF {break // 结束读取}if err ! nil {return fmt.Errorf(read failed: %v, err)}
}5.5 函数变量
函数变量也有类型可以赋给变量或者传递给其他函数或者从其他函数中返回。
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
mt.Println(f(3)) // 9函数类型的零值是nil(空值)调用一个空的函数变量导致宕机。
5.6 匿名函数
strings.Map(func(r rune) rune) {return r 1}, HAL-9000)函数里可以使用外层函数的变量。这些隐藏的变量引用就是我们把函数归类为引用类型而且函数变量无法进行比较的原因。函数变量类似于使用闭包方法实现的变量Go 程序员通常把函数变量成为闭包。
func squares() func() int {var x intreturn func() int {xreturn x * x}
}
func main() {f : squares()fmt.Println(f())fmt.Println(f())fmt.Println(f())fmt.Println(f())
}输出
1
4
9
16警告捕获迭代变量
var rmdirs []func()
for _, d : range tempDirs() {dir : d // 注意这一行是必须的os.MkdirAll(dirkk, 0755)rmdirs append(rmdirs, func() {os.RemoveAll(dir)})
}for _, rmdir : range rmdirs {rmdir() // 清理
}为什么在循环体内将循环变量赋给一个新的局部变量 dir而不是下面的版本。
var rmdirs []func()
for _, d : range tempDirs() {os.MkdirAll(dirkk, 0755)rmdirs append(rmdirs, func() {os.RemoveAll(dir)})
}原因是循环变量的作用域的规则限制。在上面的程序中dir 在 for 循环引进的一个块作用域内进行声明。在循环里创建的所有函数变量共享相同的变量 – 一个可以访问的存储位置而不是固定的值。dir 变量的值在不断地迭代中更新因为当调用清理函数时dir 变量是最后一次迭代时的值。我们用内部变量解决这个问题。
for _, dir : range tempDirs() {dir : dir // 声明内部 dir并以外表 dir 初始化
}5.7 变长函数有可变的参数个数
func sum(vals ...int) int {total : 0for _, val : range vals {total val}return total
}变长函数的类型和一个带有普通 slice 参数的函数类型不相同。
5.8 延迟函数调用
package ioutilfunc ReadFile(filename string)([]byte, error) {f, err : os.Open(filename)if err ! nil {return nil, err}defer f.Closereturn ReadAll(f)
}defer 语句无论在正常的情况下执行 return 语句或者函数执行完毕还是在不正常的情况下比如发生宕机实际的调用推迟到包含 defer 语句的函数结束后才放行。
func bigSlowOperation() {defer traxce(bigSlowOperation)() // 别忘记这对圆括号time.Sleep(10 * time.Second)
}func trace(msg string) func() {start : time.Now()log.Printf(enter %s, msg)return func() { log.Printf(exit %s (%s), msg, time.Since(start))}
}延迟的匿名函数能够改变外层函数返给调用者的结果。
func triple(x int) (result int) {defer func() { result x }()return x x
}func main() {fmt.Println(triple(4))
}5.9 宕机Panic
有些错误比如数组越界访问或者解引用空指针都需要在运行时进行检查。当 Go 语言运行时检测到这些错误就会发生宕机。
一个典型的宕机发生时正常的程序执行会终止 goroutine 中所有的延迟函数会执行然后程序会异常退出并打印一条日志。日志消息包括宕机的值这往往代表某种错误消息每一个 goroutine 都会在宕机的时候显示一个函数调用的栈跟踪消息。
宕机发生时defer 函数会以倒序执行。
func main() {defer printStack()
}func printStack() {var buf [4096]byten : runtime.Stack(buf[:], false)os.Stdout.Write(buf[:n])
}5.10 恢复
recover 会终止当前的宕机状态并且返回宕机的值。函数不会从之前宕机的地方继续运行而是正常返回。如果 recover 在没有宕机的情况下调用没有任何结果并且返回 nil。