Golang学习表达式
一、保留字
Go语言规定了特定的字符序列,它们被称为关键字,又叫作保留字。我们不能把关键字作为标识符使用。
二、运算符
运算符是一个符号,通知编译器执行数学或逻辑操作。Go语言有丰富的内置运算符,可分为以下几类:
分类 | 运算符 | 说明 |
---|---|---|
算数运算符 | +,-,*,,%,++,- - | 基本与C相同,注意++和- -只是语句并不是表达式,不能把它直接赋值其他变量,b:=a++是错误的 |
关系运算符 | ==,!=,>,<,>=,<= | 用法基本与C相同 |
逻辑运算符 | &&,!,ll | 用法基本与C相同 |
按位运算符 | &,l,^,<<,>> | 用法基本与C相同 |
赋值运算符 | =,+=,-=,*=,/=,%=,<<=,>>=,&=,^=,l= | 用法基本与C相同 |
其它运算符 | *,& | *指向变量的指针,&返回变量的地址 |
三、初始化
初始化复合对象,类型标签,且左括号必须在类型的尾部。
// var a struct { x int } = { 100 } // syntax error
// var b []int = { 1, 2, 3 } // syntax error
// c := struct {x int; y string} // syntax error: unexpected semicolon or newline
// {
// }
var a = struct{ x int }{100}
var b = []int{1, 2, 3}
初始化值以“,”分割。可以分割为多行,但是最后一行必须以“,”或“}”结尾。
a := []int{
1,
2 // Error: need trailing comma before newline in composite literal
}
a := []int{
1,
2, // ok
}
b := []int{
1,
2 } // ok
四、控制流
1. IF
- 可以省略条件表达式的括号。
- 支持初始化语句,可以定义代码块局部变量。
- 代码块左大括号必须在条件表达式尾部。
x := 0
// if x > 10 // Error: missing condition in if statement
// {
// }
if n := "abc"; x > 0 { // 初始化语句未必就是定义变量,⽐比如 println("init") 也是可以的。
println(n[2])
} else if x < 0 { // 注意 else if 和 else 左⼤大括号位置。
println(n[1])
} else {
println(n[0])
}
- 不支持三元操作符 如: a > b ? a : b
2. For
支持三种循环方式,包括类 while 语法的用法。
s := "abc"
for i, n := 0, len(s); i < n; i++ { // 常⻅见的 for 循环,⽀支持初始化语句。
println(s[i])
}
n := len(s)
for n > 0 { // 替代 while (n > 0) {}
println(s[n]) // 替代 for (; n > 0;) {}
n--
}
for { // 替代 while (true) {}
println(s) // 替代 for (;;) {}
}
输出结果
call length.
0 97
1 98
2 99
3 100
3. Range
- 类似迭代器的操作,返回(索引,值)或(键,值)。
1st value | 2nd value | ||
---|---|---|---|
string | index | s[index] | unicode.rune |
array/slice | index | s[index] | |
map | key | m[key] | |
channel | element |
- 可忽略不想要的返回值,或用”_”这样的特殊变量。
s := "abc"
for i := range s { // 忽略 2nd value,⽀支持 string/array/slice/map。
println(s[i])
}
for _, c := range s { // 忽略 index。
println(c)
}
for range s { // 忽略全部返回值,仅迭代。
...
}
m := map[string]int{"a": 1, "b": 2}
for k, v := range m { // 返回 (key, value)。
println(k, v)
}
- 注意,range会复制对象。
a := [3]int{0, 1, 2}
for i, v := range a { // index、value 都是从复制品中取出。
if i == 0 { // 在修改前,我们先修改原数组。
a[1], a[2] = 999, 999
fmt.Println(a) // 确认修改有效,输出 [0, 999, 999]。
}
a[i] = v + 100 // 使⽤用复制品中取出的 value 修改原数组。
}
fmt.Println(a) // 输出 [100, 101, 102]。
- 使用引用类型,其底层数据不会被复制。
s := []int{1, 2, 3, 4, 5}
for i, v := range s { // 复制 struct slice { pointer, len, cap }。
if i == 0 {
s = s[:3] // 对 slice 的修改,不会影响 range。
s[2] = 100 // 对底层数据的修改。
}
println(i, v)
}
输出
0 1
1 2
2 100
3 4
4 5
- 另外两种引用类型 map、channel是指针包装,slice是struct。
4. Switch
- 分支表达式可以是任意类型,不限于常量。可以省略break,默认自动停止。
x := []int{1, 2, 3}
i := 2
switch i {
case x[1]:
println("a")
case 1, 3:
println("b")
default:
println("c")
}
输出:
a //注意,case 1,3是或
- 要继续下一分支,可以使用fallthrough,但不再判断条件。
x := []int{1, 2, 3}
i := 2
switch i {
case x[1]:
fmt.Println("a")
fallthrough
case 2, 3:
fmt.Println("b")
default:
fmt.Println("c")
}
输出结果:
a
b
- 省略条件表达式,可当if…else if…else使用
switch {
case x[1] > 0:
println("a")
case x[1] < 0:
println("b")
default:
println("c")
}
switch i := x[2]; { // 带初始化语句
case i > 0:
println("a")
case i < 0:
println("b")
default:
println("c")
}
5. Goto,Break,Continue
- 支持在函数内
goto
跳转。区分大小写,未使用标签引发错误。
func main() {
var i int
for {
println(i)
i++
if i > 2 { goto BREAK }
}
BREAK:
println("break")
EXIT: // Error: label EXIT defined and not used
}
- 配合
标签
,break
和continue
可在多级嵌套循环中跳出。
func main() {
L1:
for x := 0; x < 3; x++ {
L2:
for y := 0; y < 5; y++ {
if y > 2 {
continue L2
}
if x > 1 {
break L1
}
print(x, ":", y, " ")
}
println()
}
}
注意:break可以for、switch、select,而continue仅能用于for循环。
func main() {
x := 100
switch {
case x >= 0:
if x == 0 {
break
}
println(x)
}
}
五、补充:select
select是Go中的一个控制结构,类似于用于通信的switch语句。每个case必须是一个通信操作,要么是发送要么是接收。select随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。一个默认的子句应该总是可运行的。
select {
case communication clause :
statement(s);
case communication clause :
statement(s);
/* 你可以定义任意数量的 case */
default : /* 可选 */
statement(s);
}
以下描述了 select 语句的语法:
- 每个case都必须是一个通信
- 所有channel表达式都会被求值
- 所有被发送的表达式都会被求值
- 如果任意某个通信可以进行,它就执行;其他被忽略。
- 如果有多个case都可以运行,Select会随机公平地选出一个执行。其他不会执行。
否则:
- 如果有default子句,则执行该语句。
- 如果没有default字句,select将阻塞,直到某个通信可以运行
- Go不会重新对channel或值进行求值。
示例
package main
import "fmt"
func main() {
var c1, c2, c3 chan int
var i1, i2 int
select {
case i1 = <-c1:
fmt.Printf("received ", i1, " from c1\n")
case c2 <- i2:
fmt.Printf("sent ", i2, " to c2\n")
case i3, ok := (<-c3): // same as: i3, ok := <-c3
if ok {
fmt.Printf("received ", i3, " from c3\n")
} else {
fmt.Printf("c3 is closed\n")
}
default:
fmt.Printf("no communication\n")
}
}
输出结果:
no communication
六、总结
本部分简要介绍了保留字、运算符、表达式的初始化和控制流,最后补充了select控制流。熟悉这些用法,注意与C语言语法差异之处。