forked from ffhelicopter/Go42
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path42_14_flow.md
295 lines (224 loc) · 8.11 KB
/
42_14_flow.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# 《Go语言四十二章经》第十四章 流程控制
作者:李骁
## 14.1 switch 语句
```Go
switch var1 {
case val1:
...
case val2:
...
default:
...
}
```
```Go
switch {
case condition1:
...
case condition2:
...
default:
...
}
```
switch 语句的第二种形式是不提供任何被判断的值(实际上默认为判断是否为 true),然后在每个 case 分支中进行测试不同的条件。当任一分支的测试结果为 true 时,该分支的代码会被执行。
switch 语句的第三种形式是包含一个初始化语句:
```Go
switch initialization {
case val1:
...
case val2:
...
default:
...
}
```
```Go
switch result := calculate(); {
case result < 0:
...
case result > 0:
...
default:
// 0
}
```
变量 var1 可以是任何类型,而 val1 和 val2 则可以是同类型的任意值。类型不被局限于常量或整数,但必须是相同的类型;或者最终结果为相同类型的表达式。前花括号 { 必须和 switch 关键字在同一行。
您可以同时测试多个可能符合条件的值,使用逗号分割它们,例如:case val1,val2,val3。
一旦成功地匹配到某个分支,在执行完相应代码后就会退出整个 switch 代码块,也就是说您不需要特别使用 break 语句来表示结束。
如果在执行完每个分支的代码后,还希望继续执行后续分支的代码,可以使用 fallthrough 关键字来达到目的。
fallthrough强制执行后面的case代码,fallthrough不会判断下一条case的expr结果是否为true。
```Go
package main
import "fmt"
func main() {
switch a := 1; {
case a == 1:
fmt.Println("The integer was == 1")
fallthrough
case a == 2:
fmt.Println("The integer was == 2")
case a == 3:
fmt.Println("The integer was == 3")
fallthrough
case a == 4:
fmt.Println("The integer was == 4")
case a == 5:
fmt.Println("The integer was == 5")
fallthrough
default:
fmt.Println("default case")
}
}
```
```Go
程序输出:
The integer was == 1
The integer was == 2
```
## 14.2 select控制
select是Go语言中的一个控制结构,类似于switch语句,主要用于处理异步通道操作,所有情况都会涉及通信操作。因此select会监听分支语句中通道的读写操作,当分支中的通道读写操作为非阻塞状态(即能读写)时,将会触发相应的动作。select语句会选择一组可以发送或接收操作中的一个分支继续执行。select没有条件表达式,一直在等待分支进入可运行状态。
>select中的case语句必须是一个channel操作
>
>select中的default子句总是可运行的。
* 如果有多个分支都可以运行,select会伪随机公平地选出一个执行,其他分支不会执行。
* 如果没有可运行的分支,且有default语句,那么就会执行default的动作。
* 如果没有可运行的分支,且没有default语句,select将阻塞,直到某个分支可以运行。
```Go
package main
import (
"fmt"
"time"
)
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):
if ok {
fmt.Printf("received ", i3, " from c3\n")
} else {
fmt.Printf("c3 is closed\n")
}
case <-time.After(time.Second * 3): //超时退出
fmt.Println("request time out")
}
}
// 输出:request time out
```
## 14.3 for循环
最简单的基于计数器的迭代,基本形式为:
```Go
for 初始化语句; 条件语句; 修饰语句 {}
```
这三部分组成的循环的头部,它们之间使用分号 ; 相隔,但并不需要括号 () 将它们括起来。
您还可以在循环中同时使用多个计数器:
```Go
for i, j := 0, N; i < j; i, j = i+1, j-1 {}
```
这得益于 Go 语言具有的平行赋值的特性,for 结构的第二种形式是没有头部的条件判断迭代(类似其它语言中的 while 循环),基本形式为:for 条件语句 {}。
您也可以认为这是没有初始化语句和修饰语句的 for 结构,因此 ;; 便是多余的了
条件语句是可以被省略的,如 i:=0; ; i++ 或 for { } 或 for ;; { }(;; 会在使用 Gofmt 时被移除):这些循环的本质就是无限循环。
最后一个形式也可以被改写为 for true { },但一般情况下都会直接写 for { }。
如果 for 循环的头部没有条件语句,那么就会认为条件永远为 true,因此循环体内必须有相关的条件判断以确保会在某个时刻退出循环。
```Go
package main
import (
"fmt"
)
func main() {
a := []int{1, 2, 3, 4, 5, 6}
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
a[i], a[j] = a[j], a[i]
}
for j := 0; j < 5; j++ {
for i := 0; i < 10; i++ {
if i > 5 {
break
}
fmt.Println(i)
}
}
}
```
## 14.4 for-range 结构
for-range 结构是 Go 语言特有的一种迭代结构,它在许多情况下都非常有用。它可以迭代任何一个集合,包括数组(array)和字典(map),同时可以获得每次迭代所对应的索引和值。一般形式为:
```Go
for ix, val := range coll { }
```
要注意的是,val 始终为集合中对应索引的值的副本,因此它一般只具有只读性质,对它所做的任何修改都不会影响到集合中原有的值(注:如果 val 为指针,则会产生指针的副本,依旧可以修改集合中的原值)。
```Go
package main
import (
"fmt"
"time"
)
type field struct {
name string
}
func (p *field) print() {
fmt.Println(p.name)
}
func main() {
data := []field{ {"one"}, {"two"}, {"three"} }
for _, v := range data {
go v.print()
}
time.Sleep(3 * time.Second)
// goroutines (可能)显示: three, three, three
}
```
当前的迭代变量作为匿名goroutine的参数。
```Go
package main
import (
"fmt"
"time"
)
func main() {
data := []string{"one", "two", "three"}
for _, v := range data {
go func(in string) {
fmt.Println(in)
}(v)
}
time.Sleep(3 * time.Second)
// goroutines输出: one, two, three
}
```
一个字符串是 Unicode 编码的字符(或称之为 rune)集合,因此您也可以用它迭代字符串:
```Go
for pos, char := range str {
...
}
```
**if**
If语句由布尔表达式后紧跟一个或多个语句组成,注意布尔表达式不用()
```Go
if 布尔表达式 {
/* 在布尔表达式为 true 时执行 */
}
```
**break**
一个 break 的作用范围为该语句出现后的最内部的结构,它可以被用于任何形式的 for 循环(计数器、条件判断等)。
但在 switch 或 select 语句中,break 语句的作用结果是跳过整个代码块,执行后续的代码。
**continue**
关键字 continue 忽略剩余的循环体而直接进入下一次循环的过程,但不是无条件执行下一次循环,执行之前依旧需要满足循环的判断条件。
关键字 continue 只能被用于 for 循环中。
**label**
for、switch 或 select 语句都可以配合标签(label)形式的标识符使用,即某一行第一个以冒号(:)结尾的单词(Gofmt 会将后续代码自动移至下一行)
(标签的名称是大小写敏感的,为了提升可读性,一般建议使用全部大写字母)
continue 语句指向 LABEL1,当执行到该语句的时候,就会跳转到 LABEL1 标签的位置。
使用标签和 Goto 语句是不被鼓励的:它们会很快导致非常糟糕的程序设计,而且总有更加可读的替代方案来实现相同的需求。
[目录](https://github.com/ffhelicopter/Go42/blob/master/SUMMARY.md)
[第十三章 字典(Map)](https://github.com/ffhelicopter/Go42/blob/master/content/42_13_map.md)
[第十五章 错误处理](https://github.com/ffhelicopter/Go42/blob/master/content/42_15_errors.md)
>本书《Go语言四十二章经》内容在github上同步地址:https://github.com/ffhelicopter/Go42
>
>
>虽然本书中例子都经过实际运行,但难免出现错误和不足之处,烦请您指出;如有建议也欢迎交流。
>联系邮箱:[email protected]