Go 函数

5.1 函数声明

|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 函数声明 基本语法 | 每个函数声明,都包含了一个函数名 、一个形参列表 、一个可选的返回列表 、一个函数体 |
| 函数声明 基本语法 | func name(parameter-list) (return-list) { body } |
| ||
| 形参与返回值 | 形参列表,指定了一组变量的参数名和参数类型 |
| 形参与返回值 | 形参是函数的局部变量,这些形参都由调用者提供的实参传递而来 |
| 形参与返回值 | 返回列表,指定了函数返回值的类型 |
| 形参与返回值 | 当函数返回一个未命名的返回值,或者函数没有返回值的时候,返回列表的圆括号可以省略 |
| 形参与返回值 | 如果一个函数既没有返回列表,也没有任何返回值,那么这个函数的目的是调用函数所带来的附加效果 |
| 代码示例 | 在下面的 hypot 函数中: func name( x , y float64 ) float64 { return math.Sqrt( x*x + y*y ) } fmt.Println( hypot( 3 , 4 ) ) // " 5 " x 和 y 是函数声明中的形参,3 和 4 是调用函数时的实参,并且函数返回一个类型为 float64 的值 |
| ||
| 形参与返回值 | 返回值可以像形参一样命名; 每个命名的返回值会声明为一个局部变量,并根据变量类型初始化为对应的零值 |
| 形参与返回值 | 当函数存在返回列表时,必须显式地以 return 语句结束; 除非函数明确不会走完整个执行流程,比如在函数中抛出宕机异常或函数体内存在一个没有 break 退出条件的无限 for 循环 |
| 形参与返回值 | 如果几个形参或者返回值的类型相同,那么类型标识符只需要写一次 |
| 代码示例 | 以下两个声明是完全相同的: func f(i , j , k int , s , t string) { /* ... */ } func f(i int , j int , k int , s string , t string) { /* ... */ } |
| ||
| 匿名变量 | 空白标识符 " _ " 用来强调这个形参在函数中未被使用 |
| 代码示例 | 下面使用 4 种方式声明一个带有两个形参和一个返回值的函数,所有变量都是 int 类型 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" |
| ||
| 函数签名 (类型) | 函数的类型 称作 "函数签名" |
| 函数签名 (类型) | 当两个函数拥有相同的形参列表以及返回列表时,认为这两个函数的类型(或签名)是相同的 |
| 函数签名 (类型) | 形参和返回值的名字不会影响函数类型,采用简写同样不会影响函数类型 形参列表(返回列表)是否相同,看类型是否一样,类型的个数,类型的位置 |
| ||
| 实参 初始化 形参 | 每一次调用函数,都需要提供实参来初始化对应的形参,包括参数的调用次序也必须一致 |
| 实参 初始化 形参 | Go 语言没有默认参数值的概念,也不能指定形参名 所以除了用于文档说明之外,形参和返回值的命名不会影响调用者 |
| ||
| 形参与返回值 局部变量 | 形参变量是函数的局部变量,形参的初始值由调用者提供的实参传递 |
| 形参与返回值 局部变量 | 函数形参以及命名的返回值,同属于函数最外层作用域的局部变量 |
| ||
| 按值传递 | 实参是 "按值传递" 的,所以函数接收到的是每个实参的副本(拷贝) |
| 按值传递 | 修改函数的形参变量,不会影响调用者提供的实参(的值) |
| 按值传递 | 然而,如果提供的实参包含引用类型,比如 指针 、slice 、map 、函数 、通道,那么当函数使用形参变量时,就有可能间接地修改实参变量 |
| ||
| | 有些函数的声明没有函数体,说明这个函数使用了除 Go 以外的语言实现 |
| | 这样的声明定义了该函数的签名 package math func Sin(x float64) float64 // 使用汇编语言实现 |

5.2 递归

|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 递归的含义 | 函数可以递归调用,这意味着函数可以直接或间接地调用自己 |
| 递归的含义 | 递归,是一种实用的技术,可以处理许多带有递归特性的数据结构 |
| ||
| | 下面使用递归处理 HTML 文件 |
| | 下面的代码示例使用了一个非标准的包 golang.org/x/net/html ,它提供了解析 HTML 的功能; golang.org/x/... 下的仓库(比如网络 、国际化语言处理 、移动平台 、图片处理 、加密功能以及开发者工具)都由 Go 团队负责设计和维护;这些包并不属于标准库,原因是它们还在开发当中,或者很少被 Go 程序员使用 |
| | 我们需要的 golang.org/x/net/html API 如下面的代码所示; 函数 html.Parse 读入一段字节序列,解析它们,然后返回 HTML 文档树的根节点 html.Node ;HTML 有多种节点,比如文本 、注释等; 此处,我们只关心表单的 "元素节点 <name key='value'> " |
| ||
| 类型声明 | ++golang.org/x/net/html++ package html type Node struct { Type NodeType Data string Attr []Attribute FirstChild , NextSibling *Node } type NodeType int32 const ( ErrorNode NodeType = iota TextNode DocumentNode ElementNode CommentNode DoctypeNode ) type Attribute struct { Key , Val string } func Parse( r io.Reader ) ( *Node , error ) |
| ||
| main 函数 | 主函数从标准输入中读入 HTML ,使用递归的 visit 函数获取 HTML 文本的超链接,并且把所有的超链接输出 |
| main 函数 | ++gop1.io/ch5/findlinks1++ package main import ( "fmt" "os" "golang.org/x/net/html" ) func main() { doc , err := html.Parse(os.Stdin) // 标准输入进入 Parse 函数进行解析 if err != nil { // 判断解析函数执行是否出错 fmt.Fprintf(os.Stderr , "findlinks1 :%v\n" , err) os.Exit(1) } for _ , link := range visit(nil , doc) { // 获取 HTML 文本的超链接 fmt.Println(link) // 输出超链接 } } |
| ||
| visit 函数 | visit 函数遍历 HTML 树上的所有节点,从 HTML 锚元素 <a href='...'> 中得到 href 属性的内容,将获取到的链接内容添加到字符串 slice ,最后返回这个 slice |
| visit 函数 | // visit 函数会将 n 节点中的每个链接添加到结果中 func visit(links []string , n *html.Node) []string { if n.Type == html.ElementNode && n.Data == "a" { for _ , a := range n.Attr { if a.Key == "href" { links = append(links , a.Val) } } } for c := n.FirstChild ; c != nil ; c = c.NextSibling { links = visit(links , c) } return links } |
| visit 函数 | 要对树中的任意节点 n 进行递归,visit 递归地调用自己去访问节点 n 的所有子节点,并且将访问过的节点保存在 FirstChild 链表中 |
| | |
| | 下面的程序使用递归遍历所有 HTML 文本中的结点树,并输出树的结构;当递归遇到每个元素时,会将元素标签压入栈,然后输出栈 |
| | func main() { doc,err := html.Parse(os.Stdin) if err != nil { fmt.Fprintf(os.Stderr,"outline:%v\n",err) os.Exit(1) // 解析出错则退出程序 } outline(nil,doc) } func outline(stack []string,n *html.Node) { if n.Type == html.ElementNode { stack = append(stack,n.Data) // 把标签压入栈 fmt.Println(stack) } for c := n.FirstChild;c != nil;c = c.NextSibling { outline(stack,c) } } |
| | 尽管 outline 会将元素压栈但不会出栈; 当 outline 递归调用自己时,被调用的函数会接收到栈的副本,所以 outline 无法修改调用者的栈 |
| | |
| | |
| 函数调用栈 | 许多变成语言使用固定长度的函数调用栈;大小在 64KB 到 2MB 之间; 递归的深度会受限于固定长度的栈大小,所以当进行深度递归调用时必须严防 "栈溢出" ;固定长度的栈甚至会造成一定的安全隐患 相比固定长的栈 ,Go 语言的实现使用了可变长度的栈,栈大小随使用而增长,可达 1GB 左右的上限;安全使用栈,不必担心溢出问题 |

5.3 多返回值

|------------|----------------------------------------------------------|
| 多返回值概念 | 一个函数能够返回不止一个结果 |
| 多返回值概念 | 标准包内的许多函数返回两个值:一个期望得到的计算结果、一个错误值(或者一个表示函数是否调用正确的布尔值) |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |

5.4 错误

5.5 函数变量

5.6 匿名函数

5.7 变长函数

5.8 延迟函数调用

5.9 宕机

5.10 恢复

相关推荐
虽千万人 吾往矣26 分钟前
golang gorm
开发语言·数据库·后端·tcp/ip·golang
__AtYou__16 小时前
Golang | Leetcode Golang题解之第448题找到所有数组中消失的数字
leetcode·golang·题解
千年死缓18 小时前
go+redis基于tcp实现聊天室
redis·tcp/ip·golang
吃着火锅x唱着歌1 天前
Redis设计与实现 学习笔记 第五章 跳跃表
golang
技术卷1 天前
Redis数据库与GO完结篇:redis操作总结与GO使用redis
数据库·redis·golang
white.tie1 天前
vscode配置golang
ide·vscode·golang
陈序缘1 天前
Go语言实现长连接并发框架 - 任务管理器
linux·服务器·开发语言·后端·golang
0x派大星1 天前
【Golang】语法基础——切片:灵活、高效的数据处理利器
golang
技术卷2 天前
GO网络编程(二):客户端与服务端通信【重要】
golang·网络编程
小帅吖2 天前
浅析Golang的Context
开发语言·后端·golang