当前位置: 首页 > news >正文

Go语言程序设计-第5章--函数

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 "errors"var 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 {x++return 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。

相关文章:

  • 【Swagger】常用注解的使用、SpringBoot的整合及生产环境下屏蔽Swagger
  • [每周一更]-(第43期):Golang版本的升级历程
  • linux安装anaconda
  • 自定义html5中日期选取器的样式
  • uniapp-H5项目的坑
  • 经典卷积神经网络-VGGNet
  • Qt 中使用 MySQL 数据库保姆级教程(下)
  • Springer build pdf乱码
  • Android 理解Context
  • Oracle-深入了解cache buffer chain
  • Postman常见问题及解决方法
  • 目标检测-One Stage-SSD
  • go mod 命令详解
  • C# 如何读取Excel文件
  • Qt高质量的开源项目合集
  • IE9 : DOM Exception: INVALID_CHARACTER_ERR (5)
  • CentOS6 编译安装 redis-3.2.3
  • ES6系列(二)变量的解构赋值
  • JavaScript 基本功--面试宝典
  • JavaScript 奇技淫巧
  • JSONP原理
  • php的插入排序,通过双层for循环
  • puppeteer stop redirect 的正确姿势及 net::ERR_FAILED 的解决
  • 阿里云ubuntu14.04 Nginx反向代理Nodejs
  • 使用 @font-face
  • 一道闭包题引发的思考
  • 原创:新手布局福音!微信小程序使用flex的一些基础样式属性(一)
  • 在Docker Swarm上部署Apache Storm:第1部分
  • 阿里云服务器购买完整流程
  • ​批处理文件中的errorlevel用法
  • #LLM入门|Prompt#2.3_对查询任务进行分类|意图分析_Classification
  • #Lua:Lua调用C++生成的DLL库
  • #NOIP 2014# day.1 T3 飞扬的小鸟 bird
  • (poj1.2.1)1970(筛选法模拟)
  • (附源码)ssm失物招领系统 毕业设计 182317
  • (十六)Flask之蓝图
  • (四)Android布局类型(线性布局LinearLayout)
  • (算法)求1到1亿间的质数或素数
  • (转)创业家杂志:UCWEB天使第一步
  • .[backups@airmail.cc].faust勒索病毒的最新威胁:如何恢复您的数据?
  • .NET LINQ 通常分 Syntax Query 和Syntax Method
  • .Net高阶异常处理第二篇~~ dump进阶之MiniDumpWriter
  • .NET序列化 serializable,反序列化
  • .NET中 MVC 工厂模式浅析
  • [ 2222 ]http://e.eqxiu.com/s/wJMf15Ku
  • [ HTML + CSS + Javascript ] 复盘尝试制作 2048 小游戏时遇到的问题
  • [BZOJ1178][Apio2009]CONVENTION会议中心
  • [cb]UIGrid+UIStretch的自适应
  • [Head First设计模式]策略模式
  • [IMX6DL] CPU频率调节模式以及降频方法
  • [macOS] Mojave10.14 夜神安卓模拟器启动问题
  • [mysql] mysqldump 导出数据库表
  • [NAND Flash 6.2] NAND 初始化常用命令:复位 (Reset) 和 Read ID 和 Read UID 操作和代码实现
  • [NOIP2004] 提高组 洛谷P1090 合并果子
  • [pytorch] 2. tensorboard