上一篇咱們瞭解了golang 的變量、函數和基本類型,這一篇將介紹一下控制流
如今咱們看一個複雜點的例子:html
package main import "fmt" func main() { result := 0 for i := 0; i <= 10; i++ { result = fibonacci(i) fmt.Printf("fibonacci(%d) is: %d\n", i, result) } } func fibonacci(n int) (res int) { if n <= 1 { res = 1 } else { res = fibonacci(n-1) + fibonacci(n-2) } return } // outputs fibonacci(0) is: 1 fibonacci(1) is: 1 fibonacci(2) is: 2 fibonacci(3) is: 3 fibonacci(4) is: 5 fibonacci(5) is: 8 fibonacci(6) is: 13 fibonacci(7) is: 21 fibonacci(8) is: 34 fibonacci(9) is: 55 fibonacci(10) is: 89
Go 只有一種循環接口-- for 循環
linux
For 支持三種循環方式,包括類 while 語法git
s := "abc" for i, n := 0, len(s); i < n; i++ { // i, n 爲定義的變量 只在for 循環內做用 println(s[i]) }
基本的 for 循環包含三個由分號分開的組成部分:github
C 的 while 在 Go 中叫作 forgolang
n := len(s) // 循環初始化語句和後置語句都是可選的。 for n > 0 { // 等同於 for (; n > 0;) {} println(s[n]) n-- }
for { // while true println(s) }
就像 for 循環同樣,Go 的 if 語句也不要求用 ( ) 將條件括起來,同時, { } 仍是必須有的
x := 0 // if x > 10 // Error: missing condition in if statement(左大括號必須在條件表達式尾部) // { // } if x > 10{ ... }else{ ... } if n := "abc"; x > 0 { // 初始化語句(在這裏是定義變量) println(n[2]) } else if x < 0 { println(n[1]) } else { println(n[0]) // 局部變量 n 有效範圍是 整個 if/else 塊 }
if 語句定義的變量做用域僅在if範圍以內(包含else語句)
不支持三元操做符 "a > b ? a : b"
以上是上段代碼出現的兩個控制流,剩下的控制流還有數據庫
switch 語句用於選擇執行,語法以下:express
switch optionalStatement; optionalExpression{ case expressionList1: block1 ... case expressionListN: blockN default: blockD }
先看一個例子:小程序
package main import ( "fmt" "runtime" ) func main() { fmt.Print("Go runs on ") switch os := runtime.GOOS; os { // 將 os 與 case 條件匹配 case "darwin": fmt.Println("OS X.") case "linux": fmt.Println("Linux.") case "plan9", "openbsd": // 多個條件命中其一便可(OR) fmt.Println("plan9 | openbsd") default: // freebsd, openbsd, // plan9, windows... fmt.Printf("%s.", os) } }
switch i { case 0: // 空分支,只有當 i == 0 時纔會進入分支 至關於 "case 0: break;" case 1: f() // 當 i == 0 時函數不會被調用 }
package main import "fmt" func switch1(n int) { switch { // 這裏用的是沒有條件的switch 語句會直接執行 case n == 0: fmt.Println(0) fallthrough // fallthrough 需放在 case 塊結尾,可用 break 阻止 case n == 1: // 若是匹配到0 這裏會繼續執行 fmt.Println(1) case n == 2: // fallthrough 不會對這裏有做用 fmt.Println(2) default: fmt.Println("default") } } func main() { switch1(0) } # output 0 1
switch i { case 0: case 1: f() default: g() // 當i不等於0 或 1 時調用 }
Range 相似迭代器的操做,返回(索引,值)或(健,值)
它能夠迭代任何一個集合(包括字符串、數組、數組指針、切片、字典、通道)windows
基本語法以下:設計模式
coll := 3string["a", "b", "c"] for ix, val := range coll { ... } // 容許返回單值 for ix := range coll { println(ix, coll[ix]) } // 也可使用 _ 忽略 for _, val := range coll { println(val) } // 也能夠只迭代,不返回。可用來執行清空 channel 等操做 for range coll { ... }
val 始終爲集合中對應索引的值拷貝,所以它通常只具備只讀性質,對它所作的任何修改都不會影響到集合中原有的值(譯者注:若是 val 爲指針,則會產生指針的拷貝,依舊能夠修改集合中的原值
一個字符串是 Unicode 編碼的字符(或稱之爲 rune)集合,所以您也能夠用它迭代字符串
下面是每種數據類型使用range時 ix和val 的值
date type | ix | value | 值類型 |
---|---|---|---|
string | index | s[index] | unicode, rune |
array/slice | index | s[index] | |
map | key | m[index] | |
channel | element |
range 會複製目標數據。字符串、切片基本結構是個很小的結構體,而字典、通道自己是指針封裝,複製成本很小,無需專門優化。若是是數組,可改爲數組指針或者切片類型。
break 和 continue 均可在多級嵌套循環中跳出
break 可用於 for、switch、select語句,終止整個語句塊執行continue 僅能 於 for 循環,終止後續操做,當即進入下一輪循環。
goto 語句能夠配合標籤(label)形式的標識符使用,即某一行第一個以冒號:
結尾的單詞,標籤區分大小寫。
package main func main() { i:=0 HERE: print(i) i++ if i==5 { return } goto HERE } # output 01234
使用標籤和 goto 語句是不被鼓勵的:它們會很快致使很是糟糕的程序設計,並且總有更加可讀的替代方案來實現相同的需求。
for、switch 或 select 語句均可以配合標籤(label)形式的標識符使用
package main import "fmt" func main() { LABEL1: for i := 0; i <= 5; i++ { for j := 0; j <= 5; j++ { if j == 4 { continue LABEL1 } fmt.Printf("i is: %d, and j is: %d\n", i, j) } } }
continue 語句指向 LABEL1,當執行到該語句的時候,就會跳轉到 LABEL1 標籤的位置
defer 語句會延遲函數的執行直到上層函數返回
延遲調用的參數會馬上生成,可是在上層函數返回前函數都不會被調用
package main import "fmt" func main() { defer fmt.Println("world") fmt.Println("hello") } // output hello world
defer 棧
延遲的函數調用被壓入一個棧中。當函數返回時, 會按照後進先出的順序調用被延遲的函數調用。
defer 經常使用來定義簡單的方法
package main import "fmt" func main() { fmt.Println("counting") for i := 0; i < 10; i++ { defer fmt.Println(i) } fmt.Println("done") } // 能夠想一下會輸出什麼 // 代碼執行 https://tour.go-zh.org/flowcontrol/13
關鍵字 defer 容許咱們進行一些函數執行完成後的收尾工做,例如:
// open a file defer file.Close()
mu.Lock() defer mu.Unlock()
printHeader() defer printFooter()
// open a database connection defer disconnectFromDB()
合理使用 defer 語句可以使得代碼更加簡潔。
下面的代碼展現了在調試時使用 defer 語句的手法
package main import ( "io" "log" ) func func1(s string) (n int, err error) { defer func() { log.Printf("func1(%q) = %d, %v", s, n, err) }() return 7, io.EOF } func main() { func1("Go") } // 輸出 Output: 2016/04/25 10:46:11 func1("Go") = 7, EOF
更多defer 的用法(https://blog.go-zh.org/defer-panic-and-recover)
Go 指南
The way to go -- 控制結構
Effective Go
到這裏簡單的控制流用法講解就結束了
下節將會是golang 數據結構部分, 會用到的代碼爲
package main import ( "fmt" "time" ) const LIM = 41 var fibs [LIM]uint64 func main() { var result uint64 = 0 start := time.Now() for i := 0; i < LIM; i++ { result = fibonacci(i) fmt.Printf("fibonacci(%d) is: %d\n", i, result) } end := time.Now() delta := end.Sub(start) fmt.Printf("longCalculation took this amount of time: %s\n", delta) } func fibonacci(n int) (res uint64) { // memoization: check if fibonacci(n) is already known in array: if fibs[n] != 0 { res = fibs[n] return } if n <= 1 { res = 1 } else { res = fibonacci(n-1) + fibonacci(n-2) } fibs[n] = res return }
最後,感謝女友支持和包容,比❤️
想了解如下內容能夠在公號輸入相應關鍵字獲取歷史文章: 公號&小程序
| 設計模式
| 併發&協程