鍍金池/ 教程/ GO/ 5.4 for 結(jié)構(gòu)
4.7 strings 和 strconv 包
13.6 啟動(dòng)外部命令和程序
?# 11.4 類型判斷:type-switch
12.1 讀取用戶的輸入
10.6 方法
12.2 文件讀寫
13 錯(cuò)誤處理與測試
9.3 鎖和 sync 包
12.3 文件拷貝
?# 11.7 第一個(gè)例子:使用 Sorter 接口排序
?# 11.5 測試一個(gè)值是否實(shí)現(xiàn)了某個(gè)接口
6.4 defer 和追蹤
12.10 XML 數(shù)據(jù)格式
13.10 性能調(diào)試:分析并優(yōu)化 Go 程序
?# 11.1 接口是什么
2.2 Go 環(huán)境變量
2.6 安裝目錄清單
2.5 在 Windows 上安裝 Go
11.11 Printf 和反射
1.2 語言的主要特性與發(fā)展的環(huán)境和影響因素
9.0 包(package)
7.4 切片重組(reslice)
13.2 運(yùn)行時(shí)異常和 panic
10.2 使用工廠方法創(chuàng)建結(jié)構(gòu)體實(shí)例
12.8 使用接口的實(shí)際例子:fmt.Fprintf
2.4 在 Mac OS X 上安裝 Go
3.8 Go 性能說明
7.2 切片
8.0 Map
3.1 Go 開發(fā)環(huán)境的基本要求
5.6 標(biāo)簽與 goto
6.10 使用閉包調(diào)試
9.5 自定義包和可見性
4.3 常量
?# 11.2 接口嵌套接口
6.5 內(nèi)置函數(shù)
前言
10.8 垃圾回收和 SetFinalizer
2.8 Go 解釋器
13.7 Go 中的單元測試和基準(zhǔn)測試
6.8 閉包
4.9 指針
13.1 錯(cuò)誤處理
10.1 結(jié)構(gòu)體定義
5.1 if-else 結(jié)構(gòu)
6.6 遞歸函數(shù)
9.9 通過 Git 打包和安裝
2.7 Go 運(yùn)行時(shí)(runtime)
10.7 類型的 String() 方法和格式化描述符
3.7 其它工具
9.6 為自定義包使用 godoc
11.12 接口與動(dòng)態(tài)類型
13.3 從 panic 中恢復(fù)(Recover)
10.3 使用自定義包中的結(jié)構(gòu)體
11.14 結(jié)構(gòu)體、集合和高階函數(shù)
3.6 生成代碼文檔
9.2 regexp 包
4.1 文件名、關(guān)鍵字與標(biāo)識符
?# 11.6 使用方法集與接口
7.0 數(shù)組與切片
7.1 聲明和初始化
12.11 用 Gob 傳輸數(shù)據(jù)
5.5 Break 與 continue
1.1 起源與發(fā)展
?# 11 接口(Interfaces)與反射(reflection)
6.9 應(yīng)用閉包:將函數(shù)作為返回值
4.2 Go 程序的基本結(jié)構(gòu)和要素
8.6 將 map 的鍵值對調(diào)
6.11 計(jì)算函數(shù)執(zhí)行時(shí)間
5.0 控制結(jié)構(gòu)
10.5 匿名字段和內(nèi)嵌結(jié)構(gòu)體
4.6 字符串
3.0 編輯器、集成開發(fā)環(huán)境與其它工具
13.8 測試的具體例子
7.6 字符串、數(shù)組和切片的應(yīng)用
8.4 map 類型的切片
3.9 與其它語言進(jìn)行交互
7.3 For-range 結(jié)構(gòu)
9.7 使用 go install 安裝自定義包
6.0 函數(shù)
9.8 自定義包的目錄結(jié)構(gòu)、go install 和 go test
6.3 傳遞變長參數(shù)
13.9 用(測試數(shù)據(jù))表驅(qū)動(dòng)測試
11.9 空接口
8.1 聲明、初始化和 make
6.2 函數(shù)參數(shù)與返回值
9.11 在 Go 程序中使用外部庫
3.3 調(diào)試器
4.5 基本類型和運(yùn)算符
?# 11.8 第二個(gè)例子:讀和寫
12.5 用 buffer 讀取文件
總結(jié):Go 中的面向?qū)ο?/span>
11.10 反射包
12.7 用 defer 關(guān)閉文件
9.4 精密計(jì)算和 big 包
4.4 變量
6.1 介紹
13.4 自定義包中的錯(cuò)誤處理和 panicking
12.4 從命令行讀取參數(shù)
9.10 Go 的外部包和項(xiàng)目
8.3 for-range 的配套用法
3.5 格式化代碼
10.4 帶標(biāo)簽的結(jié)構(gòu)體
7.5 切片的復(fù)制與追加
?# 11.3 類型斷言:如何檢測和轉(zhuǎn)換接口變量的類型
5.4 for 結(jié)構(gòu)
4.8 時(shí)間和日期
2.3 在 Linux 上安裝 Go
12 讀寫數(shù)據(jù)
6.12 通過內(nèi)存緩存來提升性能
9.1 標(biāo)準(zhǔn)庫概述
12.6 用切片讀寫文件
10 結(jié)構(gòu)(struct)與方法(method)
8.5 map 的排序
12.9 JSON 數(shù)據(jù)格式
13.5 一種用閉包處理錯(cuò)誤的模式
3.2 編輯器和集成開發(fā)環(huán)境
12.12 Go 中的密碼學(xué)
5.2 測試多返回值函數(shù)的錯(cuò)誤
6.7 將函數(shù)作為參數(shù)
8.2 測試鍵值對是否存在及刪除元素
3.4 構(gòu)建并運(yùn)行 Go 程序
2.1 平臺與架構(gòu)
5.3 switch 結(jié)構(gòu)

5.4 for 結(jié)構(gòu)

如果想要重復(fù)執(zhí)行某些語句,Go 語言中您只有 for 結(jié)構(gòu)可以使用。不要小看它,這個(gè) for 結(jié)構(gòu)比其它語言中的更為靈活。

注意事項(xiàng) 其它許多語言中也沒有發(fā)現(xiàn)和 do while 完全對等的 for 結(jié)構(gòu),可能是因?yàn)檫@種需求并不是那么強(qiáng)烈。

5.4.1 基于計(jì)數(shù)器的迭代

文件 for1.go 中演示了最簡單的基于計(jì)數(shù)器的迭代,基本形式為:

for 初始化語句; 條件語句; 修飾語句 {}

示例 5.6 for1.go

package main

import "fmt"

func main() {
    for i := 0; i < 5; i++ {
        fmt.Printf("This is the %d iteration\n", i)
    }
}

輸出:

This is the 0 iteration
This is the 1 iteration
This is the 2 iteration
This is the 3 iteration
This is the 4 iteration

由花括號括起來的代碼塊會(huì)被重復(fù)執(zhí)行已知次數(shù),該次數(shù)是根據(jù)計(jì)數(shù)器(此例為 i)決定的。循環(huán)開始前,會(huì)執(zhí)行且僅會(huì)執(zhí)行一次初始化語句 i := 0;;這比在循環(huán)之前聲明更為簡短。緊接著的是條件語句 i < 5;,在每次循環(huán)開始前都會(huì)進(jìn)行判斷,一旦判斷結(jié)果為 false,則退出循環(huán)體。最后一部分為修飾語句 i++,一般用于增加或減少計(jì)數(shù)器。

這三部分組成的循環(huán)的頭部,它們之間使用分號 ; 相隔,但并不需要括號 () 將它們括起來。例如:for (i = 0; i < 10; i++) { },這是無效的代碼!

同樣的,左花括號 { 必須和 for 語句在同一行,計(jì)數(shù)器的生命周期在遇到右花括號 } 時(shí)便終止。一般習(xí)慣使用 i、j、z 或 ix 等較短的名稱命名計(jì)數(shù)器。

特別注意,永遠(yuǎn)不要在循環(huán)體內(nèi)修改計(jì)數(shù)器,這在任何語言中都是非常差的實(shí)踐!

您還可以在循環(huán)中同時(shí)使用多個(gè)計(jì)數(shù)器:

for i, j := 0, N; i < j; i, j = i+1, j-1 {}

這得益于 Go 語言具有的平行賦值的特性(可以查看第 7 章 string_reverse.go 中反轉(zhuǎn)數(shù)組的示例)。

您可以將兩個(gè) for 循環(huán)嵌套起來:

for i:=0; i<5; i++ {
    for j:=0; j<10; j++ {
        println(j)
    }
}

如果您使用 for 循環(huán)迭代一個(gè) Unicode 編碼的字符串,會(huì)發(fā)生什么?

示例 5.7 for_string.go

package main

import "fmt"

func main() {
    str := "Go is a beautiful language!"
    fmt.Printf("The length of str is: %d\n", len(str))
    for ix :=0; ix < len(str); ix++ {
        fmt.Printf("Character on position %d is: %c \n", ix, str[ix])
    }
    str2 := "日本語"
    fmt.Printf("The length of str2 is: %d\n", len(str2))
    for ix :=0; ix < len(str2); ix++ {
        fmt.Printf("Character on position %d is: %c \n", ix, str2[ix])
    }
}

輸出:

The length of str is: 27
Character on position 0 is: G 
Character on position 1 is: o 
Character on position 2 is:   
Character on position 3 is: i 
Character on position 4 is: s 
Character on position 5 is:   
Character on position 6 is: a 
Character on position 7 is:   
Character on position 8 is: b 
Character on position 9 is: e 
Character on position 10 is: a 
Character on position 11 is: u 
Character on position 12 is: t 
Character on position 13 is: i 
Character on position 14 is: f 
Character on position 15 is: u 
Character on position 16 is: l 
Character on position 17 is:   
Character on position 18 is: l 
Character on position 19 is: a 
Character on position 20 is: n 
Character on position 21 is: g 
Character on position 22 is: u 
Character on position 23 is: a 
Character on position 24 is: g 
Character on position 25 is: e 
Character on position 26 is: ! 
The length of str2 is: 9
Character on position 0 is: ? 
Character on position 1 is: ? 
Character on position 2 is: ¥ 
Character on position 3 is: ? 
Character on position 4 is: ? 
Character on position 5 is: ? 
Character on position 6 is: è 
Character on position 7 is: a 
Character on position 8 is: ? 

如果我們打印 str 和 str2 的長度,會(huì)分別得到 27 和 9。

由此我們可以發(fā)現(xiàn),ASCII 編碼的字符占用 1 個(gè)字節(jié),既每個(gè)索引都指向不同的字符,而非 ASCII 編碼的字符(占有 2 到 4 個(gè)字節(jié))不能單純地使用索引來判斷是否為同一個(gè)字符。我們會(huì)在第 5.4.4 節(jié)解決這個(gè)問題。

練習(xí)題

練習(xí) 5.4 for_loop.go

  1. 使用 for 結(jié)構(gòu)創(chuàng)建一個(gè)簡單的循環(huán)。要求循環(huán) 15 次然后使用 fmt 包來打印計(jì)數(shù)器的值。
  2. 使用 goto 語句重寫循環(huán),要求不能使用 for 關(guān)鍵字。

練習(xí) 5.5 for_character.go

創(chuàng)建一個(gè)程序,要求能夠打印類似下面的結(jié)果(直到每行 25 個(gè)字符時(shí)為止):

G
GG
GGG
GGGG
GGGGG
GGGGGG
  1. 使用 2 層嵌套 for 循環(huán)。
  2. 使用一層 for 循環(huán)以及字符串截?cái)唷?/li>

練習(xí) 5.6 bitwise_complement.go

使用按位補(bǔ)碼從 0 到 10,使用位表達(dá)式 %b 來格式化輸出。

練習(xí) 5.7 Fizz-Buzz 問題:fizzbuzz.go

寫一個(gè)從 1 打印到 100 的程序,但是每當(dāng)遇到 3 的倍數(shù)時(shí),不打印相應(yīng)的數(shù)字,但打印一次 "Fizz"。遇到 5 的倍數(shù)時(shí),打印 Buzz 而不是相應(yīng)的數(shù)字。對于同時(shí)為 3 和 5 的倍數(shù)的數(shù),打印 FizzBuzz(提示:使用 switch 語句)。

練習(xí) 5.8 rectangle_stars.go

使用 * 符號打印寬為 20,高為 10 的矩形。

5.4.2 基于條件判斷的迭代

for 結(jié)構(gòu)的第二種形式是沒有頭部的條件判斷迭代(類似其它語言中的 while 循環(huán)),基本形式為:for 條件語句 {}

您也可以認(rèn)為這是沒有初始化語句和修飾語句的 for 結(jié)構(gòu),因此 ;; 便是多余的了。

Listing 5.8 for2.go

package main

import "fmt"

func main() {
    var i int = 5

    for i >= 0 {
        i = i - 1
        fmt.Printf("The variable i is now: %d\n", i)
    }
}

輸出:

The variable i is now: 4
The variable i is now: 3
The variable i is now: 2
The variable i is now: 1
The variable i is now: 0
The variable i is now: -1

5.4.3 無限循環(huán)

條件語句是可以被省略的,如 i:=0; ; i++for { }for ;; { };; 會(huì)在使用 gofmt 時(shí)被移除):這些循環(huán)的本質(zhì)就是無限循環(huán)。最后一個(gè)形式也可以被改寫為 for true { },但一般情況下都會(huì)直接寫 for { }。

如果 for 循環(huán)的頭部沒有條件語句,那么就會(huì)認(rèn)為條件永遠(yuǎn)為 true,因此循環(huán)體內(nèi)必須有相關(guān)的條件判斷以確保會(huì)在某個(gè)時(shí)刻退出循環(huán)。

想要直接退出循環(huán)體,可以使用 break 語句(第 5.5 節(jié))或 return 語句直接返回(第 6.1 節(jié))。

但這兩者之間有所區(qū)別,break 只是退出當(dāng)前的循環(huán)體,而 return 語句提前對函數(shù)進(jìn)行返回,不會(huì)執(zhí)行后續(xù)的代碼。

無限循環(huán)的經(jīng)典應(yīng)用是服務(wù)器,用于不斷等待和接受新的請求。

for t, err = p.Token(); err == nil; t, err = p.Token() {
    ...
}

5.4.4 for-range 結(jié)構(gòu)

這是 Go 特有的一種的迭代結(jié)構(gòu),您會(huì)發(fā)現(xiàn)它在許多情況下都非常有用。它可以迭代任何一個(gè)集合(包括數(shù)組和 map,詳見第 7 和 8 章)。語法上很類似其它語言中 foreach 語句,但您依舊可以獲得每次迭代所對應(yīng)的索引。一般形式為:for ix, val := range coll { }。

要注意的是,val 始終為集合中對應(yīng)索引的值拷貝,因此它一般只具有只讀性質(zhì),對它所做的任何修改都不會(huì)影響到集合中原有的值(譯者注:如果 val 為指針,則會(huì)產(chǎn)生指針的拷貝,依舊可以修改集合中的原值)。一個(gè)字符串是 Unicode 編碼的字符(或稱之為 rune)集合,因此您也可以用它迭代字符串:

for pos, char := range str {
...
}

每個(gè) rune 字符和索引在 for-range 循環(huán)中是一一對應(yīng)的。它能夠自動(dòng)根據(jù) UTF-8 規(guī)則識別 Unicode 編碼的字符。

示例 5.9 range_string.go

package main

import "fmt"

func main() {
    str := "Go is a beautiful language!"
    fmt.Printf("The length of str is: %d\n", len(str))
    for pos, char := range str {
        fmt.Printf("Character on position %d is: %c \n", pos, char)
    }
    fmt.Println()
    str2 := "Chinese: 日本語"
    fmt.Printf("The length of str2 is: %d\n", len(str2))
    for pos, char := range str2 {
        fmt.Printf("character %c starts at byte position %d\n", char, pos)
    }
    fmt.Println()
    fmt.Println("index int(rune) rune    char bytes")
    for index, rune := range str2 {
        fmt.Printf("%-2d      %d      %U '%c' % X\n", index, rune, rune, rune, []byte(string(rune)))
    }
}

輸出:

The length of str is: 27
Character on position 0 is: G 
Character on position 1 is: o 
Character on position 2 is:   
Character on position 3 is: i 
Character on position 4 is: s 
Character on position 5 is:   
Character on position 6 is: a 
Character on position 7 is:   
Character on position 8 is: b 
Character on position 9 is: e 
Character on position 10 is: a 
Character on position 11 is: u 
Character on position 12 is: t 
Character on position 13 is: i 
Character on position 14 is: f 
Character on position 15 is: u 
Character on position 16 is: l 
Character on position 17 is:   
Character on position 18 is: l 
Character on position 19 is: a 
Character on position 20 is: n 
Character on position 21 is: g 
Character on position 22 is: u 
Character on position 23 is: a 
Character on position 24 is: g 
Character on position 25 is: e 
Character on position 26 is: ! 

The length of str2 is: 18
character C starts at byte position 0
character h starts at byte position 1
character i starts at byte position 2
character n starts at byte position 3
character e starts at byte position 4
character s starts at byte position 5
character e starts at byte position 6
character : starts at byte position 7
character   starts at byte position 8
character 日 starts at byte position 9
character 本 starts at byte position 12
character 語 starts at byte position 15

index int(rune) rune    char bytes
0       67      U+0043 'C' 43
1       104      U+0068 'h' 68
2       105      U+0069 'i' 69
3       110      U+006E 'n' 6E
4       101      U+0065 'e' 65
5       115      U+0073 's' 73
6       101      U+0065 'e' 65
7       58      U+003A ':' 3A
8       32      U+0020 ' ' 20
9       26085      U+65E5 '日' E6 97 A5
12      26412      U+672C '本' E6 9C AC
15      35486      U+8A9E '語' E8 AA 9E

請將輸出結(jié)果和 Listing 5.7(for_string.go)進(jìn)行對比。

我們可以看到,常用英文字符使用 1 個(gè)字節(jié)表示,而漢字(譯者注:嚴(yán)格來說,“Chinese: 日本語”的Chinese應(yīng)該是Japanese)使用 3 個(gè)字符表示。

練習(xí) 5.9 以下程序的輸出結(jié)果是什么?

for i := 0; i < 5; i++ {
    var v int
    fmt.Printf("%d ", v)
    v = 5
}

問題 5.2: 請描述以下 for 循環(huán)的輸出結(jié)果:

1.

for i := 0; ; i++ {
    fmt.Println("Value of i is now:", i)
}

2.

for i := 0; i < 3; {
    fmt.Println("Value of i:", i)
}

3.

s := ""
for ; s != "aaaaa"; {
    fmt.Println("Value of s:", s)
    s = s + "a"
}

4.

for i, j, s := 0, 5, "a"; i < 3 && j < 100 && s != "aaaaa"; i, j,
    s = i+1, j+1, s + "a" {
    fmt.Println("Value of i, j, s:", i, j, s)
}

鏈接