1.5 表达式、语句、内置函数 #
一、表达式 #
An expression specifies the computation of a value by applying operators and functions to operands.
一个表达式通过将操作符和函数应用于操作数,来指定值的计算。
表达式列表 #
表达式 | 释义 | 示例 |
---|---|---|
操作数 | 表示表达式中的基本值 | |
限定标识符 | 是一个带有包名前缀的标识符 | math.Sin |
复合字面量 | 由字面量的类型后跟一个花括号括起的元素列表组成 | line := Line{origin, Point3D{y: -4, z: 12.3}} |
函数字面量 | 表示一个匿名函数。可以赋值给变量或直接调用 函数字面量是闭包:它们可以引用在周围函数中定义的变量。这些变量然后在周围函数和函数字面量之间共享,只要它们仍然可访问,就会持续存在。 | func(a, b int, z float64) bool { return a*b < int(z) }<br/>f := func(x, y int) int { return x + y }<br/>func(ch chan int) { ch <- ACK }(replyChan) |
基本表达式 | 一元表达式和二元表达式的操作数 | x<br/>2<br/>(s + ".txt")<br/>f(3.1415, true)<br/>Point{1, 2}<br/>m["foo"]<br/>s[i : j + 1]<br/>obj.color<br/>f.p[i].x() |
选择器 | 表示值x (或有时是 *x ;见下文的字段或方法f | x.f |
方法表达式 | 类似函数 M 在类型 T 的方法集中, T.M 是一个函数,它可以用与 M 相同的参数(在前面加上一个接收者参数)作为常规函数来调用 | T.Mv |
方法值 | 类似函数值 | T.Mv |
索引表达式 | 表示标识数组、指向数组的指针、切片、字符串或映射的第 x 个元素 | a[x] |
切片表达式 | 索引 low 和 high 选择操作数 a 中的哪些元素出现在结果中,设置为 max - low 控制结果的切片容量 | a[low : high] a[low : high : max] |
类型断言 | 断言 x 不是 nil ,并且存储在 x 中的值是类型 | x.(T) |
调用 | f(a1, a2, … an) | |
将参数传递给可变参数 | 如果 f 是可变参数,且最后一个参数 p 的类型为 ...T ,那么在 f 中 p 的类型等效于 []T 类型 | Greeting("hello:", "Joe", "Anna", "Eileen") |
实例化 | 一个泛型函数或类型通过用类型参数替换类型参数来实例化 | |
类型推断 | 如果泛型函数的使用上下文可以推断出某些或所有类型参数,包括函数类型参数的约束,则可以使用泛型函数时省略一些或所有类型参数。 | |
类型统一 | 类型推断通过类型统一解决类型方程 | |
运算符 | 运算符将操作数组合成表达式 | `binary_op = " |
运算符优先级 | ||
类型转换 | 一个类型转换将表达式的类型转换为转换指定的类型。 | (*Point)(p) |
常量表达式 | const a = 2 + 3.0 | |
求值顺序 | 在包级别,初始化依赖关系决定了变量声明中各个初始化表达式的求值顺序。 否则,在求值表达式的操作数、赋值语句或返回语句时,所有函数调用、方法调用、接收操作和二元逻辑运算都按词法从左到右的顺序进行求值。 |
更新详情,见官方文档 https://tip.golang.org/ref/spec#Expressions
二、语句 #
语句列表 #
语句 | 释义 | 示例 |
---|---|---|
终止语句 | 中断代码块中的常规控制流 | return"或"goto"语句 panic 一个以终止语句结束的代码块 一个"if"语句,其中:包含"else"分支,并且两个分支都是终止语句。 一个"for"语句,其中:没有引用该"for"语句的"break"语句,并且循环条件不存在,并且该"for"语句不使用范围子句。 一个"switch"语句,其中:没有任何"break"语句引用该"switch"语句,有一个默认情况,并且语句中每个情况(包括默认情况)都以终止语句或一个可能带标签的"fallthrough"语句结束。 一个"select"语句,其中:没有任何"break"语句引用该"select"语句,并且每个情况的语句列表,包括默认情况(如果存在),都以终止语句结束。 一个标记语句,标记一个终止语句 |
空语句 | 什么也不做 | EmptyStmt = . |
标记语句 | 可以是 goto 、 break 或 continue 语句的目标 | Error: log.Panic("error encountered") |
表达式语句 | h(x+y) | |
发送语句 | 在通道上发送一个值 | ch <- 3 |
自增自减语句 | “++” 和 “–” 语句通过无类型常量 1 来递增或递减它们的操作数。与赋值一样,操作数必须是可寻址的或是一个映射索引表达式。 | x++ x-- |
赋值语句 | 用表达式指定的值替换变量中当前存储的值。 每个左操作数必须是可寻址的、映射索引表达式,或者(仅对 = 赋值而言)空白标识符。操作数可以加括号。 | x = 1<br/>*p = f()<br/>a[i] = 23<br/>(k) = <-ch // same as: k = <-ch |
if 语句 | 根据布尔表达式的值指定两个分支的 条件执行 | if x > max {<br/> x = max<br/>} |
switch 语句 | 提供多分支执行,存在两种形式:表达式 switch 和类型 switch。 | |
select 语句 | 选择一组可能的发送或接收操作中哪一个将进行。类似switch,但所有的情况都指的是通信操作 | select {<br/>case i1 = <-c1: print("received ", i1, " from c1\n") default: print("no communication\n")<br/>} |
for 语句 | 三种循环:迭代可以由单个条件、“for"子句或"range"子句控制 | |
go 语句 | 在同一地址空间内作为独立的并发控制线程(或 goroutine)启动函数调用的执行。 表达式必须是函数或方法调用;不能加括号。 | go Server()<br/>go func(ch chan<- bool) { for { sleep(10); ch <- true }} (c) |
return 语句 | 终止函数 F 的执行,并可选地提供一个或多个结果值。由 F 延迟的任何函数在 F 返回到调用者之前执行 | |
break 语句 | 终止在同一个函数内最内层的 “for”、“switch” 或 “select” 语句的执行 | |
continue 语句 | 将控制权移至循环块的末尾来开始最内层包含的"for"循环的下一个迭 | |
goto 语句 | 将控制权转移到同一函数内对应标签的语句 | goto Error |
fallthrough 语句 | 将控制权转移到表达式 “switch” 语句中下一个 case 子句的第一条语句 Go设计成case子句默认break(区别于C/C++/Java),无需手动写break。同时在需要时支持显式 fallthrough | |
defer 语句 | 会调用一个函数,该函数的执行被推迟到外围函数返回的时。 无论是由于外围函数执行了 return 语句、到达了函数体的末尾,还是因为相应的 goroutine 正在panic |
if 条件分支 #
if
、switch
,像 for
一样可接受可选的初始化语句设置局部变量;
|
|
语法上其主体强制大括号促使语句分成多行而更简洁清晰,没有圆括号;
|
|
**Go采用排错式风格(如err、nil未处理,导致线上运行时故障),若控制流成功继续,则说明程序已排除错误,正常直行的语句块不会被if else缩进的支离破碎。**由于出错时将以return
、break
、continue
、goto
结束, 之后的代码也就无需else
了。
由于两个err在同一个词法域,且d为声明,Go在短变量声明语法上出于实用性设计,后一个err声明为重新赋值,而不必使用err1、err2、err3…
|
|
Go只有一个更通用的 for
循环,不再使用 do
或 while
循环;
select
包含类型选择和多路通信复用器的新控制结构;
Go中的函数形参和返回值的作用域,和在词法上处于大括号内的函数体一致;
switch、select多分支执行 #
select 分支专用于处理chan
- switch: 用于控制流,基于值的条件判断
- select: 用于并发控制,基于通道的通信
特性 switch select 用途 条件分支 通道操作 执行 顺序执行 并发等待 阻塞 不阻塞 可阻塞 超时 不支持 支持 性能 O(n) 按顺序检查每个 case O(1) 不会遍历所有 case,而是随机选择一个就绪的
Go风格将if-elif-else
链写成一个 switch
,其表达式无需为常量或整数,case
语句会自上而下逐一进行求值直到匹配为止,最后一个没有case将匹配为true。
|
|
switch
默认不会自动下溯(case匹配执行后自动跳出switch),case
可通过逗号分隔来列举相同的处理条件。显式下溯使用fallthrough关键字;
|
|
break
语句可以使 switch
打破层层的循环提前终止。在Go中,我们只需将标签放置到循环外,然后 “蹦”到那里即可。continue
语句也能接受一个可选的标签,不过它只能在循环中使用。
|
|
作为这一节的结束,此程序通过使用两个 switch
语句对字节数组进行比较:
|
|
switch
也可用于判断接口变量的动态类型。如 类型选择 通过圆括号中的关键字 type
使用类型断言语法。若 switch
在表达式中声明了一个变量,那么该变量的每个子句中都将有该变量对应的类型。
|
|
for 循环 #
Go的 for
循环类似于C,不再有while
、 do-while
了
|
|
短变量声明能方便的在循环中声明下标变量:
|
|
range
子句遍历数组、切片、字符串或者映射,或从信道中读取消息
|
|
- 若你只需要该遍历中的第一个项(键或下标),去掉第二个就行了:
1 2 3 4 5
for key := range m { if key.expired() { delete(m, key) } }
- 若你需要丢弃第一个项(键或下标)请使用空白标识符,即下划线来丢弃第一个值:
1 2 3 4
sum := 0 for _, value := range array { sum += value }
- 对于字符串,
range
能够提供更多便利。它能通过解析UTF-8, 将每个独立的Unicode码点分离出来。错误的编码将占用一个字节,并以符文U+FFFD来代替。 (名称“符文”和内建类型rune
是Go对单个Unicode码点的成称谓。 详情见 语言规范)。循环1 2 3 4 5 6 7
for pos, char := range "日本\x80語" { // \x80 是个非法的UTF-8编码 fmt.Printf("字符 %#U 始于字节位置 %d\n", char, pos) } 字符 U+65E5 '日' 始于字节位置 0 字符 U+672C '本' 始于字节位置 3 字符 U+FFFD '�' 始于字节位置 6 字符 U+8A9E '語' 始于字节位置 7
Go中没有逗号操作符,++
和 --
为语句而非表达式,所以 for ; ; i++,j++
被拒绝。所以如要在 for
中使用多个变量,应采用平行赋值(i, j = i+1, j–)的方式
|
|
更多详情,见官方文档 https://tip.golang.org/ref/spec#Statements
三、内置函数 #
Built-in functions are predeclared. They are called like any other function but some of them accept a type instead of an expression as the first argument.
内置函数是预先声明的。
The built-in functions do not have standard Go types, so they can only appear in call expressions ; they cannot be used as function values.
内置函数没有标准的 Go 类型,因此它们只能出现在调用表达式中;它们不能作为函数值使用。
内置函数的设计考量 #
- 性能考虑
|
|
- 类型系统简化
|
|
- 编译器优化
|
|
使用限制:
|
|
绕过限制:
|
|
内置函数列表 #
更多详情,见 官方文档 https://tip.golang.org/ref/spec#Built-in_functions
内置函数 | 示例 | 说明 |
---|---|---|
slice操作 | 追加元素:append(slice, elements...) 复制切片: copy(dst, src) | slice容量不足以容纳附加的值: append 将分配一个新的、足够大的底层数组,以容纳现有的切片元素和附加的值。slice容量足够:** **append** 将重用底层数组。**(注意:这可能会写出隐式bug)切片元素从源 src 复制到目标 dst 并返回复制的元素数量 |
长度和容量 | 获取长度:len(slice) 获取容量: cap(slice) | 任何时候都保持以下关系:0 <= len(s) <= cap(s) |
chan操作 | 关闭chan:close(channel) | |
复数操作 | 创建复数:complex(real, imag) 获取实部: real(complex) 获取虚部: imag(complex) | |
map操作 | 删除map元素:delete(map, key) | |
内存分配 | 创建切片、映射、通道:make([]T, length, capacity) 返回T创建指针: new(T) 返回*T | |
删除或清零所有元素 | clear() | 接收一个 map、slice 或类型参数类型的参数 |
min max | min: m := min(x, y) max: c := max(1, 2.0, 10) | |
panic | 触发恐慌:panic(value) 恢复恐慌: recover() |