mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2026-02-08 10:52:46 +08:00
rebuild
This commit is contained in:
@@ -1,17 +1,17 @@
|
||||
# 貢獻者列錶
|
||||
# 貢獻者列表
|
||||
|
||||
|
||||
*大傢幫助完善, 請保證列錶有序(忽略大小寫)!*
|
||||
*大家幫助完善, 請保證列表有序(忽略大小寫)!*
|
||||
|
||||
|
||||
作者 | 章節
|
||||
-------------------------------------- | -------------------------
|
||||
`chai2010 <chaishushan@gmail.com>` | 前言/第二章/第十章/第十一章/第十二章/第十三章
|
||||
`chai2010 <chaishushan@gmail.com>` | 前言/第二章/第三章/第十章/第十一章/第十二章/第十三章
|
||||
`CrazySssst` | 第三章(0節)
|
||||
`foreversmart <njutree@gmail.com>` | 第七章(0節和1節)
|
||||
`Xargin <cao1988228@163.com>` | 第一章/第六章/第八章
|
||||
|
||||
# 版權
|
||||
|
||||
除特彆註明外, 本站內容均採用[知識共享-署名(CC-BY) 3.0協議](http://creativecommons.org/licenses/by/3.0/)授權, 代碼遵循[Go項目的BSD協議](http://golang.org/LICENSE)授權.
|
||||
除特别註明外, 本站內容均采用[知識共享-署名(CC-BY) 3.0協議](http://creativecommons.org/licenses/by/3.0/)授權, 代碼遵循[Go項目的BSD協議](http://golang.org/LICENSE)授權.
|
||||
|
||||
|
||||
7
Makefile
7
Makefile
@@ -8,7 +8,7 @@
|
||||
# https://github.com/GitbookIO/gitbook
|
||||
# https://github.com/wastemobile/gitbook
|
||||
|
||||
default: zh2tw
|
||||
default:
|
||||
gitbook build
|
||||
|
||||
zh2tw:
|
||||
@@ -16,3 +16,8 @@ zh2tw:
|
||||
|
||||
tw2zh:
|
||||
go run zh2tw.go . .md$$ tw2zh
|
||||
|
||||
loop:
|
||||
go run zh2tw.go . .md$$ tw2zh
|
||||
go run zh2tw.go . .md$$ zh2tw
|
||||
|
||||
|
||||
18
README.md
18
README.md
@@ -1,11 +1,11 @@
|
||||
# 關於 [《Go聖經讀書筆記》](http://golang-china.github.io/gopl-zh)
|
||||
|
||||
作為 [《The Go Programming Language》](http://gopl.io/) (中文名[《Go編程語言》](http://golang-china.github.io/gopl-zh)) 英文原版紙質圖書的購買者, [《Go聖經讀書筆記》](http://golang-china.github.io/gopl-zh) 是我們的 **讀書筆記** 和 **習題解答**, 僅供學習交流用.
|
||||
作爲 [《The Go Programming Language》](http://gopl.io/) (中文名[《Go編程語言》](http://golang-china.github.io/gopl-zh)) 英文原版紙質圖書的購買者, [《Go聖經讀書筆記》](http://golang-china.github.io/gopl-zh) 是我們的 **讀書筆記** 和 **習題解答**, 僅供學習交流用.
|
||||
|
||||
- 此 **讀書筆記** 在綫預覽: http://golang-china.github.io/gopl-zh
|
||||
- 此 **讀書筆記** 在線預覽: http://golang-china.github.io/gopl-zh
|
||||
- 此 **讀書筆記** 的源文件: http://github.com/golang-china/gopl-zh
|
||||
- 此 **讀書筆記** 項目進度: http://github.com/golang-china/gopl-zh/blob/master/progress.md
|
||||
- 此 **讀書筆記** 蔘與人員: http://github.com/golang-china/gopl-zh/blob/master/CONTRIBUTORS.md
|
||||
- 此 **讀書筆記** 參與人員: http://github.com/golang-china/gopl-zh/blob/master/CONTRIBUTORS.md
|
||||
- 原版官網: http://gopl.io
|
||||
|
||||
[](https://github.com/golang-china/gopl-zh)
|
||||
@@ -16,12 +16,16 @@
|
||||
先安裝 Go語言環境, git 工具 和 GitBook 命令行工具(`npm install gitbook-cli -g` 命令).
|
||||
|
||||
1. 運行 `go get github.com/golang-china/gopl-zh`, 穫取 源文件
|
||||
2. 運行 `go generate github.com/golang-china/gopl-zh`, 生成 `_book` 目彔
|
||||
2. 運行 `go generate github.com/golang-china/gopl-zh`, 生成 `_book` 目録
|
||||
3. 打開 `_book/index.html` 文件
|
||||
|
||||
### 簡體中文讀者
|
||||
|
||||
如果是使用簡體中文的用戶, 可在執行上述命令前運行 `make tw2zh` 命令, 將繁體中文轉換為簡體中文.
|
||||
如果是使用簡體中文的用戶, 可在執行上述命令前運行 `make tw2zh` 命令, 將繁體中文轉換爲簡體中文.
|
||||
|
||||
### Markdown 格式預覽
|
||||
|
||||
- [SUMMARY-github.md](SUMMARY-github.md)
|
||||
|
||||
# 版權聲明
|
||||
|
||||
@@ -29,7 +33,7 @@
|
||||
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Creative Commons License" style="border-width:0" src="./images/by-nc-sa-4.0-88x31.png"></img></a>
|
||||
|
||||
嚴禁任何商業行為使用或引用該 **讀書筆記** 的全部或部分內容!
|
||||
嚴禁任何商業行爲使用或引用該 **讀書筆記** 的全部或部分內容!
|
||||
|
||||
歡迎大傢提供建議!
|
||||
歡迎大家提供建議!
|
||||
|
||||
|
||||
153
SUMMARY-github.md
Normal file
153
SUMMARY-github.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# Go聖經讀書筆記
|
||||
|
||||
* [前言](preface.md)
|
||||
* [0.1. Go語言起源](ch0/ch0-01.md)
|
||||
* [0.2. Go語言項目](ch0/ch0-02.md)
|
||||
* [0.3. 本書的組織](ch0/ch0-03.md)
|
||||
* [0.4. 更多的信息](ch0/ch0-04.md)
|
||||
* [0.5. 致謝](ch0/ch0-05.md)
|
||||
* [第一章 入門](ch1/ch1.md)
|
||||
* [1.1. Hello, World](ch1/ch1-01.md)
|
||||
* [1.2. 命令行參數](ch1/ch1-02.md)
|
||||
* [1.3. 査找重複的行](ch1/ch1-03.md)
|
||||
* [1.4. GIF動畵](ch1/ch1-04.md)
|
||||
* [1.5. 穫取URL](ch1/ch1-05.md)
|
||||
* [1.6. 併發穫取多個URL](ch1/ch1-06.md)
|
||||
* [1.7. Web服務](ch1/ch1-07.md)
|
||||
* [1.8. 本章要點](ch1/ch1-08.md)
|
||||
* [第二章 程序結構](ch2/ch2.md)
|
||||
* [2.1. 命名](ch2/ch2-01.md)
|
||||
* [2.2. 聲明](ch2/ch2-02.md)
|
||||
* [2.3. 變量](ch2/ch2-03.md)
|
||||
* [2.3.1. 簡短變量聲明](ch2/ch2-03-1.md)
|
||||
* [2.3.2 指針](ch2/ch2-03-2.md)
|
||||
* [2.3.3 new 函數](ch2/ch2-03-3.md)
|
||||
* [2.3.4. 變量的生命週期](ch2/ch2-03-4.md)
|
||||
* [2.4. 賦值](ch2/ch2-04.md)
|
||||
* [2.4.1. 元組賦值](ch2/ch2-04-1.md)
|
||||
* [2.4.2. 可賦值性](ch2/ch2-04-2.md)
|
||||
* [2.5. 類型](ch2/ch2-05.md)
|
||||
* [2.6. 包和文件](ch2/ch2-06.md)
|
||||
* [2.6.1. 導入包](ch2/ch2-06-1.md)
|
||||
* [2.6.2. 包的初始化](ch2/ch2-06-2.md)
|
||||
* [2.7. 作用域](ch2/ch2-07.md)
|
||||
* [第三章 基礎數據類型](ch3/ch3.md)
|
||||
* [3.1. 整型](ch3/ch3-01.md)
|
||||
* [3.2. 浮點數](ch3/ch3-02.md)
|
||||
* [3.3. 複數](ch3/ch3-03.md)
|
||||
* [3.4. 布爾型](ch3/ch3-04.md)
|
||||
* [3.5. 字符串](ch3/ch3-05.md)
|
||||
* [3.5.1. 字符串面值](ch3/ch3-05-1.md)
|
||||
* [3.5.2. Unicode](ch3/ch3-05-2.md)
|
||||
* [3.5.3. UTF-8](ch3/ch3-05-3.md)
|
||||
* [3.5.4. 字符串和Byte切片](ch3/ch3-05-4.md)
|
||||
* [3.5.5. 字符串和數字的轉換](ch3/ch3-05-5.md)
|
||||
* [3.6. 常量](ch3/ch3-06.md)
|
||||
* [3.6.1. iota 常量生成器](ch3/ch3-06-1.md)
|
||||
* [3.6.2. 無類型常量](ch3/ch3-06-2.md)
|
||||
* [第四章 複合數據類型](ch4/ch4.md)
|
||||
* [4.1. 數組](ch4/ch4-01.md)
|
||||
* [4.2. 切片](ch4/ch4-02.md)
|
||||
* [4.3. 字典](ch4/ch4-03.md)
|
||||
* [4.4. 結構體](ch4/ch4-04.md)
|
||||
* [4.5. JSON](ch4/ch4-05.md)
|
||||
* [4.6. 文本和HTML模闆](ch4/ch4-06.md)
|
||||
* [第五章 函數](ch5/ch5.md)
|
||||
* [5.1. 函數聲明](ch5/ch5-01.md)
|
||||
* [5.2. 遞歸](ch5/ch5-02.md)
|
||||
* [5.3. 多返迴值](ch5/ch5-03.md)
|
||||
* [5.4. 錯誤](ch5/ch5-04.md)
|
||||
* [5.5. 函數值](ch5/ch5-05.md)
|
||||
* [5.6. 匿名函數](ch5/ch5-06.md)
|
||||
* [5.7. 可變參數](ch5/ch5-07.md)
|
||||
* [5.8. Deferred函數](ch5/ch5-08.md)
|
||||
* [5.9. Panic異常](ch5/ch5-09.md)
|
||||
* [5.10. Recover捕穫異常](ch5/ch5-10.md)
|
||||
* [第六章 方法](ch6/ch6.md)
|
||||
* [6.1. 方法聲明](ch6/ch6-01.md)
|
||||
* [6.2. 基於指針對象的方法](ch6/ch6-02.md)
|
||||
* [6.3. 通過嵌入結構體來擴展類型](ch6/ch6-03.md)
|
||||
* [6.4. 方法值和方法表達式](ch6/ch6-04.md)
|
||||
* [6.5. 示例: Bit數組](ch6/ch6-05.md)
|
||||
* [6.6. 封裝](ch6/ch6-06.md)
|
||||
* [第七章 接口](ch7/ch7.md)
|
||||
* [7.1. 接口是合約](ch7/ch7-01.md)
|
||||
* [7.2. 接口類型](ch7/ch7-02.md)
|
||||
* [7.3. 實現接口的條件](ch7/ch7-03.md)
|
||||
* [7.4. flag.Value接口](ch7/ch7-04.md)
|
||||
* [7.5. 接口值](ch7/ch7-05.md)
|
||||
* [7.6. sort.Interface接口](ch7/ch7-06.md)
|
||||
* [7.7. http.Handler接口](ch7/ch7-07.md)
|
||||
* [7.8. error接口](ch7/ch7-08.md)
|
||||
* [7.9. 示例: 表達式求值](ch7/ch7-09.md)
|
||||
* [7.10. 類型斷言](ch7/ch7-10.md)
|
||||
* [7.11. 基於類型斷言識别錯誤類型](ch7/ch7-11.md)
|
||||
* [7.12. 通過類型斷言査詢接口](ch7/ch7-12.md)
|
||||
* [7.13. 類型分支](ch7/ch7-13.md)
|
||||
* [7.14. 示例: 基於標記的XML解碼](ch7/ch7-14.md)
|
||||
* [7.15. 補充幾點](ch7/ch7-15.md)
|
||||
* [第八章 Goroutines和Channels](ch8/ch8.md)
|
||||
* [8.1. Goroutines](ch8/ch8-01.md)
|
||||
* [8.2. 示例: 併發的Clock服務](ch8/ch8-02.md)
|
||||
* [8.3. 示例: 併發的Echo服務](ch8/ch8-03.md)
|
||||
* [8.4. Channels](ch8/ch8-04.md)
|
||||
* [8.5. 併行的循環](ch8/ch8-05.md)
|
||||
* [8.6. 示例: 併發的Web爬蟲](ch8/ch8-06.md)
|
||||
* [8.7. 基於select的多路複用](ch8/ch8-07.md)
|
||||
* [8.8. 示例: 併發的字典遍歷](ch8/ch8-08.md)
|
||||
* [8.9. 併發的退齣](ch8/ch8-09.md)
|
||||
* [8.10. 示例: 聊天服務](ch8/ch8-10.md)
|
||||
* [第九章 基於共享變量的併發](ch9/ch9.md)
|
||||
* [9.1. 競爭條件](ch9/ch9-01.md)
|
||||
* [9.2. sync.Mutex互斥鎖](ch9/ch9-02.md)
|
||||
* [9.3. sync.RWMutex讀寫鎖](ch9/ch9-03.md)
|
||||
* [9.4. 內存同步](ch9/ch9-04.md)
|
||||
* [9.5. sync.Once初始化](ch9/ch9-05.md)
|
||||
* [9.6. 競爭條件檢測](ch9/ch9-06.md)
|
||||
* [9.7. 示例: 併發的非阻塞緩存](ch9/ch9-07.md)
|
||||
* [9.8. Goroutines和線程](ch9/ch9-08.md)
|
||||
* [第十章 包和工具](ch10/ch10.md)
|
||||
* [10.1. 簡介](ch10/ch10-01.md)
|
||||
* [10.2. 導入路徑](ch10/ch10-02.md)
|
||||
* [10.3. 包聲明](ch10/ch10-03.md)
|
||||
* [10.4. 導入聲明](ch10/ch10-04.md)
|
||||
* [10.5. 匿名導入](ch10/ch10-05.md)
|
||||
* [10.6. 包和命名](ch10/ch10-06.md)
|
||||
* [10.7. 工具](ch10/ch10-07.md)
|
||||
* [10.7.1. 工作區結構](ch10/ch10-07-1.md)
|
||||
* [10.7.2. 下載包](ch10/ch10-07-2.md)
|
||||
* [10.7.3. 構建包](ch10/ch10-07-3.md)
|
||||
* [10.7.4. 包文檔](ch10/ch10-07-4.md)
|
||||
* [10.7.5. 內部包](ch10/ch10-07-5.md)
|
||||
* [10.7.6. 査詢包](ch10/ch10-07-6.md)
|
||||
* [第十一章 測試](ch11/ch11.md)
|
||||
* [11.1. go test](ch11/ch11-01.md)
|
||||
* [11.2. 測試函數](ch11/ch11-02.md)
|
||||
* [11.2.1. 隨機測試](ch11/ch11-02-1.md)
|
||||
* [11.2.2. 測試一個命令](ch11/ch11-02-2.md)
|
||||
* [11.2.3. 白盒測試](ch11/ch11-02-3.md)
|
||||
* [11.2.4. 擴展測試包](ch11/ch11-02-4.md)
|
||||
* [11.2.5. 編寫有效的測試](ch11/ch11-02-5.md)
|
||||
* [11.2.6. 避免的不穩定的測試](ch11/ch11-02-6.md)
|
||||
* [11.3. 測試覆蓋率](ch11/ch11-03.md)
|
||||
* [11.4. 基準測試](ch11/ch11-04.md)
|
||||
* [11.5. 剖析](ch11/ch11-05.md)
|
||||
* [11.6. 示例函數](ch11/ch11-06.md)
|
||||
* [第十二章 反射](ch12/ch12.md)
|
||||
* [12.1. 爲何需要反射?](ch12/ch12-01.md)
|
||||
* [12.2. reflect.Type和reflect.Value](ch12/ch12-02.md)
|
||||
* [12.3. Display遞歸打印](ch12/ch12-03.md)
|
||||
* [12.4. 示例: 編碼S表達式](ch12/ch12-04.md)
|
||||
* [12.5. 通過reflect.Value脩改值](ch12/ch12-05.md)
|
||||
* [12.6. 示例: 解碼S表達式](ch12/ch12-06.md)
|
||||
* [12.7. 穫取結構體字段標識](ch12/ch12-07.md)
|
||||
* [12.8. 顯示一個類型的方法集](ch12/ch12-08.md)
|
||||
* [12.9. 幾點忠告](ch12/ch12-09.md)
|
||||
* [第十三章 底層編程](ch13/ch13.md)
|
||||
* [13.1. unsafe.Sizeof, Alignof 和 Offsetof](ch13/ch13-01.md)
|
||||
* [13.2. unsafe.Pointer](ch13/ch13-02.md)
|
||||
* [13.3. 示例: 深度相等判斷](ch13/ch13-03.md)
|
||||
* [13.4. 通過cgo調用C代碼](ch13/ch13-04.md)
|
||||
* [13.5. 幾點忠告](ch13/ch13-05.md)
|
||||
* [勘誤](errata.md)
|
||||
|
||||
110
ch0/ch0-01.html
110
ch0/ch0-01.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="0.1" data-chapter-title="Go語言起源" data-filepath="ch0/ch0-01.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="0.1" data-chapter-title="Go語言起源" data-filepath="ch0/ch0-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,16 +2024,16 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="go語言起源">Go語言起源</h2>
|
||||
<p>就像生物物種, 一個成功的編程語言的後代一般都會繼承它們祖先的優點; 當然有時多種語言混閤也會產生令人驚訝的特性; 還有一些激進的新特性可能併沒有先例. 我們可以通過觀察語言的和環境是如何相互促進和影響的演化過程而學到很多.</p>
|
||||
<p>下圖展示了最早期的編程語言對Go語言設計產生的重要影響.</p>
|
||||
<p>就像生物物種, 一個成功的編程語言的後代一般都會繼承它們祖先的優點; 當然有時多種語言混合也會産生令人驚訝的特性; 還有一些激進的新特性可能併沒有先例. 我們可以通過觀察語言的和環境是如何相互促進和影響的演化過程而學到很多.</p>
|
||||
<p>下圖展示了最早期的編程語言對Go語言設計産生的重要影響.</p>
|
||||
<p><img src="../images/ch0-01.png" alt=""></p>
|
||||
<p>Go有時候被描述為"C類似語言", 或者是"21世紀的C語言". Go從C語言繼承了相似的錶達式語法, 控製流結構, 基礎數據類型, 調用參數傳值, 指鍼等很多思想, 還有C語言一直看中的編譯後機器碼的運行效率以及和現有操作繫統的無縫的適配.</p>
|
||||
<p>但是在Go語言傢的族樹中還有其他的祖先. 其中一個有影響的分支來自Niklaus Wirth設計的Pascal語言. Modula-2 激髮了包的概唸. Oberon 摒棄了模塊接口文件和模塊實現文件之間的區彆. Oberon-2 影響了的包的導入和聲明的語法, 還有 麫曏對象 Oberon 所提供的方法的聲明語法等.</p>
|
||||
<p>Go的另一支祖先, 也是Go區彆其他語言的重要特性, 靈感來自貝爾實驗室的Tony Hoare的1978年髮錶的鮮為外界所知的關於併髮研究的基礎文獻communicating sequential processes (CSP). 在CSP中, 程序是一組中間沒有共享狀態的平行的處理過程, 它們使用管道進行通信和衕步. 不過Tony Hoare的CSP隻是一個用於描述併髮性基本概唸的描述語言, 併不是一個編寫可執行程序的編程語言.</p>
|
||||
<p>Rob Pike和其他人開始嚐試將CSP引入實際的編程語言中. 第一個語言叫Squeak(老鼠間交流的語言), 一個提供鼠標和鍵盤事件處理的語言, 它的管道是靜態創建的. 然後是Newsqueak, 提供了類似C語言語句和錶達式的的語法和Pascal的類似推導語法. 它是一個帶垃圾迴收的純函數式語言, 再此鍼對管理鍵盤, 鼠標和窗口事件管理. 但是Newsqueak中管道是動態創建的, 屬於第一類值, 可以保存到變量中.</p>
|
||||
<p>在Plan9操作繫統中, 這些想法被吸收到一個叫Alef的編程語言中. Alef視圖將Newsqueak改造為繫統編程語言, 但是因為缺少垃圾迴收機製而導緻併髮處理很痛苦.</p>
|
||||
<p>Go的其他一些特性零散地來着其他的一些語言; 比如 iota 從 APL 借鑑, 詞法作用域與嵌套函數來自 Scheme (和其他很多語言). 我們也可以從Go中髮現很多創新的設計. 比如Go的切片為動態數組提供了有效的隨機存取性能, 以及可能會讓人聯想到鏈錶的底層的共享機製.
|
||||
還有Go自己髮明的defer語句.</p>
|
||||
<p>Go有時候被描述爲"C類似語言", 或者是"21世紀的C語言". Go從C語言繼承了相似的表達式語法, 控製流結構, 基礎數據類型, 調用參數傳值, 指針等很多思想, 還有C語言一直看中的編譯後機器碼的運行效率以及和現有操作繫統的無縫的適配.</p>
|
||||
<p>但是在Go語言家的族樹中還有其他的祖先. 其中一個有影響的分支來自Niklaus Wirth設計的Pascal語言. Modula-2 激發了包的概念. Oberon 摒棄了模塊接口文件和模塊實現文件之間的區别. Oberon-2 影響了的包的導入和聲明的語法, 還有 面向對象 Oberon 所提供的方法的聲明語法等.</p>
|
||||
<p>Go的另一支祖先, 也是Go區别其他語言的重要特性, 靈感來自貝爾實驗室的Tony Hoare的1978年發表的鮮爲外界所知的關於併發研究的基礎文獻communicating sequential processes (CSP). 在CSP中, 程序是一組中間沒有共享狀態的平行的處理過程, 它們使用管道進行通信和同步. 不過Tony Hoare的CSP隻是一個用於描述併發性基本概念的描述語言, 併不是一個編寫可執行程序的編程語言.</p>
|
||||
<p>Rob Pike和其他人開始嚐試將CSP引入實際的編程語言中. 第一個語言叫Squeak(老鼠間交流的語言), 一個提供鼠標和鍵盤事件處理的語言, 它的管道是靜態創建的. 然後是Newsqueak, 提供了類似C語言語句和表達式的的語法和Pascal的類似推導語法. 它是一個帶垃圾迴收的純函數式語言, 再此針對管理鍵盤, 鼠標和窗口事件管理. 但是Newsqueak中管道是動態創建的, 屬於第一類值, 可以保存到變量中.</p>
|
||||
<p>在Plan9操作繫統中, 這些想法被吸收到一個叫Alef的編程語言中. Alef視圖將Newsqueak改造爲繫統編程語言, 但是因爲缺少垃圾迴收機製而導致併發處理很痛苦.</p>
|
||||
<p>Go的其他一些特性零散地來着其他的一些語言; 比如 iota 從 APL 借鑒, 詞法作用域與嵌套函數來自 Scheme (和其他很多語言). 我們也可以從Go中發現很多創新的設計. 比如Go的切片爲動態數組提供了有效的隨機存取性能, 以及可能會讓人聯想到鏈表的底層的共享機製.
|
||||
還有Go自己發明的defer語句.</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
106
ch0/ch0-02.html
106
ch0/ch0-02.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="0.2" data-chapter-title="Go語言項目" data-filepath="ch0/ch0-02.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="0.2" data-chapter-title="Go語言項目" data-filepath="ch0/ch0-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2061,13 +2025,13 @@
|
||||
|
||||
<h2 id="go語言項目">Go語言項目</h2>
|
||||
<p>所有的編程語言都反映了設計者對編程哲學的反思, 通常包括之前的語言所暴露的一些不足的地方.
|
||||
Go項目是在Google超級復雜的幾個軟件繫統遇到的一些問題的反思(但是這個問題絶不是穀歌特有的).</p>
|
||||
<p>正如Rob Pike所說, “復雜性是乘法級相關的”, 通過增加一個部分的復雜性來脩復問題通常將慢慢地增加其他部分的復雜性. 通過增加功能和選項和配置是脩復問題的最快的途徑, 但是這很容易忽略簡潔的內涵, 卽使從長遠來看, 簡潔依然是好的軟件關鍵因素.</p>
|
||||
<p>簡潔需要在工作開始的時候減少不必要的想法, 併且在軟件的生命週期內嚴格區彆好的改變或壞的改變. 通過足夠的努力, 一個好的改變可以在不破壞完整概唸的前提下保持自適應, 正如 Fred Brooks 所說的 "概唸完整性"; 而一個壞的改變則不能, 它們僅僅是通過膚淺的簡單的妥協來破壞設計的一緻性. 隻有通過簡潔的設計, 纔能讓一個繫統保持穩定, 安全, 和持續的生長.</p>
|
||||
<p>Go項目包括語言本身, 附帶的工具和標準庫, 最後但併非不重要的, 簡潔編程哲學的宣言. 就事後的目光來看, Go的這些地方都做的不錯: 擁有自動垃圾迴收, 一個包繫統, 函數作為一等公民, 詞法作用域, 繫統調用接口, 隻讀的UTF8字符串. 但是Go隻有相對較少的特性, 也不太可能對添加更多. 例如, 它沒有隱式的數值轉換, 沒有構造函數和析構函數, 沒有運算符重載, 沒有默認參數, 沒有繼承, 沒有氾型, 沒有異常, 沒有宏, 沒有函數脩飾, 沒有綫程侷部存儲. 但是語言是成熟和穩定的, 而且保證曏後兼容: 以前的Go程序可以用新版本的編譯器和標準庫下構建.</p>
|
||||
<p>Go有足夠的類型繫統以避免動態語言中那些粗心的類型錯誤, 但是Go的類型繫統相比傳統的強類型語言又要簡潔很多. 有時候這會導緻一個"無類型"的抽象類型, 但是Go程序員併不需要像 C++ 或 Haskell 程序員那樣糾結具體類型的安全屬性. 但實踐中Go的簡潔的類型繫統給了程序員更多的安全性和更好的運行時性能.</p>
|
||||
<p>Go 鼓勵當代計算機繫統設計的認識, 特彆是侷部的重要性. 它的內置數據類型和大多數的準庫數據結構都經過精心設計而避免顯式的初始化或隱式的構造函數, 因此較少的內存分配和內存初始化被隱藏在了代碼中. Go的聚閤類型(結構體和數組)直接操作它們的元素, 需要更少的存儲空間, 更少的內存分配, 而且指鍼操作比其他間接語言也更直接. 由於現代計算機是一個併行的機器, Go提供了基於CSP的併髮特性. Go的動態棧使得輕量級綫程goroutine的初始棧很小, 創建一個goroutine的代價很小, 因此創建百萬級的goroutine是可行的.</p>
|
||||
<p>Go的標準庫(通常被稱為自帶的電池), 提供了清晰的構建模塊和接口, 包含 I/O, 文本處理, 圖像, 密碼學, 網絡, 和分佈式應用程序, 併支持許多標準的文件格式和協議. 庫和工具使用大量的約定來減少額外的配置和解釋, 從而簡化程序的邏輯, 而且每個Go程序結構都是如此的相似, 因此也更容易學習. 構建項目使用的Go工具隻使用文件名和標識符名稱, 一個偶爾的特殊註釋來確定所有的庫, 可執行文件, 測試, 基準測試, 例子, 特定於平颱的變量, 項目的文檔; Go源代碼本身包含構建規範.</p>
|
||||
Go項目是在Google超級複雜的幾個軟件繫統遇到的一些問題的反思(但是這個問題絶不是谷歌特有的).</p>
|
||||
<p>正如Rob Pike所説, “複雜性是乘法級相關的”, 通過增加一個部分的複雜性來脩複問題通常將慢慢地增加其他部分的複雜性. 通過增加功能和選項和配置是脩複問題的最快的途徑, 但是這很容易忽略簡潔的內涵, 卽使從長遠來看, 簡潔依然是好的軟件關鍵因素.</p>
|
||||
<p>簡潔需要在工作開始的時候減少不必要的想法, 併且在軟件的生命週期內嚴格區别好的改變或壞的改變. 通過足夠的努力, 一個好的改變可以在不破壞完整概念的前提下保持自適應, 正如 Fred Brooks 所説的 "概念完整性"; 而一個壞的改變則不能, 它們僅僅是通過膚淺的簡單的妥協來破壞設計的一致性. 隻有通過簡潔的設計, 纔能讓一個繫統保持穩定, 安全, 和持續的生長.</p>
|
||||
<p>Go項目包括語言本身, 附帶的工具和標準庫, 最後但併非不重要的, 簡潔編程哲學的宣言. 就事後的目光來看, Go的這些地方都做的不錯: 擁有自動垃圾迴收, 一個包繫統, 函數作爲一等公民, 詞法作用域, 繫統調用接口, 隻讀的UTF8字符串. 但是Go隻有相對較少的特性, 也不太可能對添加更多. 例如, 它沒有隱式的數值轉換, 沒有構造函數和析構函數, 沒有運算符重載, 沒有默認參數, 沒有繼承, 沒有泛型, 沒有異常, 沒有宏, 沒有函數脩飾, 沒有線程局部存儲. 但是語言是成熟和穩定的, 而且保證向後兼容: 以前的Go程序可以用新版本的編譯器和標準庫下構建.</p>
|
||||
<p>Go有足夠的類型繫統以避免動態語言中那些粗心的類型錯誤, 但是Go的類型繫統相比傳統的強類型語言又要簡潔很多. 有時候這會導致一個"無類型"的抽象類型, 但是Go程序員併不需要像 C++ 或 Haskell 程序員那樣糾結具體類型的安全屬性. 但實踐中Go的簡潔的類型繫統給了程序員更多的安全性和更好的運行時性能.</p>
|
||||
<p>Go 鼓勵當代計算機繫統設計的認識, 特别是局部的重要性. 它的內置數據類型和大多數的準庫數據結構都經過精心設計而避免顯式的初始化或隱式的構造函數, 因此較少的內存分配和內存初始化被隱藏在了代碼中. Go的聚合類型(結構體和數組)直接操作它們的元素, 需要更少的存儲空間, 更少的內存分配, 而且指針操作比其他間接語言也更直接. 由於現代計算機是一個併行的機器, Go提供了基於CSP的併發特性. Go的動態棧使得輕量級線程goroutine的初始棧很小, 創建一個goroutine的代價很小, 因此創建百萬級的goroutine是可行的.</p>
|
||||
<p>Go的標準庫(通常被稱爲自帶的電池), 提供了清晰的構建模塊和接口, 包含 I/O, 文本處理, 圖像, 密碼學, 網絡, 和分布式應用程序, 併支持許多標準的文件格式和協議. 庫和工具使用大量的約定來減少額外的配置和解釋, 從而簡化程序的邏輯, 而且每個Go程序結構都是如此的相似, 因此也更容易學習. 構建項目使用的Go工具隻使用文件名和標識符名稱, 一個偶爾的特殊註釋來確定所有的庫, 可執行文件, 測試, 基準測試, 例子, 特定於平颱的變量, 項目的文檔; Go源代碼本身包含構建規范.</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
116
ch0/ch0-03.html
116
ch0/ch0-03.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="0.3" data-chapter-title="本書的組織" data-filepath="ch0/ch0-03.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="0.3" data-chapter-title="本書的組織" data-filepath="ch0/ch0-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2062,33 +2026,33 @@
|
||||
<h2 id="本書的組織">本書的組織</h2>
|
||||
<p>我們假設你有一個或多個其他編程語言的使用經歷, 不過是類似 C、c++,和Java 的編譯型語言,
|
||||
還是類似 Python, Ruby, JavaScript 的腳本語言, 因此我們不會相對完全的編程語言初學者那樣解釋所有的細節.
|
||||
因為Go的語言的 變量,常量,錶達式,控製流和函數等語法也是類似的.</p>
|
||||
因爲Go的語言的 變量,常量,表達式,控製流和函數等語法也是類似的.</p>
|
||||
<p>第一章包含了Go敎程的基本結構, 通過十幾個程序介紹了用Go如何實現 類似讀寫文件, 文本格式化, 創建圖像,
|
||||
網絡客戶端和服務器通訊 等日常工作.</p>
|
||||
<p>第二章描述了一個Go程序的基本元素結構, 變量, 定義新的類型, 包和文件, 和作用域. 第三章討論了數字, 佈爾值, 字符串和常量, 併演示了如何顯示和處理Unicode. 第四章描述了復閤類型, 從簡單的數組, 字典, 切片, 到動態列錶. 第五章涵蓋了函數, 併討論了錯誤處理, panic 和 recover, 和 defer 語句.</p>
|
||||
<p>第三章討論了數字、佈爾值、字符串和常數,併解釋顯示處理Unicode。
|
||||
第四章描述了復閤類型,類型建立使用數組,從簡單的地圖,結構,和切割的方法去動態列錶。第五章涵蓋了函數和討論錯誤處理,恐慌和恢復,而推遲的陳述。</p>
|
||||
<p>第二章描述了一個Go程序的基本元素結構, 變量, 定義新的類型, 包和文件, 和作用域. 第三章討論了數字, 布爾值, 字符串和常量, 併演示了如何顯示和處理Unicode. 第四章描述了複合類型, 從簡單的數組, 字典, 切片, 到動態列表. 第五章涵蓋了函數, 併討論了錯誤處理, panic 和 recover, 和 defer 語句.</p>
|
||||
<p>第三章討論了數字、布爾值、字符串和常數,併解釋顯示處理Unicode。
|
||||
第四章描述了複合類型,類型建立使用數組,從簡單的地圖,結構,和切割的方法去動態列表。第五章涵蓋了函數和討論錯誤處理,恐慌和恢複,而推遲的陳述。</p>
|
||||
<p>第一章到第五章是基礎部分, 任何主流命令式語言的一部分都是類似的. 雖然有時候Go的語法和風格會有自己的特色, 但是大多數程序員將很快適應.
|
||||
剩下的章節是Go中特有的部分: 方法, 接口, 併髮, 包, 測試和反射等.</p>
|
||||
<p>Go的麫曏對象是不衕尋常的. 它沒有類層次結構, 甚至沒有類; 僅僅是通過組閤(而不是繼承)簡單的對象來構建復雜的對象.
|
||||
剩下的章節是Go中特有的部分: 方法, 接口, 併發, 包, 測試和反射等.</p>
|
||||
<p>Go的面向對象是不同尋常的. 它沒有類層次結構, 甚至沒有類; 僅僅是通過組合(而不是繼承)簡單的對象來構建複雜的對象.
|
||||
方法不僅僅可以定義在結構體上, 而是可以在任何用戶自己定義的類型上; 併且具體類型和抽象類型(接口)之間的關繫是隱式的,
|
||||
所以很多類型的設計者可能併不知道該類型到底滿足了哪些接口. 方法在第六章討論, 接口在第七章將討論.</p>
|
||||
<p>第八章討論了基於順序通信進程(CSP)的概唸的併髮編程, 使用 goroutines 和 channels. 第九章討論了更為傳統的基於共享變量的併髮性.</p>
|
||||
<p>第八章討論了基於順序通信進程(CSP)的概念的併發編程, 使用 goroutines 和 channels. 第九章討論了更爲傳統的基於共享變量的併發性.</p>
|
||||
<p>第十章描述了包機製, 包的組織結構. 本章還展示了如何利用有效的利用Go自帶的工具,
|
||||
通過一個命令提供了編譯, 測試, 基準測試, 代碼格式化, 文檔, 和許多其他任務.</p>
|
||||
<p>第十一章討論單元測試, Go的工具和標準庫中集成了輕量級的測試功能, 從而避免了採用復雜強大的測試框架. 測試庫提供一些基本的構建, 如果有必要可以構建更復雜的測試抽象.</p>
|
||||
<p>第十一章討論單元測試, Go的工具和標準庫中集成了輕量級的測試功能, 從而避免了采用複雜強大的測試框架. 測試庫提供一些基本的構建, 如果有必要可以構建更複雜的測試抽象.</p>
|
||||
<p>第十二章討論了反射, 一個程序在運行期間來檢視自己的能力. 反射是一個強大的工具, 不過要謹慎地使用; 本章通過用反射實現一些重要的Go庫來展示了反射的用法. 第十三章解釋了低級編程的細節, 通過使用 unsafe 包來繞過Go的類型繫統, 有時這是必要的.</p>
|
||||
<p>每一章會有一些練習, 你可以根據你對Go語言的理解, 然後脩改書中的例子來探索Go的其他用法.</p>
|
||||
<p>書中所有的代碼都可以從 gopl.io 上的 Git 倉庫下載. go get可以根據每個例子的其導入路徑方便地穫取/構建/併安裝. 你需要選擇一個目彔作為工作空間, 然後將GOPATH環境指曏這個工作目彔.</p>
|
||||
<p>Go工具將在必要時創建的相應的目彔. 例如:</p>
|
||||
<pre><code>$ export GOPATH=$HOME/gobook # 選擇工作目彔
|
||||
<p>書中所有的代碼都可以從 gopl.io 上的 Git 倉庫下載. go get可以根據每個例子的其導入路徑方便地穫取/構建/併安裝. 你需要選擇一個目録作爲工作空間, 然後將GOPATH環境指向這個工作目録.</p>
|
||||
<p>Go工具將在必要時創建的相應的目録. 例如:</p>
|
||||
<pre><code>$ export GOPATH=$HOME/gobook # 選擇工作目録
|
||||
$ go get gopl.io/ch1/helloworld # 穫取/編譯/安裝
|
||||
$ $GOPATH/bin/helloworld # 運行
|
||||
Hello, 世界 # 這是中文, 不是日文
|
||||
</code></pre><p>要運行這些例子, 你需要安裝Go1.5以上的版本.</p>
|
||||
<p>$ go version
|
||||
go version go1.5 linux/amd64</p>
|
||||
<p>如果你用的是其他的操作繫統, 請參考 <a href="https://golang.org/doc/install" target="_blank">https://golang.org/doc/install</a> 提供的說明安裝.</p>
|
||||
<p>如果你用的是其他的操作繫統, 請參考 <a href="https://golang.org/doc/install" target="_blank">https://golang.org/doc/install</a> 提供的説明安裝.</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
106
ch0/ch0-04.html
106
ch0/ch0-04.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="0.4" data-chapter-title="更多的信息" data-filepath="ch0/ch0-04.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="0.4" data-chapter-title="更多的信息" data-filepath="ch0/ch0-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,12 +2024,12 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="更多的信息">更多的信息</h2>
|
||||
<p>最佳的信息來自Go的官方網站, <a href="https://golang.org" target="_blank">https://golang.org</a>, 它提供了訪問完善的參考文檔, 包括編程語言規範和標準庫等諸多信息. 衕時也包含了如果更好了編寫Go程序的基本敎程, 還有各種各樣的在綫文本資源和視頻資源, 它們是本身終有價值的補充. Go的博客 blog.golang.org 髮佈一些Go語言最好的實踐文章, 包括當前語言的狀態, 未來的計劃, 會議報告和Go相關的各種主題.</p>
|
||||
<p>在綫訪問的一個有價值的地方是可以從web頁麫運行Go的程序(而紙質書則沒有這麼便利了). 這個功能 由來自 play.golang.org 的 Go Playground 提供, 併且可以方便地嵌入到其他頁麫, 例如 golang.org 的主頁, 或 godoc 提供的文檔中.</p>
|
||||
<p>Playground 可以簡單的通過執行一個小程序來測試對語法, 語義, 或對程序庫的理解, 類似其他很多語言提供的 REPL 卽時運行的方式. 衕時它可以生成對應的url, 非常適閤共享Go代碼片段, bug報告或提齣建議.</p>
|
||||
<p>基於 Playground 構建的 Go Tour (tour.golang.org), 是一個繫列的Go入門敎程, 包含了諸多基本概唸和結構相關的可在綫運行的互動小程序.</p>
|
||||
<p>Playground 和 Tour 也有一些不足, 它們隻能導入標準庫, 而且因為安全的原因對一些網絡庫做了限製. 而且要編譯和運行需要訪問互聯網. 對於一些更復製的實驗, 你可能需要在自己的電腦上運行程序. 倖運的是下載Go的過程很簡單, 從 golang.org 下載應該不超過幾分鍾, 然後就可以在自己電腦上編寫和運行Go程序了.</p>
|
||||
<p>Go是一個開源項目, 你可以 在 <a href="https://golang.org/pkg" target="_blank">https://golang.org/pkg</a> 閱讀標準庫中任意函數和類型的代碼, 和下載的代碼完全一緻. 這樣可以知道很多函數是如何工作的, 挖掘一些答案的細節, 或者僅僅是欣賞 專業的Go代碼.</p>
|
||||
<p>最佳的信息來自Go的官方網站, <a href="https://golang.org" target="_blank">https://golang.org</a>, 它提供了訪問完善的參考文檔, 包括編程語言規范和標準庫等諸多信息. 同時也包含了如果更好了編寫Go程序的基本敎程, 還有各種各樣的在線文本資源和視頻資源, 它們是本身終有價值的補充. Go的博客 blog.golang.org 發布一些Go語言最好的實踐文章, 包括當前語言的狀態, 未來的計劃, 會議報告和Go相關的各種主題.</p>
|
||||
<p>在線訪問的一個有價值的地方是可以從web頁面運行Go的程序(而紙質書則沒有這麽便利了). 這個功能 由來自 play.golang.org 的 Go Playground 提供, 併且可以方便地嵌入到其他頁面, 例如 golang.org 的主頁, 或 godoc 提供的文檔中.</p>
|
||||
<p>Playground 可以簡單的通過執行一個小程序來測試對語法, 語義, 或對程序庫的理解, 類似其他很多語言提供的 REPL 卽時運行的方式. 同時它可以生成對應的url, 非常適合共享Go代碼片段, bug報告或提齣建議.</p>
|
||||
<p>基於 Playground 構建的 Go Tour (tour.golang.org), 是一個繫列的Go入門敎程, 包含了諸多基本概念和結構相關的可在線運行的互動小程序.</p>
|
||||
<p>Playground 和 Tour 也有一些不足, 它們隻能導入標準庫, 而且因爲安全的原因對一些網絡庫做了限製. 而且要編譯和運行需要訪問互聯網. 對於一些更複製的實驗, 你可能需要在自己的電腦上運行程序. 幸運的是下載Go的過程很簡單, 從 golang.org 下載應該不超過幾分鐘, 然後就可以在自己電腦上編寫和運行Go程序了.</p>
|
||||
<p>Go是一個開源項目, 你可以 在 <a href="https://golang.org/pkg" target="_blank">https://golang.org/pkg</a> 閲讀標準庫中任意函數和類型的代碼, 和下載的代碼完全一致. 這樣可以知道很多函數是如何工作的, 挖掘一些答案的細節, 或者僅僅是欣賞 專業的Go代碼.</p>
|
||||
|
||||
|
||||
</section>
|
||||
@@ -2079,7 +2043,7 @@
|
||||
<a href="../ch0/ch0-03.html" class="navigation navigation-prev " aria-label="Previous page: 本書的組織"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../ch0/ch0-05.html" class="navigation navigation-next " aria-label="Next page: 緻謝"><i class="fa fa-angle-right"></i></a>
|
||||
<a href="../ch0/ch0-05.html" class="navigation navigation-next " aria-label="Next page: 致謝"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
114
ch0/ch0-05.html
114
ch0/ch0-05.html
@@ -5,7 +5,7 @@
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>緻謝 | Go编程语言</title>
|
||||
<title>致謝 | Go编程语言</title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="generator" content="GitBook 2.5.2">
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="0.5" data-chapter-title="緻謝" data-filepath="ch0/ch0-05.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="0.5" data-chapter-title="致謝" data-filepath="ch0/ch0-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2059,23 +2023,23 @@
|
||||
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="緻謝">緻謝</h2>
|
||||
<p>Rob Pike 和 Russ Cox, 以及其他很多Go團隊的覈心成員多次仔細閱讀了本書的手稿,
|
||||
他們對本書的組織結構和錶述用詞等給齣了很多寶貴的建議. 在準備日本翻譯的時候,
|
||||
Yoshiki Shibata 更是仔細地審閱了本書的每個部分, 及時髮現了諸多英文和代碼的錯誤.
|
||||
我們非常感謝本書的審閱者, 併感謝對本書給齣了重要的建議的 Brian Goetz, Corey Kosak,
|
||||
<h2 id="致謝">致謝</h2>
|
||||
<p>Rob Pike 和 Russ Cox, 以及其他很多Go糰隊的覈心成員多次仔細閲讀了本書的手稿,
|
||||
他們對本書的組織結構和表述用詞等給齣了很多寶貴的建議. 在準備日本翻譯的時候,
|
||||
Yoshiki Shibata 更是仔細地審閲了本書的每個部分, 及時發現了諸多英文和代碼的錯誤.
|
||||
我們非常感謝本書的審閲者, 併感謝對本書給齣了重要的建議的 Brian Goetz, Corey Kosak,
|
||||
Arnold Robbins, Josh Bleecher Snyder 和 Peter Weinberger 等人.</p>
|
||||
<p>我們感謝 Sameer Ajmani, Ittai Balaban, David Crawshaw, Billy Donohue, Jonathan Feinberg, Andrew Gerrand,
|
||||
Robert Griesemer, John Linderman, Minux Ma, Bryan Mills, Bala Natarajan, Cosmos Nicolaou, Paul Staniforth, Nigel Tao
|
||||
以及 Howard Trickey 給齣的許多有用的建議.
|
||||
我們還要感謝 David Brailsford 和 Raph Levien 關於類型設置的建議.</p>
|
||||
<p>我們來自 Addison-Wesley 的編輯 Greg Doench, 從最初就得到了越來越多的幫助.
|
||||
來自AW生產團隊的 John Fuller, Dayna Isley, Julie Nahil, Chuti Prasertsith, 和 Barbara Wood,
|
||||
來自AW生産糰隊的 John Fuller, Dayna Isley, Julie Nahil, Chuti Prasertsith, 和 Barbara Wood,
|
||||
感謝你們的熱心幫助.</p>
|
||||
<p>Alan Donovan 特彆感謝: Sameer Ajmani, Chris Demetriou, Walt Drummond 和 Google的Reid Tatge 允許他有充裕的時間去寫本書;
|
||||
感謝 Stephen Donovan 的建議和始終如一的鼓勵, 以及他的妻子 Leila Kazemi 沒有讓他為了傢庭瑣事而分心, 併熱情堅定地支持這個項目.</p>
|
||||
<p>Brian Kernighan特彆感謝: 朋友和衕事的耐心和寬容他, 讓他慢慢地梳理本身的寫作思路.
|
||||
衕時感謝他的妻子 Meg 和其他很多朋友對他寫作事業的支持.</p>
|
||||
<p>Alan Donovan 特别感謝: Sameer Ajmani, Chris Demetriou, Walt Drummond 和 Google的Reid Tatge 允許他有充裕的時間去寫本書;
|
||||
感謝 Stephen Donovan 的建議和始終如一的鼓勵, 以及他的妻子 Leila Kazemi 沒有讓他爲了家庭瑣事而分心, 併熱情堅定地支持這個項目.</p>
|
||||
<p>Brian Kernighan特别感謝: 朋友和同事的耐心和寬容他, 讓他慢慢地梳理本身的寫作思路.
|
||||
同時感謝他的妻子 Meg 和其他很多朋友對他寫作事業的支持.</p>
|
||||
<p>2015年 10月 於 紐約</p>
|
||||
|
||||
|
||||
|
||||
126
ch1/ch1-01.html
126
ch1/ch1-01.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="1.1" data-chapter-title="Hello, World" data-filepath="ch1/ch1-01.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="1.1" data-chapter-title="Hello, World" data-filepath="ch1/ch1-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,7 +2024,7 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="11-hello-world">1.1. Hello, World</h2>
|
||||
<p>我們以1978年,c語言歷史上經典的hello world案例來開始吧。C語言對Go語言的設計產生了很多影響。用這個例子,我們來講解一些Go語言的覈心特性:</p>
|
||||
<p>我們以1978年,c語言歷史上經典的hello world案例來開始吧。C語言對Go語言的設計産生了很多影響。用這個例子,我們來講解一些Go語言的覈心特性:</p>
|
||||
<pre><code class="lang-go"><span class="hljs-comment">//gopl.io/ch1/helloworld</span>
|
||||
<span class="hljs-keyword">package</span> main
|
||||
|
||||
@@ -2070,31 +2034,31 @@
|
||||
fmt.Println(<span class="hljs-string">"Hello, BF"</span>)
|
||||
}
|
||||
</code></pre>
|
||||
<p>Go是一門編譯型語言,Go的工具鏈將源代碼和其依賴一起打包,生成機器的本地指令(譯註:靜態編譯)。Go語言提供的工具可以通過go下的一繫列子命令來調用。最簡單的一個子命令就是run。這個命令會將一個或多個以.go結束的源文件,和關聯庫鏈接到一起,然後運行最終的可執行文件。(本書將用$錶示命令行的提示符)</p>
|
||||
<p>Go是一門編譯型語言,Go的工具鏈將源代碼和其依賴一起打包,生成機器的本地指令(譯註:靜態編譯)。Go語言提供的工具可以通過go下的一繫列子命令來調用。最簡單的一個子命令就是run。這個命令會將一個或多個以.go結束的源文件,和關聯庫鏈接到一起,然後運行最終的可執行文件。(本書將用$表示命令行的提示符)</p>
|
||||
<pre><code>$ go run helloworld.go
|
||||
</code></pre><p>毫無意外,這個命令會輸齣:</p>
|
||||
<pre><code>Hello, BF
|
||||
</code></pre><p>Go原生支持Unicode,所以你可以用Go處理世界上的任何語言。</p>
|
||||
<p>如果你希望自己的程序不隻是簡單的一次性實驗,那麼你一定會希望能夠編譯這個程序,併且能夠將編譯結果保存下來以備將來之用。這個可以用build子命令來實現:</p>
|
||||
<p>如果你希望自己的程序不隻是簡單的一次性實驗,那麽你一定會希望能夠編譯這個程序,併且能夠將編譯結果保存下來以備將來之用。這個可以用build子命令來實現:</p>
|
||||
<pre><code>$ go build helloworld.go
|
||||
</code></pre><p>這會創建一個名為helloworld的可執行的二進製文件,之後你可以在任何時間去運行這個二進製文件,不需要其它的任何處理(譯註:因為是靜態編譯,所以也不用擔心在繫統庫更新的時候衝突,倖福感滿滿)。</p>
|
||||
<p>下麫是運行我們的編譯結果樣例:</p>
|
||||
</code></pre><p>這會創建一個名爲helloworld的可執行的二進製文件,之後你可以在任何時間去運行這個二進製文件,不需要其它的任何處理(譯註:因爲是靜態編譯,所以也不用擔心在繫統庫更新的時候衝突,幸福感滿滿)。</p>
|
||||
<p>下面是運行我們的編譯結果樣例:</p>
|
||||
<pre><code>$ ./helloworld
|
||||
Hello, BF
|
||||
</code></pre><p>本書中我們所有的例子都做了一個特殊標記,你可以通過這些標記在gopl.io在綫網站上找到這些樣例代碼,比如這個 gopl.io/ch1/helloworld</p>
|
||||
<p>如果你執行go get gopl.io/ch1/helloworld,go能夠自己從網上穫取到這些代碼,併且將這些代碼放到對應的目彔中。更詳細的介紹在2.6和10.7章節中。</p>
|
||||
<p>我們來討論一下程序本身。Go的代碼是用package來組織的,package的概唸和你知道的其它語言裏的libraries或者modules比較類似。一個package會包含一個或多個.go結束的源代碼文件。每一個源文件都是以一個package xxx的聲明開頭的,比如我們的例子裏就是package main。這行聲明錶示該文件是屬於哪一個package,緊跟着是一繫列import的package名,錶示這個文件中引入的package。再之後是本文件本身的代碼</p>
|
||||
</code></pre><p>本書中我們所有的例子都做了一個特殊標記,你可以通過這些標記在gopl.io在線網站上找到這些樣例代碼,比如這個 gopl.io/ch1/helloworld</p>
|
||||
<p>如果你執行go get gopl.io/ch1/helloworld,go能夠自己從網上穫取到這些代碼,併且將這些代碼放到對應的目録中。更詳細的介紹在2.6和10.7章節中。</p>
|
||||
<p>我們來討論一下程序本身。Go的代碼是用package來組織的,package的概念和你知道的其它語言里的libraries或者modules比較類似。一個package會包含一個或多個.go結束的源代碼文件。每一個源文件都是以一個package xxx的聲明開頭的,比如我們的例子里就是package main。這行聲明表示該文件是屬於哪一個package,緊跟着是一繫列import的package名,表示這個文件中引入的package。再之後是本文件本身的代碼</p>
|
||||
<p>Go的標準庫已經提供了100多個package,用來完成一門程序語言的一些基本任務,比如輸入、輸齣、排序或者字符串/文本處理。比如fmt這個package,就包括接收輸入、格式化輸齣的各種函數。Println是其中的一個函數,可以用這個函數來打印一個或多個值,該函數會將這些參數用空格隔開進行輸齣,併在輸齣完畢之後在行末加上一個換行符。</p>
|
||||
<p>package main比較特殊。這個package裏會定義一個獨立的程序,這個程序是可以運行的,而不是像其它package一樣的library。在main這個package裏,main函數也是一個特殊的函數,這是我們整個程序的入口(譯註:其實c繫語言差不多都是這樣)。main函數所做的事情就是我們程序做的事情。當然了,main函數一般完成的工作是調用其它packge裏的函數來完成自己的工作,比如fmt.Println。</p>
|
||||
<p>我們必鬚告訴編譯器如果要正確地執行這個源文件,需要用到哪些package,這就是import在這個文件裏扮演的角色。上述的hello world隻用到了一個其它的package,就是fmt。一般情況下,需要import的package不隻一個。</p>
|
||||
<p>也正是因為go語言必鬚引入所有用到的package的原則,假如你沒有在代碼裏import需要用到的package,程序將無法編譯通過,當你import了沒有用到的package,也會無法編譯通過(譯註:爭議特性之一)。</p>
|
||||
<p>import聲明必鬚跟在文件的package聲明之後。在import之後,則是各種方法、變量、常量、類型的聲明(分彆用關鍵字func, var, const, type來進行定義)。這些內容的聲明順序併沒有什麼規定,可以隨便(譯註:最好還是定一下規範)。我們例子裏的程序比較簡單,隻包含了一個函數。併且在該函數裏也隻調用了一個其它函數。為了節省空間,有些時候的例子我們會省略package和import聲明,但是讀者需要註意這些聲明是一定要包含在源文件裏的。</p>
|
||||
<p>一個函數的聲明包含func這個關鍵字、函數名、參數列錶(我們例子裏的main函數是空)、返迴結果列錶(這裏的例子也是空)以及包含在大括號裏的函數體。關於函數的更詳細描述在第五章。</p>
|
||||
<p>Go是一門不需要分號作為語句或者聲明結束的語言,除非要在一行中將多個語句、聲明隔開。然而在編譯時,編譯器會主動在一些特定的符號(譯註:比如行末是,一個標識符、一個整數、浮點數、虛數、字符或字符串文字、關鍵字break、continue、fallthrough或return中的一個、運算符和分隔符++、--、)、]或}中的一個) 後添加分號,所以在哪裏加分號閤適是取決於Go的代碼的。例如:在Go語言中的函數聲明和 { 必鬚在衕一行,而在x + y的錶達式中,在+號後換行可以,但是在+號前換行則會有問題。</p>
|
||||
<p>Go語言在代碼格式上採取了很強硬的態度。gofmt工具會將你的代碼格式化為標準格式,併且go工具中的fmt子命令會自動對特定package下的所有.go源文件應用gofmt。如果不指定package,則默認對當前目彔下的源文件進行格式化。本書中的所有代碼已經是執行過gofmt後的標準格式代碼。你應該在自己的代碼上也執行這種格式化。規定一種標準的代碼格式可以規避掉無盡的無意義的撕逼。當然了,也可以避免由於代碼格式導緻的邏輯上的歧義。</p>
|
||||
<p>很多文本編輯器都可以設置為保存文件時自動執行gofmt,所以你的源代碼應該總是會被格式化。這裏還有一個相關的工具,goimports,會自動地添加你代碼裏需要用到的import聲明以及需要移除的import聲明。這個工具併沒有包含在標準的分髮包中,然而你可以自行安裝:</p>
|
||||
<p>package main比較特殊。這個package里會定義一個獨立的程序,這個程序是可以運行的,而不是像其它package一樣的library。在main這個package里,main函數也是一個特殊的函數,這是我們整個程序的入口(譯註:其實c繫語言差不多都是這樣)。main函數所做的事情就是我們程序做的事情。當然了,main函數一般完成的工作是調用其它packge里的函數來完成自己的工作,比如fmt.Println。</p>
|
||||
<p>我們必鬚告訴編譯器如果要正確地執行這個源文件,需要用到哪些package,這就是import在這個文件里扮演的角色。上述的hello world隻用到了一個其它的package,就是fmt。一般情況下,需要import的package不隻一個。</p>
|
||||
<p>也正是因爲go語言必鬚引入所有用到的package的原則,假如你沒有在代碼里import需要用到的package,程序將無法編譯通過,當你import了沒有用到的package,也會無法編譯通過(譯註:爭議特性之一)。</p>
|
||||
<p>import聲明必鬚跟在文件的package聲明之後。在import之後,則是各種方法、變量、常量、類型的聲明(分别用關鍵字func, var, const, type來進行定義)。這些內容的聲明順序併沒有什麽規定,可以隨便(譯註:最好還是定一下規范)。我們例子里的程序比較簡單,隻包含了一個函數。併且在該函數里也隻調用了一個其它函數。爲了節省空間,有些時候的例子我們會省略package和import聲明,但是讀者需要註意這些聲明是一定要包含在源文件里的。</p>
|
||||
<p>一個函數的聲明包含func這個關鍵字、函數名、參數列表(我們例子里的main函數是空)、返迴結果列表(這里的例子也是空)以及包含在大括號里的函數體。關於函數的更詳細描述在第五章。</p>
|
||||
<p>Go是一門不需要分號作爲語句或者聲明結束的語言,除非要在一行中將多個語句、聲明隔開。然而在編譯時,編譯器會主動在一些特定的符號(譯註:比如行末是,一個標識符、一個整數、浮點數、虛數、字符或字符串文字、關鍵字break、continue、fallthrough或return中的一個、運算符和分隔符++、--、)、]或}中的一個) 後添加分號,所以在哪里加分號合適是取決於Go的代碼的。例如:在Go語言中的函數聲明和 { 必鬚在同一行,而在x + y的表達式中,在+號後換行可以,但是在+號前換行則會有問題。</p>
|
||||
<p>Go語言在代碼格式上采取了很強硬的態度。gofmt工具會將你的代碼格式化爲標準格式,併且go工具中的fmt子命令會自動對特定package下的所有.go源文件應用gofmt。如果不指定package,則默認對當前目録下的源文件進行格式化。本書中的所有代碼已經是執行過gofmt後的標準格式代碼。你應該在自己的代碼上也執行這種格式化。規定一種標準的代碼格式可以規避掉無盡的無意義的撕逼。當然了,也可以避免由於代碼格式導致的邏輯上的歧義。</p>
|
||||
<p>很多文本編輯器都可以設置爲保存文件時自動執行gofmt,所以你的源代碼應該總是會被格式化。這里還有一個相關的工具,goimports,會自動地添加你代碼里需要用到的import聲明以及需要移除的import聲明。這個工具併沒有包含在標準的分發包中,然而你可以自行安裝:</p>
|
||||
<pre><code>$ go get golang.org/x/tools/cmd/goimports
|
||||
</code></pre><p>對於大多數用戶來說,下載、build package、運行測試用例、顯示go的文檔等等常用功能都是可以用go的工具來實現的。這些工具的詳細介紹我們會在10.7節中提到。</p>
|
||||
</code></pre><p>對於大多數用戶來説,下載、build package、運行測試用例、顯示go的文檔等等常用功能都是可以用go的工具來實現的。這些工具的詳細介紹我們會在10.7節中提到。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
152
ch1/ch1-02.html
152
ch1/ch1-02.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="1.2" data-chapter-title="命令行參數" data-filepath="ch1/ch1-02.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="1.2" data-chapter-title="命令行參數" data-filepath="ch1/ch1-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,11 +2024,11 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="12-命令行參數">1.2. 命令行參數</h2>
|
||||
<p>大多數的程序都是處理輸入,產生輸齣;這也正是“計算”的定義。但是一個程序要如何穫取輸入呢?一些程序會生成自己的數據,但通常情況下,輸入都來自於程序外部:比如文件、網絡連接、其它程序的輸齣、用戶的鍵盤、命令行的參數或其它類似輸入源。下麫幾個例子會討論其中的一些輸入類型,首先是命令行參數。</p>
|
||||
<p>大多數的程序都是處理輸入,産生輸齣;這也正是“計算”的定義。但是一個程序要如何穫取輸入呢?一些程序會生成自己的數據,但通常情況下,輸入都來自於程序外部:比如文件、網絡連接、其它程序的輸齣、用戶的鍵盤、命令行的參數或其它類似輸入源。下面幾個例子會討論其中的一些輸入類型,首先是命令行參數。</p>
|
||||
<p>os這個package提供了操作繫統無關(跨平颱)的,與繫統交互的一些函數和相關的變量,運行時程序的命令行參數可以用一個叫os包中的Args這個變量來穫取;在外部需要使用該變量時,需要用os.Args來訪問。</p>
|
||||
<p>os.Args這個變量是一個字符串(string)的slice,slice在go語言裏是一個基礎的數據結構,之後我們很快會提到。現在可以先把slice當一個簡單的元素序列,可以用類似s[i]的下標訪問形式穫取其內容,併且可以用形如s[m:n]的形式來穫取到一個slice的子集(譯註:和python裏的差不多)。其長度可以用len(s)函數來穫取。和其它大多數語言差不多,go語言裏的這種索引形式也採用了開區間,包括m~n的第一個元素,但不包括最後那個元素(譯註:比如a = [1, 2, 3, 4, 5], a[0: 3] =[1, 2, 3],不包含最後一個元素)。這樣可以簡化我們的邏輯。比如s[m:n]這個slice,0 ≤ m ≤ n ≤ len(s),包含n-m個元素。</p>
|
||||
<p>os.Args的第一個元素,卽os.Args[0]是命令行執行時的命令本身;其它的元素則是執行該命令時傳給這個程序的參數。前麫提到的切片錶達式,s[m:n]會返迴第m到第n-1個元素,所以下一個例子裏需要用到的os.Args[1:len(os.Args)]卽是除了命令本身外的所有傳入參數。如果我們省略s[m:n]裏的m和n,那麼默認這個錶達式會填入0:len(s),所以這裏我們還可以省略掉n,寫os.Args[1:]。</p>
|
||||
<p>下麫是一個Unix裏echo命令的實現,這個命令會在單行內打印齣命令行參數。這個程序import了兩個package,併且用括號把這兩個package包了起來,這是分彆import各個package聲明的簡化寫法。當然了你分開來寫import也沒有什麼問題,隻是一般為了方便我們都會像下麫這樣來導入多個package。我們自己寫的導入順序併不重要,因為gofmt工具會幫助我們按照字母順序來排列好這些導入包名。(本書中如果一個例子有多種版本時,我們會用編號標記齣來)</p>
|
||||
<p>os.Args這個變量是一個字符串(string)的slice,slice在go語言里是一個基礎的數據結構,之後我們很快會提到。現在可以先把slice當一個簡單的元素序列,可以用類似s[i]的下標訪問形式穫取其內容,併且可以用形如s[m:n]的形式來穫取到一個slice的子集(譯註:和python里的差不多)。其長度可以用len(s)函數來穫取。和其它大多數語言差不多,go語言里的這種索引形式也采用了開區間,包括m~n的第一個元素,但不包括最後那個元素(譯註:比如a = [1, 2, 3, 4, 5], a[0: 3] =[1, 2, 3],不包含最後一個元素)。這樣可以簡化我們的邏輯。比如s[m:n]這個slice,0 ≤ m ≤ n ≤ len(s),包含n-m個元素。</p>
|
||||
<p>os.Args的第一個元素,卽os.Args[0]是命令行執行時的命令本身;其它的元素則是執行該命令時傳給這個程序的參數。前面提到的切片表達式,s[m:n]會返迴第m到第n-1個元素,所以下一個例子里需要用到的os.Args[1:len(os.Args)]卽是除了命令本身外的所有傳入參數。如果我們省略s[m:n]里的m和n,那麽默認這個表達式會填入0:len(s),所以這里我們還可以省略掉n,寫os.Args[1:]。</p>
|
||||
<p>下面是一個Unix里echo命令的實現,這個命令會在單行內打印齣命令行參數。這個程序import了兩個package,併且用括號把這兩個package包了起來,這是分别import各個package聲明的簡化寫法。當然了你分開來寫import也沒有什麽問題,隻是一般爲了方便我們都會像下面這樣來導入多個package。我們自己寫的導入順序併不重要,因爲gofmt工具會幫助我們按照字母順序來排列好這些導入包名。(本書中如果一個例子有多種版本時,我們會用編號標記齣來)</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/echo1
|
||||
<span class="hljs-comment">// Echo1 prints its command-line arguments.</span>
|
||||
<span class="hljs-keyword">package</span> main
|
||||
@@ -2081,43 +2045,43 @@
|
||||
fmt.Println(s)
|
||||
}
|
||||
</code></pre>
|
||||
<p>Go裏的註釋是以//來錶示。//後的內容一直到行末都是這條註釋的一部分,併且這些註釋會被編譯器忽略。</p>
|
||||
<p>按照慣例,我們會在每一個package前麫放上這個package的詳盡的註釋對其進行說明;對於一個main package來說,一般這段評論會包含幾句話來說明這個項目/程序整體是做什麼用的。</p>
|
||||
<p>var關鍵字用來做變量聲明。這裏聲明了s和sep兩個string變量。變量可以在聲明期間直接進行初始化。如果沒有顯式地初始化的話,Go會隱式地給這些未初始化的變量賦予對應其類型的零值,比如數值類型就是0,字符串類型就是“”空字符串。在這個例子裏的s和sep被隱式地賦值為了空字符串。在第2章中我們會更詳細地講解變量和聲明。</p>
|
||||
<p>對於數字類型,Go語言提供了常規的數值計算和邏輯運算符。而對於string類型,+號錶示字符串的連接(譯註:和C++或者js是一樣的)。所以下麫這個錶達式:</p>
|
||||
<p>Go里的註釋是以//來表示。//後的內容一直到行末都是這條註釋的一部分,併且這些註釋會被編譯器忽略。</p>
|
||||
<p>按照慣例,我們會在每一個package前面放上這個package的詳盡的註釋對其進行説明;對於一個main package來説,一般這段評論會包含幾句話來説明這個項目/程序整體是做什麽用的。</p>
|
||||
<p>var關鍵字用來做變量聲明。這里聲明了s和sep兩個string變量。變量可以在聲明期間直接進行初始化。如果沒有顯式地初始化的話,Go會隱式地給這些未初始化的變量賦予對應其類型的零值,比如數值類型就是0,字符串類型就是“”空字符串。在這個例子里的s和sep被隱式地賦值爲了空字符串。在第2章中我們會更詳細地講解變量和聲明。</p>
|
||||
<p>對於數字類型,Go語言提供了常規的數值計算和邏輯運算符。而對於string類型,+號表示字符串的連接(譯註:和C++或者js是一樣的)。所以下面這個表達式:</p>
|
||||
<pre><code class="lang-go">sep + os.Args[i]
|
||||
</code></pre>
|
||||
<p>錶示將sep字符串和os.Args[i]字符串進行連接。我們在程序裏用的另外一個錶達式:</p>
|
||||
<p>表示將sep字符串和os.Args[i]字符串進行連接。我們在程序里用的另外一個表達式:</p>
|
||||
<pre><code class="lang-go">s += sep + os.Args[i]
|
||||
</code></pre>
|
||||
<p>會將sep與os.Args[i]連接,然後再將得到的結果與s進行連接,這種方式和下麫的錶達是等價的:</p>
|
||||
<p>會將sep與os.Args[i]連接,然後再將得到的結果與s進行連接,這種方式和下面的表達是等價的:</p>
|
||||
<pre><code class="lang-go">s = s + sep + os.Args[i]
|
||||
</code></pre>
|
||||
<p>運算符+=是一個賦值運算符(assignment operator),每一種數值和邏輯運算符,例如*或者+都有其對應的賦值運算符。</p>
|
||||
<p>echo程序可以每循環一次輸齣一個參數,不過我們這裏的版本是不斷地將其結果連接到一個字符串的末尾。s這個字符串在聲明的時候是一個空字符串,而之後循環每次都會被在末尾添加一段字符串;第一次迭代之後,一個空格會被插入到字符串末尾,所以每插入一個新值,都會和前一個中間有一個空格隔開。這是一種非綫性的操作,當我們的參數數量變得龐大的時候(當然不是說這裏的echo,一般echo也不會有太多參數)其運行開銷也會變得龐大。下麫我們會介紹一繫列的echo改進版,來應對這裏說到的運行效率低下。</p>
|
||||
<p>在for循環中,我們用到了i來做下標索引,可以看到我們用了:=符號來給i進行初始化和賦值,這是var xxx=yyy的一種簡寫形式,Go會根據等號右邊的值的類型自動判斷左邊的值類型,下一章會對這一點進行詳細說明。</p>
|
||||
<p>自增錶達式i++會為i加上1;這個i += 1以及i = i + 1都是等價的。對應的還有i--是給i減去1。這些在go語言裏是語句,而不像C繫的其它語言裏是錶達式。所以在Go語言裏j = i++是非法的,而且++和--都隻能放在變量名後麫,因此--i也是非法的。</p>
|
||||
<p>在Go語言裏隻有for循環一種循環。當然了為了滿足需求,Go的for循環有很多種形式,下麫是其中的一種:</p>
|
||||
<p>echo程序可以每循環一次輸齣一個參數,不過我們這里的版本是不斷地將其結果連接到一個字符串的末尾。s這個字符串在聲明的時候是一個空字符串,而之後循環每次都會被在末尾添加一段字符串;第一次迭代之後,一個空格會被插入到字符串末尾,所以每插入一個新值,都會和前一個中間有一個空格隔開。這是一種非線性的操作,當我們的參數數量變得龐大的時候(當然不是説這里的echo,一般echo也不會有太多參數)其運行開銷也會變得龐大。下面我們會介紹一繫列的echo改進版,來應對這里説到的運行效率低下。</p>
|
||||
<p>在for循環中,我們用到了i來做下標索引,可以看到我們用了:=符號來給i進行初始化和賦值,這是var xxx=yyy的一種簡寫形式,Go會根據等號右邊的值的類型自動判斷左邊的值類型,下一章會對這一點進行詳細説明。</p>
|
||||
<p>自增表達式i++會爲i加上1;這個i += 1以及i = i + 1都是等價的。對應的還有i--是給i減去1。這些在go語言里是語句,而不像C繫的其它語言里是表達式。所以在Go語言里j = i++是非法的,而且++和--都隻能放在變量名後面,因此--i也是非法的。</p>
|
||||
<p>在Go語言里隻有for循環一種循環。當然了爲了滿足需求,Go的for循環有很多種形式,下面是其中的一種:</p>
|
||||
<pre><code class="lang-go"><span class="hljs-keyword">for</span> initialization; condition; post {
|
||||
<span class="hljs-comment">// zero or more statements</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>這裏需要註意,for循環的兩邊是不需要像其它語言一樣寫括號的。併且左大括號需要和for語句在衕一行。</p>
|
||||
<p>initialization部分是可選的,如果你寫了這部分的話,在for循環之前這部分的邏輯會被執行。需要註意的是這部分必鬚是一個簡單的語句,也就是說是一個簡短的變量聲明,一個賦值語句,或是一個函數調用。condition部分必鬚是一個結果為boolean值的錶達式,在每次循環之前,語言都會檢査當前是否滿足這個條件,如果不滿足的話便會結束循環;post部分的語句則是在每次循環結束之後被執行,之後conditon部分會在下一次執行前再被執行,依此往復。當condition條件裏的判斷結果變為false之後,循環卽結束。</p>
|
||||
<p>上麫提到是for循環裏的三個部分都是可以被省略的,如果你把initialization和post部分都省略的話,那麼連中間隔離他們的分號也是可以被省略的,比如下麫這種for循環,就和傳統的while循環是一樣的:</p>
|
||||
<p>這里需要註意,for循環的兩邊是不需要像其它語言一樣寫括號的。併且左大括號需要和for語句在同一行。</p>
|
||||
<p>initialization部分是可選的,如果你寫了這部分的話,在for循環之前這部分的邏輯會被執行。需要註意的是這部分必鬚是一個簡單的語句,也就是説是一個簡短的變量聲明,一個賦值語句,或是一個函數調用。condition部分必鬚是一個結果爲boolean值的表達式,在每次循環之前,語言都會檢査當前是否滿足這個條件,如果不滿足的話便會結束循環;post部分的語句則是在每次循環結束之後被執行,之後conditon部分會在下一次執行前再被執行,依此往複。當condition條件里的判斷結果變爲false之後,循環卽結束。</p>
|
||||
<p>上面提到是for循環里的三個部分都是可以被省略的,如果你把initialization和post部分都省略的話,那麽連中間隔離他們的分號也是可以被省略的,比如下面這種for循環,就和傳統的while循環是一樣的:</p>
|
||||
<pre><code class="lang-go"><span class="hljs-comment">// a traditional "while" loop</span>
|
||||
<span class="hljs-keyword">for</span> condition {
|
||||
<span class="hljs-comment">// ...</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>當然了,如果你連唯一的條件都省了,那麼for循環就會變成一個無限循環,像下麫這樣:</p>
|
||||
<p>當然了,如果你連唯一的條件都省了,那麽for循環就會變成一個無限循環,像下面這樣:</p>
|
||||
<pre><code class="lang-go"><span class="hljs-comment">// a traditional infinite loop</span>
|
||||
<span class="hljs-keyword">for</span> {
|
||||
<span class="hljs-comment">// ...</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>在無限循環中,你還是可以靠break或者return來終止掉循環。</p>
|
||||
<p>如果你的遍歷對象是string或者slice裏的值的話,還有另外一種循環的寫法,我們來看看另一個版本的echo:</p>
|
||||
<p>如果你的遍歷對象是string或者slice里的值的話,還有另外一種循環的寫法,我們來看看另一個版本的echo:</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/echo2
|
||||
<span class="hljs-comment">// Echo2 prints its command-line arguments.</span>
|
||||
<span class="hljs-keyword">package</span> main
|
||||
@@ -2135,31 +2099,31 @@
|
||||
fmt.Println(s)
|
||||
}
|
||||
</code></pre>
|
||||
<p>每一次循環迭代,range都會返迴一對結果;當前迭代的下標以及在該下標處的元素的值。在這個例子裏,我們不需要這個下標,但是因為range的處理要求我們必鬚要衕時處理下標和值。我們可以在這裏聲明一個接收index的臨時變量來解決這個問題,但是go語言又不允許隻聲明而在後續代碼裏不使用這個變量,如果你這樣做了編譯器會返迴一個編譯錯誤。</p>
|
||||
<p>在Go語言中,應對這種情況的解決方法是用空白標識符,對,就是上麫那個下劃綫<em>。空白標識符可以在任何你接收自己不需要處理的值時使用。在這裏,我們用他來忽略掉range返迴的那個沒用的下標值。大多數的Go程序員都會像上麫這樣來寫類似的os.Args遍歷,可以避免錯誤的下標引用。(這裏可能有翻譯錯,附上原文)
|
||||
<p>每一次循環迭代,range都會返迴一對結果;當前迭代的下標以及在該下標處的元素的值。在這個例子里,我們不需要這個下標,但是因爲range的處理要求我們必鬚要同時處理下標和值。我們可以在這里聲明一個接收index的臨時變量來解決這個問題,但是go語言又不允許隻聲明而在後續代碼里不使用這個變量,如果你這樣做了編譯器會返迴一個編譯錯誤。</p>
|
||||
<p>在Go語言中,應對這種情況的解決方法是用空白標識符,對,就是上面那個下劃線<em>。空白標識符可以在任何你接收自己不需要處理的值時使用。在這里,我們用他來忽略掉range返迴的那個沒用的下標值。大多數的Go程序員都會像上面這樣來寫類似的os.Args遍歷,可以避免錯誤的下標引用。(這里可能有翻譯錯,附上原文)
|
||||
Most Go programmers would likely use range and </em> to write the echo program as above, since the indexing over os.Args is implicit, not explicit, and thus easier to get right.</p>
|
||||
<p>上麫這個版本將s和sep的聲明和初始化都放到了一起,但是我們可以等價地將聲明和賦值分開來寫,下麫這些寫法都是等價的</p>
|
||||
<p>上面這個版本將s和sep的聲明和初始化都放到了一起,但是我們可以等價地將聲明和賦值分開來寫,下面這些寫法都是等價的</p>
|
||||
<pre><code class="lang-go">s := <span class="hljs-string">""</span>
|
||||
<span class="hljs-keyword">var</span> s <span class="hljs-typename">string</span>
|
||||
<span class="hljs-keyword">var</span> s = <span class="hljs-string">""</span>
|
||||
<span class="hljs-keyword">var</span> s <span class="hljs-typename">string</span> = <span class="hljs-string">""</span>
|
||||
</code></pre>
|
||||
<p>那麼這些等價的形式應該怎麼做選擇呢?這裏提供一些建議:第一種形式,最好隻用在一個函數內部,而package級彆的變量,請不要使用這樣的聲明方式。第二種形式依賴於string類型的內部初始化機製,被初始化為空字符串。第三種形式使用得很少,除非衕時聲明多個變量。第四種形式會顯式地標明變量的類型,在多變量衕時聲明時可以用到。實踐中你應該隻使用上麫的前兩種形式,顯式地指定變量的類型,讓編譯器自己去初始化其值,或者直接用隱式初始化,錶明初始值怎麼樣併不重要。</p>
|
||||
<p>像上麫提到的,每次循環中字符串s都會得到一個新內容。+=語句會分配一個新的字符串,併將老字符串連接起來的值賦予給它。而目標字符串的老字麫值在得到新值以後就失去了用處,這些臨時值會被go的垃圾收集器幹掉。</p>
|
||||
<p>如果不斷連接的數據量很大,那麼上麫這種操作就是成本非常高的操作。更簡單併且有效的一種方式是使用字符串的Join函數,像下麫這樣:</p>
|
||||
<p>那麽這些等價的形式應該怎麽做選擇呢?這里提供一些建議:第一種形式,最好隻用在一個函數內部,而package級别的變量,請不要使用這樣的聲明方式。第二種形式依賴於string類型的內部初始化機製,被初始化爲空字符串。第三種形式使用得很少,除非同時聲明多個變量。第四種形式會顯式地標明變量的類型,在多變量同時聲明時可以用到。實踐中你應該隻使用上面的前兩種形式,顯式地指定變量的類型,讓編譯器自己去初始化其值,或者直接用隱式初始化,表明初始值怎麽樣併不重要。</p>
|
||||
<p>像上面提到的,每次循環中字符串s都會得到一個新內容。+=語句會分配一個新的字符串,併將老字符串連接起來的值賦予給它。而目標字符串的老字面值在得到新值以後就失去了用處,這些臨時值會被go的垃圾收集器榦掉。</p>
|
||||
<p>如果不斷連接的數據量很大,那麽上面這種操作就是成本非常高的操作。更簡單併且有效的一種方式是使用字符串的Join函數,像下面這樣:</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/echo3
|
||||
<span class="hljs-keyword">func</span> main() {
|
||||
fmt.Println(strings.Join(os.Args[<span class="hljs-number">1</span>:], <span class="hljs-string">" "</span>))
|
||||
}
|
||||
</code></pre>
|
||||
<p>最後,如果我們對輸齣的格式也不是很關心,隻是想簡單地輸齣值得的話,還可以像下麫這麼寫,Println函數會為我們自動格式化輸齣。</p>
|
||||
<p>最後,如果我們對輸齣的格式也不是很關心,隻是想簡單地輸齣值得的話,還可以像下面這麽寫,Println函數會爲我們自動格式化輸齣。</p>
|
||||
<pre><code class="lang-go">fmt.Println(os.Args[<span class="hljs-number">1</span>:])
|
||||
</code></pre>
|
||||
<p>這個輸齣結果和前麫的string.Join得到的結果很相似,隻是被自動地放到了一個括號裏,對slice調用Println函數都會被打印成這樣形式的結果。</p>
|
||||
<p>下麫是幾道練習題:</p>
|
||||
<p>這個輸齣結果和前面的string.Join得到的結果很相似,隻是被自動地放到了一個括號里,對slice調用Println函數都會被打印成這樣形式的結果。</p>
|
||||
<p>下面是幾道練習題:</p>
|
||||
<pre><code>Exercise 1.1:脩改echo程序,使其能夠打印os.Args[0]。
|
||||
Exercise 1.2:脩改echo程序,使其打印value和index,每個value和index顯示一行。
|
||||
Exercise 1.3:上手實踐前麫提到的strings.Join和直接Println,併觀察輸齣結果的區彆。
|
||||
Exercise 1.3:上手實踐前面提到的strings.Join和直接Println,併觀察輸齣結果的區别。
|
||||
</code></pre>
|
||||
|
||||
</section>
|
||||
@@ -2173,7 +2137,7 @@ Exercise 1.3:上手實踐前麫提到&#x
|
||||
<a href="../ch1/ch1-01.html" class="navigation navigation-prev " aria-label="Previous page: Hello, World"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../ch1/ch1-03.html" class="navigation navigation-next " aria-label="Next page: 査找重復的行"><i class="fa fa-angle-right"></i></a>
|
||||
<a href="../ch1/ch1-03.html" class="navigation navigation-next " aria-label="Next page: 査找重複的行"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
144
ch1/ch1-03.html
144
ch1/ch1-03.html
@@ -5,7 +5,7 @@
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>査找重復的行 | Go编程语言</title>
|
||||
<title>査找重複的行 | Go编程语言</title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="generator" content="GitBook 2.5.2">
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="1.3" data-chapter-title="査找重復的行" data-filepath="ch1/ch1-03.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="1.3" data-chapter-title="査找重複的行" data-filepath="ch1/ch1-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2059,9 +2023,9 @@
|
||||
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="13-査找重復的行">1.3. 査找重復的行</h2>
|
||||
<p>文件拷貝、文件打印、文件蒐索、文件排序、文件統計類的程序一般都會有比較相似的程序結構:處理輸入的一個循環,在每一個輸入元素上執行計算處理,在處理的衕時或者處理完成之後進行結果輸齣。我們會展示一個叫dup程序的三個版本;這個程序的靈感來自於linux的uniq命令,我們的程序將會找到相鄰的重復的行。這個程序提供的模式可以很方便地被脩改來完成不衕的需求。</p>
|
||||
<p>第一個版本的dup會輸齣標準輸入流中的齣現多次的行,在行內容前會有其齣現次數的計數。這個程序將引入if錶達式,map內置數據結果和bufio的package。</p>
|
||||
<h2 id="13-査找重複的行">1.3. 査找重複的行</h2>
|
||||
<p>文件拷貝、文件打印、文件蒐索、文件排序、文件統計類的程序一般都會有比較相似的程序結構:處理輸入的一個循環,在每一個輸入元素上執行計算處理,在處理的同時或者處理完成之後進行結果輸齣。我們會展示一個叫dup程序的三個版本;這個程序的靈感來自於linux的uniq命令,我們的程序將會找到相鄰的重複的行。這個程序提供的模式可以很方便地被脩改來完成不同的需求。</p>
|
||||
<p>第一個版本的dup會輸齣標準輸入流中的齣現多次的行,在行內容前會有其齣現次數的計數。這個程序將引入if表達式,map內置數據結果和bufio的package。</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/dup1
|
||||
<span class="hljs-comment">// Dup1 prints the text of each line that appears more than</span>
|
||||
<span class="hljs-comment">// once in the standard input, preceded by its count.</span>
|
||||
@@ -2087,32 +2051,32 @@
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>和我們前麫提到的for循環一樣,在if條件的兩邊,我們也不需要加括號,但是if錶達式後的邏輯體的花括號是不能省略的。如果需要的話,像其它語言一樣,這個錶達式也可以有else部分,這部分邏輯會在if中的條件結果為false時被執行。</p>
|
||||
<p>map是go語言內置的key/value數據結構,這個數據結構能夠提供常數時間的存儲、穫取、測試操作。key可以是任意數據類型,隻要該類型能夠用==來進行比較,string是最常用的key類型。而value類型的範圍就更大了,基本上什麼類型都是可以的。這個例子中的key都是string類型,value用的是int類型。我們用內置make函數來創建一個空的map,當然了,make方法還可以有彆的用處。在4.3章中我們還會對map進行更深度的討論。</p>
|
||||
<p>dup程序每次讀取輸入的一行,這一行的內容會被當做一個map的key,而其value值會被+1。counts[input.Text()]++這個語句和下麫的兩句是等價的:</p>
|
||||
<p>和我們前面提到的for循環一樣,在if條件的兩邊,我們也不需要加括號,但是if表達式後的邏輯體的花括號是不能省略的。如果需要的話,像其它語言一樣,這個表達式也可以有else部分,這部分邏輯會在if中的條件結果爲false時被執行。</p>
|
||||
<p>map是go語言內置的key/value數據結構,這個數據結構能夠提供常數時間的存儲、穫取、測試操作。key可以是任意數據類型,隻要該類型能夠用==來進行比較,string是最常用的key類型。而value類型的范圍就更大了,基本上什麽類型都是可以的。這個例子中的key都是string類型,value用的是int類型。我們用內置make函數來創建一個空的map,當然了,make方法還可以有别的用處。在4.3章中我們還會對map進行更深度的討論。</p>
|
||||
<p>dup程序每次讀取輸入的一行,這一行的內容會被當做一個map的key,而其value值會被+1。counts[input.Text()]++這個語句和下面的兩句是等價的:</p>
|
||||
<pre><code class="lang-go">line := input.Text()
|
||||
counts[line] = counts[line] + <span class="hljs-number">1</span>
|
||||
</code></pre>
|
||||
<p>當然了,在這個例子裏我們併不用擔心map在沒有當前的key時就對其進行++操作會有什麼問題,因為go語言在碰到這種情況時,會自動將其初始化為0,然後再進行操作。</p>
|
||||
<p>在這裏我們又用了一個range的循環來打印結果,這次range是被用在map這個數據結果上。這一次的情況和上次比較類型,range會返迴兩個值,一個key和在map對應這個key的value。對map進行range循環時,其順序是不確定的,從實踐來看,很可能每次運行都會有不一樣的結果(譯註:這是go的設計者有意為之的,因為其底層實現不保證插入順序和遍歷順序一緻,而希望程序員不要依賴遍歷時的順序,所以幹脆直接在遍歷的時候做了隨機化處理,醉了),來避免程序員在業務中依賴遍歷時的順序。</p>
|
||||
<p>當然了,在這個例子里我們併不用擔心map在沒有當前的key時就對其進行++操作會有什麽問題,因爲go語言在碰到這種情況時,會自動將其初始化爲0,然後再進行操作。</p>
|
||||
<p>在這里我們又用了一個range的循環來打印結果,這次range是被用在map這個數據結果上。這一次的情況和上次比較類型,range會返迴兩個值,一個key和在map對應這個key的value。對map進行range循環時,其順序是不確定的,從實踐來看,很可能每次運行都會有不一樣的結果(譯註:這是go的設計者有意爲之的,因爲其底層實現不保證插入順序和遍歷順序一致,而希望程序員不要依賴遍歷時的順序,所以榦脆直接在遍歷的時候做了隨機化處理,醉了),來避免程序員在業務中依賴遍歷時的順序。</p>
|
||||
<p>然後輪到我們例子中的bufio這個package了,這個package主要的目的是幫助我們更方便有效地處理程序的輸入和輸齣。而這個包最有用的一個特性就是其中的一個Scanner類型,用它可以簡單地接收輸入,或者把輸入打散成行或者單詞;這個類型通常是處理行形式的輸入最簡單的方法了。</p>
|
||||
<p>本程序中用了一個短變量聲明,來創建一個buffio.Scanner對象:</p>
|
||||
<pre><code>input := bufio.NewScanner(os.Stdin)
|
||||
</code></pre><p>scanner對象可以從程序的標準輸入中讀取內容。對input.Scanner的每一次調用都會調入一個新行,併且會自動將其行末的換行符去掉;其結果可以用input.Text()得到。Scan方法在讀到了新行的時候會返迴true,而在沒有新行被讀入時,會返迴false。</p>
|
||||
<p>例子中還有一個fmt.Printf,這個函數和C繫的其它語言裏的那個printf函數差不多,都是格式化輸齣的方法。fmt.Printf的第一個參數卽是輸齣內容的格式規約,每一個參數如果格式化是取決於在格式化字符串裏齣現的“轉換字符”,這個字符串是跟着%號後的一個字母。比如%d錶示以一個整數的形式來打印一個變量,而%s,則錶示以string形式來打印一個變量。</p>
|
||||
<p>Printf有一大堆這種轉換,Go程序員把這些叫做verb(動詞)。下麫的錶格列齣了常用的動詞,當然了不是全部,但基本也夠用了。</p>
|
||||
<p>例子中還有一個fmt.Printf,這個函數和C繫的其它語言里的那個printf函數差不多,都是格式化輸齣的方法。fmt.Printf的第一個參數卽是輸齣內容的格式規約,每一個參數如果格式化是取決於在格式化字符串里齣現的“轉換字符”,這個字符串是跟着%號後的一個字母。比如%d表示以一個整數的形式來打印一個變量,而%s,則表示以string形式來打印一個變量。</p>
|
||||
<p>Printf有一大堆這種轉換,Go程序員把這些叫做verb(動詞)。下面的表格列齣了常用的動詞,當然了不是全部,但基本也夠用了。</p>
|
||||
<pre><code>%d int變量
|
||||
%x, %o, %b 分彆為16進製,8進製,2進製形式的int
|
||||
%x, %o, %b 分别爲16進製,8進製,2進製形式的int
|
||||
%f, %g, %e 浮點數: 3.141593 3.141592653589793 3.141593e+00
|
||||
%t 佈爾變量:true 或 false
|
||||
%c rune (Unicode code point),go語言裏特有的Unicode字符類型
|
||||
%t 布爾變量:true 或 false
|
||||
%c rune (Unicode code point),go語言里特有的Unicode字符類型
|
||||
%s string
|
||||
%q quoted string "abc" or rune 'c'
|
||||
%v 會將任意變量以易讀的形式打印齣來
|
||||
%T 打印變量的類型
|
||||
%% 字符型百分比標誌(不確定) literal percent sign (no operand)
|
||||
</code></pre><p>dup1中的程序還包含了一個\t和\n的格式化字符串。在字符串中會以這些特殊的轉義字符來錶示不可見字符。Printf默認不會在輸齣內容後加上換行符。按照慣例,用來格式化的函數都會在末尾以f字母結尾,比如log.Printf,fmt.Errorf,衕時還有一繫列對應以ln結尾的函數,這些函數默認以%v來格式化他們的參數,併且會在輸齣結束後在最後自動加上一個換行符。</p>
|
||||
<p>許多程序從標準輸入中讀取數據,像上麫的例子那樣。除此之外,還可能從一繫列的文件中讀取。下一個dup程序就是從標準輸入中讀到一些文件名,用os.Open函數來打開每一個文件穫取內容的。</p>
|
||||
</code></pre><p>dup1中的程序還包含了一個\t和\n的格式化字符串。在字符串中會以這些特殊的轉義字符來表示不可見字符。Printf默認不會在輸齣內容後加上換行符。按照慣例,用來格式化的函數都會在末尾以f字母結尾,比如log.Printf,fmt.Errorf,同時還有一繫列對應以ln結尾的函數,這些函數默認以%v來格式化他們的參數,併且會在輸齣結束後在最後自動加上一個換行符。</p>
|
||||
<p>許多程序從標準輸入中讀取數據,像上面的例子那樣。除此之外,還可能從一繫列的文件中讀取。下一個dup程序就是從標準輸入中讀到一些文件名,用os.Open函數來打開每一個文件穫取內容的。</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/dup2
|
||||
<span class="hljs-comment">// Dup2 prints the count and text of lines that appear more than once</span>
|
||||
<span class="hljs-comment">// in the input. It reads from stdin or from a list of named files.</span>
|
||||
@@ -2155,13 +2119,13 @@ counts[line] = counts[line] + <span class="hljs-number">1</span>
|
||||
<span class="hljs-comment">// <span class="hljs-doctag">NOTE:</span> ignoring potential errors from input.Err()</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>os.Open函數會返迴兩個值。第一個值是一個打開的文件類型(*os.File),這個對象在下麫的程序中被Scanner讀取。</p>
|
||||
<p>os.Open返迴的第二個值是一個go內置的error類型。如果這個error和內置值的nil(譯註:相當於其它語言裏的NULL)相等的話,說明文件被成功的打開了。之後文件被讀取,一直到文件的最後,Close函數關閉該文件,併釋放相應的佔用一切資源。另一方麫,如果err的值不是nil的話,那說明在打開文件的時候齣了某種錯誤。這種情況下,error類型的值會描述具體的問題。我們例子裏的簡單錯誤處理會在標準錯誤流中用Fprintf和%v來格式化該錯誤字符串。然後繼續處理下一個文件;continue語句會直接跳過之後的語句,直接開始執行下一次循環。</p>
|
||||
<p>我們在本書中早期的例子中做了比較詳盡的錯誤處理,當然了,在實際編碼過程中,像os.Open這類的函數是一定要檢査其返迴的error值的;為了減少例子程序的代碼量,我們姑且簡化掉這些不太可能返迴錯誤的邏輯。後麫的例子裏我們會跳過錯誤檢査。在5.4節中我們會對錯誤處理做更詳細的闡述。</p>
|
||||
<p>讀者可以再觀察一下上麫的例子,我們的countLines函數是在其聲明之前就被調用了。在Go語言裏,函數和包級彆的變量可以以任意的順序被聲明,併不影響其被調用。(譯註:最好還是遵循一定的規範)</p>
|
||||
<p>再來講講map這個數據結構,map是用make函數創建的數據結構的一個引用。當一個map被作為參數傳遞給一個函數時,函數接收到的是一份引用的拷貝,雖然本身併不是一個東西,但因為他們指曏的是衕一塊數據對象(譯註:類似於C艹裏的引用傳遞),所以你在函數裏對map裏的值進行脩改時,原始的map內的值也會改變。在我們的例子中,我們在countLines函數中插入到counts這個map裏的值,在主函數中也是看得到的。</p>
|
||||
<p>上麫這個版本的dup是以流的形式來處理輸入,併將其打散為行。理論上這些程序也是可以以二進製形式來處理輸入的。我們也可以一次性的把整個輸入內容全部讀到內存中,然後再把其分割為多行,然後再去處理這些行內的數據。下麫的dup3這個例子就是以這種形式來進行操作的。這個例子引入了一個新函數ReadFile(從io/ioutil這個包),這個函數會把一個指定名字的文件內容一次性調入,之後我們用strings.Split函數把文件分割為多個子字符串,併存儲到slice結構中。(Split函數是strings.Join的逆函數,Join函數之前提到過)</p>
|
||||
<p>我們簡化了dup3這個程序。首先,他隻讀取命名的文件,而不去讀標準輸入,因為ReadFile函數需要一個文件名參數。其次,我們將行計數邏輯移迴到了main函數,因為現在這個邏輯隻有一個地方需要用到。</p>
|
||||
<p>os.Open函數會返迴兩個值。第一個值是一個打開的文件類型(*os.File),這個對象在下面的程序中被Scanner讀取。</p>
|
||||
<p>os.Open返迴的第二個值是一個go內置的error類型。如果這個error和內置值的nil(譯註:相當於其它語言里的NULL)相等的話,説明文件被成功的打開了。之後文件被讀取,一直到文件的最後,Close函數關閉該文件,併釋放相應的占用一切資源。另一方面,如果err的值不是nil的話,那説明在打開文件的時候齣了某種錯誤。這種情況下,error類型的值會描述具體的問題。我們例子里的簡單錯誤處理會在標準錯誤流中用Fprintf和%v來格式化該錯誤字符串。然後繼續處理下一個文件;continue語句會直接跳過之後的語句,直接開始執行下一次循環。</p>
|
||||
<p>我們在本書中早期的例子中做了比較詳盡的錯誤處理,當然了,在實際編碼過程中,像os.Open這類的函數是一定要檢査其返迴的error值的;爲了減少例子程序的代碼量,我們姑且簡化掉這些不太可能返迴錯誤的邏輯。後面的例子里我們會跳過錯誤檢査。在5.4節中我們會對錯誤處理做更詳細的闡述。</p>
|
||||
<p>讀者可以再觀察一下上面的例子,我們的countLines函數是在其聲明之前就被調用了。在Go語言里,函數和包級别的變量可以以任意的順序被聲明,併不影響其被調用。(譯註:最好還是遵循一定的規范)</p>
|
||||
<p>再來講講map這個數據結構,map是用make函數創建的數據結構的一個引用。當一個map被作爲參數傳遞給一個函數時,函數接收到的是一份引用的拷貝,雖然本身併不是一個東西,但因爲他們指向的是同一塊數據對象(譯註:類似於C艹里的引用傳遞),所以你在函數里對map里的值進行脩改時,原始的map內的值也會改變。在我們的例子中,我們在countLines函數中插入到counts這個map里的值,在主函數中也是看得到的。</p>
|
||||
<p>上面這個版本的dup是以流的形式來處理輸入,併將其打散爲行。理論上這些程序也是可以以二進製形式來處理輸入的。我們也可以一次性的把整個輸入內容全部讀到內存中,然後再把其分割爲多行,然後再去處理這些行內的數據。下面的dup3這個例子就是以這種形式來進行操作的。這個例子引入了一個新函數ReadFile(從io/ioutil這個包),這個函數會把一個指定名字的文件內容一次性調入,之後我們用strings.Split函數把文件分割爲多個子字符串,併存儲到slice結構中。(Split函數是strings.Join的逆函數,Join函數之前提到過)</p>
|
||||
<p>我們簡化了dup3這個程序。首先,他隻讀取命名的文件,而不去讀標準輸入,因爲ReadFile函數需要一個文件名參數。其次,我們將行計數邏輯移迴到了main函數,因爲現在這個邏輯隻有一個地方需要用到。</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/dup3
|
||||
<span class="hljs-keyword">package</span> main
|
||||
|
||||
@@ -2191,9 +2155,9 @@ counts[line] = counts[line] + <span class="hljs-number">1</span>
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>ReadFile函數返迴一個byte的slice,這個slice必鬚被轉換為string,之後纔能夠用string.Split方法來進行處理。我們在3.5.4節中會更詳細地講解string和byte slice(字節數組)。</p>
|
||||
<p>ReadFile函數返迴一個byte的slice,這個slice必鬚被轉換爲string,之後纔能夠用string.Split方法來進行處理。我們在3.5.4節中會更詳細地講解string和byte slice(字節數組)。</p>
|
||||
<p>在更底層一些的地方,bufio.Scanner,ioutil.ReadFile和ioutil.WriteFile使用的是*os.File的Read和Write方法,不過一般程序員併不需要去直接了解到其底層實現細節,在bufio和io/ioutil包中提供的方法已經足夠好用。</p>
|
||||
<pre><code>Exercise 1.4: 脩改dup2,使其可以打印重復的行分彆齣現在哪些文件。
|
||||
<pre><code>Exercise 1.4: 脩改dup2,使其可以打印重複的行分别齣現在哪些文件。
|
||||
</code></pre>
|
||||
|
||||
</section>
|
||||
@@ -2207,7 +2171,7 @@ counts[line] = counts[line] + <span class="hljs-number">1</span>
|
||||
<a href="../ch1/ch1-02.html" class="navigation navigation-prev " aria-label="Previous page: 命令行參數"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../ch1/ch1-04.html" class="navigation navigation-next " aria-label="Next page: GIF動畫"><i class="fa fa-angle-right"></i></a>
|
||||
<a href="../ch1/ch1-04.html" class="navigation navigation-next " aria-label="Next page: GIF動畵"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
118
ch1/ch1-04.html
118
ch1/ch1-04.html
@@ -5,7 +5,7 @@
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>GIF動畫 | Go编程语言</title>
|
||||
<title>GIF動畵 | Go编程语言</title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="generator" content="GitBook 2.5.2">
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="1.4" data-chapter-title="GIF動畫" data-filepath="ch1/ch1-04.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="1.4" data-chapter-title="GIF動畵" data-filepath="ch1/ch1-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2059,10 +2023,10 @@
|
||||
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="14-gif動畫">1.4. GIF動畫</h2>
|
||||
<p>下麫的程序會演示Go語言標準庫裏的image這個package的用法,我們會用這個包來生成一繫列的bit-mapped圖,然後將這些圖片編碼為一個GIF動畫。我們生成的圖形名字叫利薩如圖形(Lissajous figures),這種效果是在1960年代的老電影裏齣現的一種視覺特效。他們是協振子在兩個緯度上振動所產生的麴綫,比如兩個sin正絃波分彆在x軸和y軸輸入會產生的麴綫。圖1.1是這樣的一個例子:</p>
|
||||
<h2 id="14-gif動畵">1.4. GIF動畵</h2>
|
||||
<p>下面的程序會演示Go語言標準庫里的image這個package的用法,我們會用這個包來生成一繫列的bit-mapped圖,然後將這些圖片編碼爲一個GIF動畵。我們生成的圖形名字叫利薩如圖形(Lissajous figures),這種效果是在1960年代的老電影里齣現的一種視覺特效。他們是協振子在兩個緯度上振動所産生的麴線,比如兩個sin正絃波分别在x軸和y軸輸入會産生的麴線。圖1.1是這樣的一個例子:</p>
|
||||
<p><img src="../images/ch1-01.png" alt=""></p>
|
||||
<p>這段代碼裏我們用了一些新的結構,包括const聲明,數據struct類型,復閤聲明。和我們舉的其它的例子不太一樣,這一個例子包含了浮點數運算。這些概唸我們隻在這裏簡單地說明一下,之後的章節會更詳細地講解。</p>
|
||||
<p>這段代碼里我們用了一些新的結構,包括const聲明,數據struct類型,複合聲明。和我們舉的其它的例子不太一樣,這一個例子包含了浮點數運算。這些概念我們隻在這里簡單地説明一下,之後的章節會更詳細地講解。</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/lissajous
|
||||
|
||||
<span class="hljs-comment">// Lissajous generates GIF animations of random Lissajous figures.</span>
|
||||
@@ -2116,17 +2080,17 @@ blackIndex)
|
||||
gif.EncodeAll(out, &anim) <span class="hljs-comment">// <span class="hljs-doctag">NOTE:</span> ignoring encoding errors</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>當我們import了一個包路徑包含有多個單詞的package時,比如image/color(image和color兩個單詞),我們隻需要用最後那個單詞錶示這個包就可以。所以當我們寫color.White時,這個變量指曏的是image/color包裏的變量,衕理gif.GIF是屬於image/gif包裏的變量。</p>
|
||||
<p>這個程序裏的常量聲明給齣了一繫列的常量值,常量是指在程序編譯後運行時始終都不會變化的值,比如圈數、幀數、延遲值。常量聲明和變量聲明一般都會齣現在包級彆,所以這些常量在整個包中都是可以共享的,或者你也可以把常量聲明定義在函數體內部,那麼這種常量就隻能在函數體內用。常量聲明的值必鬚是一個數字值、字符串或者一個固定的boolean值。</p>
|
||||
<p>[]color.Color{...}和gif.GIF{...}這兩個錶達式就是我們說的復閤聲明(4.2和4.4.1節有說明)。這是實例化Go語言裏的復閤類型的一種寫法。這裏的前者生成的是一個slice,後者生成的是一個struct。</p>
|
||||
<p>gif.GIF是一個struct類型(參考4.4節)。struct是一組值或者叫字段的集閤,不衕的類型集閤在一個struct可以讓我們以一個統一的單元進行處理。anim是一個gif.GIF類型的struct變量。這種寫法會生成一個struct變量,併且其內部變量LoopCount字段會被設置為nframes;而其它的字段會被設置為各自類型默認的零值。struct內部的變量可以以一個點(.)來進行訪問,就像在最後兩個賦值語句中顯式地更新了anim這個struct的Delay和Image字段。</p>
|
||||
<p>lissajous函數內部有兩層嵌太的for循環。外層循環會循環64次,每一次都會生成一個單獨的動畫幀。它生成了一個包含兩種顔色的201&201大小的圖片,白色和黑色。所有像素點都會被默認設置為其零值(也就是palette裏的第0個值),這裏我們設置的是白色。每次經過內存循環都會通過設置像素為黑色,生成一張新圖片。其結果會append到之前結果之後。這裏我們用到了append(參考4.2.1)這個內置函數,將結果appen到anim中的幀列錶末尾,併會設置一個默認的80ms的延遲值。最終循環結束,所有的延遲值也被編碼進了GIF圖片中,併將結果寫入到輸齣流。out這個變量是io.Writer類型,這個類型讓我們可以可以讓我們把輸齣結果寫到很多目標,很快我們就可以看到了。</p>
|
||||
<p>內存循環設置了兩個偏振。x軸偏振使用的是一個sin函數。y軸偏振也是一個正絃波,但是其其相對x軸的偏振是一個0-3的隨機值,併且初始偏振值是一個零值,併隨着動畫的每一幀逐漸增加。循環會一直跑到x軸完成五次完整的循環。每一步它都會調用SetColorIndex來為(x, y)點來染黑色。</p>
|
||||
<p>main函數調用了lissajous函數,併且用它來曏標準輸齣中打印信息,所以下麫這個命令會像圖1.1中產生一個GIF動畫。</p>
|
||||
<p>當我們import了一個包路徑包含有多個單詞的package時,比如image/color(image和color兩個單詞),我們隻需要用最後那個單詞表示這個包就可以。所以當我們寫color.White時,這個變量指向的是image/color包里的變量,同理gif.GIF是屬於image/gif包里的變量。</p>
|
||||
<p>這個程序里的常量聲明給齣了一繫列的常量值,常量是指在程序編譯後運行時始終都不會變化的值,比如圈數、幀數、延遲值。常量聲明和變量聲明一般都會齣現在包級别,所以這些常量在整個包中都是可以共享的,或者你也可以把常量聲明定義在函數體內部,那麽這種常量就隻能在函數體內用。常量聲明的值必鬚是一個數字值、字符串或者一個固定的boolean值。</p>
|
||||
<p>[]color.Color{...}和gif.GIF{...}這兩個表達式就是我們説的複合聲明(4.2和4.4.1節有説明)。這是實例化Go語言里的複合類型的一種寫法。這里的前者生成的是一個slice,後者生成的是一個struct。</p>
|
||||
<p>gif.GIF是一個struct類型(參考4.4節)。struct是一組值或者叫字段的集合,不同的類型集合在一個struct可以讓我們以一個統一的單元進行處理。anim是一個gif.GIF類型的struct變量。這種寫法會生成一個struct變量,併且其內部變量LoopCount字段會被設置爲nframes;而其它的字段會被設置爲各自類型默認的零值。struct內部的變量可以以一個點(.)來進行訪問,就像在最後兩個賦值語句中顯式地更新了anim這個struct的Delay和Image字段。</p>
|
||||
<p>lissajous函數內部有兩層嵌太的for循環。外層循環會循環64次,每一次都會生成一個單獨的動畵幀。它生成了一個包含兩種顔色的201&201大小的圖片,白色和黑色。所有像素點都會被默認設置爲其零值(也就是palette里的第0個值),這里我們設置的是白色。每次經過內存循環都會通過設置像素爲黑色,生成一張新圖片。其結果會append到之前結果之後。這里我們用到了append(參考4.2.1)這個內置函數,將結果appen到anim中的幀列表末尾,併會設置一個默認的80ms的延遲值。最終循環結束,所有的延遲值也被編碼進了GIF圖片中,併將結果寫入到輸齣流。out這個變量是io.Writer類型,這個類型讓我們可以可以讓我們把輸齣結果寫到很多目標,很快我們就可以看到了。</p>
|
||||
<p>內存循環設置了兩個偏振。x軸偏振使用的是一個sin函數。y軸偏振也是一個正絃波,但是其其相對x軸的偏振是一個0-3的隨機值,併且初始偏振值是一個零值,併隨着動畵的每一幀逐漸增加。循環會一直跑到x軸完成五次完整的循環。每一步它都會調用SetColorIndex來爲(x, y)點來染黑色。</p>
|
||||
<p>main函數調用了lissajous函數,併且用它來向標準輸齣中打印信息,所以下面這個命令會像圖1.1中産生一個GIF動畵。</p>
|
||||
<pre><code class="lang-bash">$ go build gopl.io/ch1/lissajous
|
||||
$ ./lissajous >out.gif
|
||||
</code></pre>
|
||||
<pre><code>Exercise 1.5: 脩改前麫的Lissajous程序裏的調色闆,由緑色改為黑色。我們可以用color.RGBA{0xRR, 0xGG, 0xBB}來得到#RRGGBB這個色值,三個十六進製的字符串分彆代錶紅、緑、藍像素。
|
||||
<pre><code>Exercise 1.5: 脩改前面的Lissajous程序里的調色闆,由緑色改爲黑色。我們可以用color.RGBA{0xRR, 0xGG, 0xBB}來得到#RRGGBB這個色值,三個十六進製的字符串分别代表紅、緑、藍像素。
|
||||
Exercise 1.6: 脩改Lissajous程序,脩改其調色闆來生成更豐富的顔色,然後脩改SetColorIndex的第三個參數,看看顯示結果吧。
|
||||
</code></pre>
|
||||
|
||||
@@ -2138,7 +2102,7 @@ Exercise 1.6: 脩改Lissajous程序,脩改&
|
||||
</div>
|
||||
|
||||
|
||||
<a href="../ch1/ch1-03.html" class="navigation navigation-prev " aria-label="Previous page: 査找重復的行"><i class="fa fa-angle-left"></i></a>
|
||||
<a href="../ch1/ch1-03.html" class="navigation navigation-prev " aria-label="Previous page: 査找重複的行"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../ch1/ch1-05.html" class="navigation navigation-next " aria-label="Next page: 穫取URL"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
108
ch1/ch1-05.html
108
ch1/ch1-05.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="1.5" data-chapter-title="穫取URL" data-filepath="ch1/ch1-05.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="1.5" data-chapter-title="穫取URL" data-filepath="ch1/ch1-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,8 +2024,8 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="15-穫取url">1.5 穫取URL</h2>
|
||||
<p>對於很多應用來說,訪問互聯網上的信息和訪問本地文件繫統一樣重要。Go在net這個大package下提供了一繫列的package來做這件事情,使用這些包可以更簡單地用網絡收髮信息,還可以建立更底層的網絡連接,編寫服務器程序。在這些情景下,Go原生的併髮特性(在第八章中會介紹)就顯得尤其好用了。</p>
|
||||
<p>為了最簡單地展示基於HTTP穫取信息的方式,下麫給齣一個示例程序fetch,這個程序將穫取對應的url,併將其源文本打印齣來;這個例子的靈感來源於curl工具(譯註:unix下的一個工具)。當然了,curl提供的功能更為復雜豐富,這裏我們隻編寫最簡單的樣例。之後我們還會在本書中經常用到這個例子。</p>
|
||||
<p>對於很多應用來説,訪問互聯網上的信息和訪問本地文件繫統一樣重要。Go在net這個大package下提供了一繫列的package來做這件事情,使用這些包可以更簡單地用網絡收發信息,還可以建立更底層的網絡連接,編寫服務器程序。在這些情景下,Go原生的併發特性(在第八章中會介紹)就顯得尤其好用了。</p>
|
||||
<p>爲了最簡單地展示基於HTTP穫取信息的方式,下面給齣一個示例程序fetch,這個程序將穫取對應的url,併將其源文本打印齣來;這個例子的靈感來源於curl工具(譯註:unix下的一個工具)。當然了,curl提供的功能更爲複雜豐富,這里我們隻編寫最簡單的樣例。之後我們還會在本書中經常用到這個例子。</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/fetch
|
||||
<span class="hljs-comment">// Fetch prints the content found at a URL.</span>
|
||||
<span class="hljs-keyword">package</span> main
|
||||
@@ -2089,7 +2053,7 @@
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>這個程序從兩個package中導入了函數,net/http和io/ioutil,http.Get函數是創建HTTP請求的函數,如果穫取過程沒有齣錯,那麼會在resp這個結構體中得到訪問的請求結果。resp的Body字段包括一個可讀的服務器響應流。這之後ioutil.ReadAll函數從response中讀取到全部內容;其結果保存在變量b中。resp.Body.Close這一句會關閉resp的Body流,防止資源洩露,Printf函數會將結果b寫齣到標準輸齣流中。</p>
|
||||
<p>這個程序從兩個package中導入了函數,net/http和io/ioutil,http.Get函數是創建HTTP請求的函數,如果穫取過程沒有齣錯,那麽會在resp這個結構體中得到訪問的請求結果。resp的Body字段包括一個可讀的服務器響應流。這之後ioutil.ReadAll函數從response中讀取到全部內容;其結果保存在變量b中。resp.Body.Close這一句會關閉resp的Body流,防止資源洩露,Printf函數會將結果b寫齣到標準輸齣流中。</p>
|
||||
<pre><code class="lang-bash">$ go build gopl.io/ch1/fetch
|
||||
$ ./fetch http://gopl.io
|
||||
<html>
|
||||
@@ -2097,13 +2061,13 @@ $ ./fetch http://gopl.io
|
||||
<title>The Go Programming Language</title>title>
|
||||
...
|
||||
</code></pre>
|
||||
<p>HTTP請求如果失敗了的話,會得到下麫這樣的結果:</p>
|
||||
<p>HTTP請求如果失敗了的話,會得到下面這樣的結果:</p>
|
||||
<pre><code class="lang-bash">$ ./fetch http://bad.gopl.io
|
||||
fetch: Get http://bad.gopl.io: dial tcp: lookup bad.gopl.io: no such host
|
||||
</code></pre>
|
||||
<p>無論哪種失敗原因,我們的程序都用了os.Exit函數來終止進程,併且返迴一個status錯誤碼,其值為1。</p>
|
||||
<p>無論哪種失敗原因,我們的程序都用了os.Exit函數來終止進程,併且返迴一個status錯誤碼,其值爲1。</p>
|
||||
<pre><code>Exercise1.7: 函數調用io.Copy(dst, src)會從src中讀取內容,併將讀到的結果寫入到dst中,使用這個函數替代掉例子中的ioutil.ReadAll來拷貝響應結構體到os.Stdout,避免申請一個緩衝區(例子中的b)來存儲。記得處理io.Copy返迴結果中的錯誤。
|
||||
Exercise 1.8: 脩改fetch這個範例,如果輸入的url參數沒有http://前綴的話,為這個url加上該前綴。你可能會用到strings.HasPrefix這個函數。
|
||||
Exercise 1.8: 脩改fetch這個范例,如果輸入的url參數沒有http://前綴的話,爲這個url加上該前綴。你可能會用到strings.HasPrefix這個函數。
|
||||
Exercise 1.9: 脩改fetch打印齣HTTP協議的狀態碼,可以從resp.Status變量得到該狀態碼。
|
||||
</code></pre>
|
||||
|
||||
@@ -2115,10 +2079,10 @@ Exercise 1.9: 脩改fetch打印齣HTTP協議&
|
||||
</div>
|
||||
|
||||
|
||||
<a href="../ch1/ch1-04.html" class="navigation navigation-prev " aria-label="Previous page: GIF動畫"><i class="fa fa-angle-left"></i></a>
|
||||
<a href="../ch1/ch1-04.html" class="navigation navigation-prev " aria-label="Previous page: GIF動畵"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../ch1/ch1-06.html" class="navigation navigation-next " aria-label="Next page: 併髮穫取多個URL"><i class="fa fa-angle-right"></i></a>
|
||||
<a href="../ch1/ch1-06.html" class="navigation navigation-next " aria-label="Next page: 併發穫取多個URL"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
110
ch1/ch1-06.html
110
ch1/ch1-06.html
@@ -5,7 +5,7 @@
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>併髮穫取多個URL | Go编程语言</title>
|
||||
<title>併發穫取多個URL | Go编程语言</title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="generator" content="GitBook 2.5.2">
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="1.6" data-chapter-title="併髮穫取多個URL" data-filepath="ch1/ch1-06.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="1.6" data-chapter-title="併發穫取多個URL" data-filepath="ch1/ch1-06.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2059,9 +2023,9 @@
|
||||
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="16-併髮穫取多個url">1.6 併髮穫取多個URL</h2>
|
||||
<p>Go語言最有意思併且最新奇的特性就是其對併髮編程的支持了。併髮編程是一個大話題,在第八章和第九章中會講到。這裏我們隻淺嚐輒止地來體驗一下Go語言裏的goroutine和channel。</p>
|
||||
<p>下麫的例子fetchall,和上麫的fetch程序所要做的工作是一緻的,但是這個fetchall的特彆之處在於它會衕時去穫取所有的URL,所以這個程序的穫取時間不會超過執行時間最長的那一個任務,而不會像前麫的fetch程序一樣,執行時間是所有任務執行時間之和。這次的fetchall程序隻會打印穫取的內容大小和經過的時間,不會像上麫那樣打印齣穫取的內容。</p>
|
||||
<h2 id="16-併發穫取多個url">1.6 併發穫取多個URL</h2>
|
||||
<p>Go語言最有意思併且最新奇的特性就是其對併發編程的支持了。併發編程是一個大話題,在第八章和第九章中會講到。這里我們隻淺嚐輒止地來體驗一下Go語言里的goroutine和channel。</p>
|
||||
<p>下面的例子fetchall,和上面的fetch程序所要做的工作是一致的,但是這個fetchall的特别之處在於它會同時去穫取所有的URL,所以這個程序的穫取時間不會超過執行時間最長的那一個任務,而不會像前面的fetch程序一樣,執行時間是所有任務執行時間之和。這次的fetchall程序隻會打印穫取的內容大小和經過的時間,不會像上面那樣打印齣穫取的內容。</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/fetchall
|
||||
<span class="hljs-comment">// Fetchall fetches URLs in parallel and reports their times and sizes.</span>
|
||||
<span class="hljs-keyword">package</span> main
|
||||
@@ -2104,7 +2068,7 @@
|
||||
ch <- fmt.Sprintf(<span class="hljs-string">"%.2fs %7d %s"</span>, secs, nbytes, url)
|
||||
}
|
||||
</code></pre>
|
||||
<p>下麫是一個使用的例子</p>
|
||||
<p>下面是一個使用的例子</p>
|
||||
<pre><code class="lang-bash">$ go build gopl.io/ch1/fetchall
|
||||
$ ./fetchall https://golang.org http://gopl.io https://godoc.org
|
||||
<span class="hljs-number">0.14</span>s <span class="hljs-number">6852</span> https://godoc.org
|
||||
@@ -2112,10 +2076,10 @@ $ ./fetchall https://golang.org http://gopl.io https://godoc.org
|
||||
<span class="hljs-number">0.48</span>s <span class="hljs-number">2475</span> http://gopl.io
|
||||
<span class="hljs-number">0.48</span>s elapsed
|
||||
</code></pre>
|
||||
<p>goroutine是一種函數的併行執行方式,而channel是用來在goroutine之間進行參數傳遞。main函數卽運行在一個goroutine中,而go function則錶示創建一個新的goroutine,併讓這個函數去這個新的goroutine裏執行。</p>
|
||||
<p>main函數中用make函數創建了一個傳遞string類型參數的channel,對每一個命令行參數,我們都用go這個關鍵字來創建一個goroutine,併且讓函數在這個goroutine異步執行http.Get方法。這個程序裏的io.Copy會把響應的Body內容拷貝到ioutil.Discard輸齣流中,因為我們需要這個方法返迴的字節數,但是又不想要其內容。每當請求返迴內容時,fetch函數都會往ch這個channel裏寫入一個字符串,由main函數裏的第二個for循環來處理併打印channel裏的這個字符串。</p>
|
||||
<p>當一個goroutine嚐試在一個channel上做send或者receive操作時,這個goroutine會阻塞在調用處,直到另一個goroutine往這個channel裏寫入、或者接收了值,這樣兩個goroutine纔會繼續執行操作channel完成之後的邏輯。在這個例子中,每一個fetch函數在執行時都會往channel裏髮送一個值(ch <- expression),主函數接收這些值(<-ch)。這個程序中我們用main函數來所有fetch函數傳迴的字符串,可以避免在goroutine異步執行時衕時結束。</p>
|
||||
<p>Exercise 1.10: 找一個數據量比較大的網站,用本小節中的程序調研網站的緩存策略,對每個URL執行兩遍請求,査看兩次時間是否有較大的差彆,併且每次穫取到的響應內容是否一緻,脩改本節中的程序,將響應結果輸齣,以便於進行對比。</p>
|
||||
<p>goroutine是一種函數的併行執行方式,而channel是用來在goroutine之間進行參數傳遞。main函數卽運行在一個goroutine中,而go function則表示創建一個新的goroutine,併讓這個函數去這個新的goroutine里執行。</p>
|
||||
<p>main函數中用make函數創建了一個傳遞string類型參數的channel,對每一個命令行參數,我們都用go這個關鍵字來創建一個goroutine,併且讓函數在這個goroutine異步執行http.Get方法。這個程序里的io.Copy會把響應的Body內容拷貝到ioutil.Discard輸齣流中,因爲我們需要這個方法返迴的字節數,但是又不想要其內容。每當請求返迴內容時,fetch函數都會往ch這個channel里寫入一個字符串,由main函數里的第二個for循環來處理併打印channel里的這個字符串。</p>
|
||||
<p>當一個goroutine嚐試在一個channel上做send或者receive操作時,這個goroutine會阻塞在調用處,直到另一個goroutine往這個channel里寫入、或者接收了值,這樣兩個goroutine纔會繼續執行操作channel完成之後的邏輯。在這個例子中,每一個fetch函數在執行時都會往channel里發送一個值(ch <- expression),主函數接收這些值(<-ch)。這個程序中我們用main函數來所有fetch函數傳迴的字符串,可以避免在goroutine異步執行時同時結束。</p>
|
||||
<p>Exercise 1.10: 找一個數據量比較大的網站,用本小節中的程序調研網站的緩存策略,對每個URL執行兩遍請求,査看兩次時間是否有較大的差别,併且每次穫取到的響應內容是否一致,脩改本節中的程序,將響應結果輸齣,以便於進行對比。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
128
ch1/ch1-07.html
128
ch1/ch1-07.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="1.7" data-chapter-title="Web服務" data-filepath="ch1/ch1-07.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="1.7" data-chapter-title="Web服務" data-filepath="ch1/ch1-07.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,7 +2024,7 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="17-web服務">1.7. Web服務</h2>
|
||||
<p>Go的內置庫讓我們寫一個像fetch這樣例子的web服務器變得異常地簡單。在本節中,我們會展示一個微型服務器,這個服務的功能是返迴當前用戶正在訪問的URL。也就是說比如用戶訪問的是<a href="http://localhost:8000/hello,那麼響應是URL.Path" target="_blank">http://localhost:8000/hello,那麼響應是URL.Path</a> = "hello"。</p>
|
||||
<p>Go的內置庫讓我們寫一個像fetch這樣例子的web服務器變得異常地簡單。在本節中,我們會展示一個微型服務器,這個服務的功能是返迴當前用戶正在訪問的URL。也就是説比如用戶訪問的是<a href="http://localhost:8000/hello,那麽響應是URL.Path" target="_blank">http://localhost:8000/hello,那麽響應是URL.Path</a> = "hello"。</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/server1
|
||||
<span class="hljs-comment">// Server1 is a minimal "echo" server.</span>
|
||||
<span class="hljs-keyword">package</span> main
|
||||
@@ -2081,18 +2045,18 @@
|
||||
fmt.Fprintf(w, <span class="hljs-string">"URL.Path = %q\n"</span>, r.URL.Path)
|
||||
}
|
||||
</code></pre>
|
||||
<p>我們隻用了八九行就實現了這個程序,這都是多虧了標準庫裏的方法已經幫我們處理了大多數的工作。main函數會將所有髮送到/目彔下的請求和handler函數關聯起來,/開頭的請求其實就是所有髮送到當前站點上的請求,我們的服務跑在了8000端口上。髮送到這個服務的“請求”是一個http.Request類型的對象,這個對象中包含了請求中的一繫列相關字段,其中就包括我們需要的URL。當請求到達服務器時,這個請求會被傳給handler函數來處理,這個函數會將/hello這個路徑從請求的URL中解析齣來,然後把其髮送到響應中,這裏我們用的是標準輸齣流的fmt.Fprintf。Web服務會在第7.7節中詳細闡述。</p>
|
||||
<p>讓我們在後颱運行這個服務程序。如果你的操作繫統是Mac OS X或者Linux,那麼在運行命令的末尾加上一個&符號,卽可讓程序簡單地跑在後颱,而在windows下,你需要在另外一個命令行窗口去運行這個程序了。</p>
|
||||
<p>我們隻用了八九行就實現了這個程序,這都是多虧了標準庫里的方法已經幫我們處理了大多數的工作。main函數會將所有發送到/目録下的請求和handler函數關聯起來,/開頭的請求其實就是所有發送到當前站點上的請求,我們的服務跑在了8000端口上。發送到這個服務的“請求”是一個http.Request類型的對象,這個對象中包含了請求中的一繫列相關字段,其中就包括我們需要的URL。當請求到達服務器時,這個請求會被傳給handler函數來處理,這個函數會將/hello這個路徑從請求的URL中解析齣來,然後把其發送到響應中,這里我們用的是標準輸齣流的fmt.Fprintf。Web服務會在第7.7節中詳細闡述。</p>
|
||||
<p>讓我們在後颱運行這個服務程序。如果你的操作繫統是Mac OS X或者Linux,那麽在運行命令的末尾加上一個&符號,卽可讓程序簡單地跑在後颱,而在windows下,你需要在另外一個命令行窗口去運行這個程序了。</p>
|
||||
<pre><code>$ go run src/gopl.io/ch1/server1/main.go &
|
||||
</code></pre><p>現在我們可以通過命令行來髮送客戶端請求了:</p>
|
||||
</code></pre><p>現在我們可以通過命令行來發送客戶端請求了:</p>
|
||||
<pre><code>$ go build gopl.io/ch1/fetch
|
||||
$ ./fetch http://localhost:8000
|
||||
URL.Path = "/"
|
||||
$ ./fetch http://localhost:8000/help
|
||||
URL.Path = "/help"
|
||||
</code></pre><p>另外我們還可以直接在瀏覽器裏訪問這個URL,然後得到返迴結果,如圖1.2:
|
||||
</code></pre><p>另外我們還可以直接在瀏覽器里訪問這個URL,然後得到返迴結果,如圖1.2:
|
||||
<img src="../images/ch1-02.png" alt=""></p>
|
||||
<p>在這個服務的基礎上疊加特性是很容易的。一種比較實用的脩改是為訪問的url添加某種狀態。比如,下麫這個版本輸齣了衕樣的內容,但是會對請求的次數進行計算;對URL的請求結果會包含各種URL被訪問的總次數,直接對/count這個URL的訪問要除外。</p>
|
||||
<p>在這個服務的基礎上疊加特性是很容易的。一種比較實用的脩改是爲訪問的url添加某種狀態。比如,下面這個版本輸齣了同樣的內容,但是會對請求的次數進行計算;對URL的請求結果會包含各種URL被訪問的總次數,直接對/count這個URL的訪問要除外。</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/server2
|
||||
<span class="hljs-comment">// Server2 is a minimal "echo" and counter server.</span>
|
||||
<span class="hljs-keyword">package</span> main
|
||||
@@ -2128,8 +2092,8 @@ URL.Path = "/help"
|
||||
mu.Unlock()
|
||||
}
|
||||
</code></pre>
|
||||
<p>這個服務器有兩個請求處理函數,請求的url會決定具體調用哪一個:對/count這個url的請求會調用到count這個函數,其它所有的url都會調用默認的處理函數。如果你的請求pattern是以/結尾,那麼所有以該url為前綴的url都會被這條規則匹配。在這些代碼的背後,服務器每一次接收請求處理時都會另起一個goroutine,這樣服務器就可以衕一時間處理多數請求。然而在併髮情況下,假如眞的有兩個請求衕一時刻去更新count,那麼這個值可能併不會被正確地增加;這個程序可能會被引髮一個嚴重的bug:競態條件(參見9.1)。為了避免這個問題,我們必鬚保證每次脩改變量的最多隻能有一個goroutine,這也就是代碼裏的mu.Lock()和mu.Unlock()調用將脩改count的所有行為包在中間的目的。第九章中我們會進一步講解共享變量。</p>
|
||||
<p>下麫是一個更為豐富的例子,handler函數會把請求的http頭和請求的form數據都打印齣來,這樣可以讓檢査和調試這個服務更為方便</p>
|
||||
<p>這個服務器有兩個請求處理函數,請求的url會決定具體調用哪一個:對/count這個url的請求會調用到count這個函數,其它所有的url都會調用默認的處理函數。如果你的請求pattern是以/結尾,那麽所有以該url爲前綴的url都會被這條規則匹配。在這些代碼的背後,服務器每一次接收請求處理時都會另起一個goroutine,這樣服務器就可以同一時間處理多數請求。然而在併發情況下,假如眞的有兩個請求同一時刻去更新count,那麽這個值可能併不會被正確地增加;這個程序可能會被引發一個嚴重的bug:競態條件(參見9.1)。爲了避免這個問題,我們必鬚保證每次脩改變量的最多隻能有一個goroutine,這也就是代碼里的mu.Lock()和mu.Unlock()調用將脩改count的所有行爲包在中間的目的。第九章中我們會進一步講解共享變量。</p>
|
||||
<p>下面是一個更爲豐富的例子,handler函數會把請求的http頭和請求的form數據都打印齣來,這樣可以讓檢査和調試這個服務更爲方便</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/server3
|
||||
<span class="hljs-comment">// handler echoes the HTTP request.</span>
|
||||
<span class="hljs-keyword">func</span> handler(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -2147,23 +2111,23 @@ URL.Path = "/help"
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>我們用http.Request這個struct裏的字段來輸齣下麫這樣的內容:</p>
|
||||
<p>我們用http.Request這個struct里的字段來輸齣下面這樣的內容:</p>
|
||||
<pre><code>GET /?q=query HTTP/1.1
|
||||
Header["Accept-Encoding"] = ["gzip, deflate, sdch"] Header["Accept-Language"] = ["en-US,en;q=0.8"]
|
||||
Header["Connection"] = ["keep-alive"]
|
||||
Header["Accept"] = ["text/html,application/xhtml+xml,application/xml;..."] Header["User-Agent"] = ["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5)..."] Host = "localhost:8000"
|
||||
RemoteAddr = "127.0.0.1:59911"
|
||||
Form["q"] = ["query"]
|
||||
</code></pre><p>可以看到這裏的ParseForm被嵌套在了if語句中。Go語言允許這樣的一個簡單的語句結果作為循環的變量聲明齣現在if語句的最前麫,這一點對錯誤處理很有用處。我們還可以像下麫這樣寫(當然看起來就長了一些):</p>
|
||||
</code></pre><p>可以看到這里的ParseForm被嵌套在了if語句中。Go語言允許這樣的一個簡單的語句結果作爲循環的變量聲明齣現在if語句的最前面,這一點對錯誤處理很有用處。我們還可以像下面這樣寫(當然看起來就長了一些):</p>
|
||||
<pre><code class="lang-go">err := r.ParseForm()
|
||||
<span class="hljs-keyword">if</span> err != <span class="hljs-constant">nil</span> {
|
||||
log.Print(err)
|
||||
}
|
||||
</code></pre>
|
||||
<p>用if和ParseForm結閤可以讓代碼更加簡單,併且可以限製err這個變量的作用域,這麼做是很不錯的。我們會在2.7節中講解作用域。</p>
|
||||
<p>在這些程序中,我們看到了很多不衕的類型被輸齣到標準輸齣流中。比如前麫的fetch程序,就把HTTP的響應數據拷貝到了os.Stdout,或者在lissajous程序裏我們輸齣的是一個文件。fetchall程序則完全忽略到了HTTP的響應體,隻是計算了一下響應體的大小,這個程序中把響應體拷貝到了ioutil.Discard。在本節的web服務器程序中則是用fmt.Fprintf直接寫到了http.ResponseWriter中。</p>
|
||||
<p>盡管這三種具體的實現流程併不太一樣,他們都實現一個共衕的接口,卽當它們被調用需要一個標準流輸齣時都可以滿足。這個接口叫作io.Writer,在7.1節中會詳細討論。</p>
|
||||
<p>Go的接口機製會在第7章中講解,為了在這裏簡單說明接口能做什麼,讓我們簡單地將這裏的web服務器和之前寫的lissajous函數結閤起來,這樣GIF動畫可以被寫到HTTP的客戶端,而不是之前的標準輸齣流。隻要在web服務器的代碼裏加入下麫這幾行。</p>
|
||||
<p>用if和ParseForm結合可以讓代碼更加簡單,併且可以限製err這個變量的作用域,這麽做是很不錯的。我們會在2.7節中講解作用域。</p>
|
||||
<p>在這些程序中,我們看到了很多不同的類型被輸齣到標準輸齣流中。比如前面的fetch程序,就把HTTP的響應數據拷貝到了os.Stdout,或者在lissajous程序里我們輸齣的是一個文件。fetchall程序則完全忽略到了HTTP的響應體,隻是計算了一下響應體的大小,這個程序中把響應體拷貝到了ioutil.Discard。在本節的web服務器程序中則是用fmt.Fprintf直接寫到了http.ResponseWriter中。</p>
|
||||
<p>盡管這三種具體的實現流程併不太一樣,他們都實現一個共同的接口,卽當它們被調用需要一個標準流輸齣時都可以滿足。這個接口叫作io.Writer,在7.1節中會詳細討論。</p>
|
||||
<p>Go的接口機製會在第7章中講解,爲了在這里簡單説明接口能做什麽,讓我們簡單地將這里的web服務器和之前寫的lissajous函數結合起來,這樣GIF動畵可以被寫到HTTP的客戶端,而不是之前的標準輸齣流。隻要在web服務器的代碼里加入下面這幾行。</p>
|
||||
<pre><code>handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
lissajous(w)
|
||||
}
|
||||
@@ -2172,9 +2136,9 @@ http.HandleFunc("/", handler)
|
||||
<pre><code>http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
lissajous(w)
|
||||
})
|
||||
</code></pre><p>HandleFunc函數的第二個參數是一個函數的字麫值,也就是一個在使用時定義的匿名函數。這些內容我們會在5.6節中講解。</p>
|
||||
<p>做完這些脩改之後,在瀏覽器裏訪問<a href="http://localhost:8000。每次你載入這個頁麫都可以看到一個像圖1.3那樣的動畫。" target="_blank">http://localhost:8000。每次你載入這個頁麫都可以看到一個像圖1.3那樣的動畫。</a></p>
|
||||
<pre><code>Exercise 1.12:脩改Lissajour服務,從URL讀取變量,比如你可以訪問http://localhost:8000/?cycles=20這個URL,這樣訪問可以將程序裏的cycles默認的5脩改為20。字符串轉換為數字可以調用strconv.Atoi函數。你可以在dodoc裏査看strconv.Atoi的詳細說明。
|
||||
</code></pre><p>HandleFunc函數的第二個參數是一個函數的字面值,也就是一個在使用時定義的匿名函數。這些內容我們會在5.6節中講解。</p>
|
||||
<p>做完這些脩改之後,在瀏覽器里訪問<a href="http://localhost:8000。每次你載入這個頁面都可以看到一個像圖1.3那樣的動畵。" target="_blank">http://localhost:8000。每次你載入這個頁面都可以看到一個像圖1.3那樣的動畵。</a></p>
|
||||
<pre><code>Exercise 1.12:脩改Lissajour服務,從URL讀取變量,比如你可以訪問http://localhost:8000/?cycles=20這個URL,這樣訪問可以將程序里的cycles默認的5脩改爲20。字符串轉換爲數字可以調用strconv.Atoi函數。你可以在dodoc里査看strconv.Atoi的詳細説明。
|
||||
</code></pre><p><img src="../images/ch1-03.png" alt=""></p>
|
||||
|
||||
|
||||
@@ -2186,7 +2150,7 @@ http.HandleFunc("/", handler)
|
||||
</div>
|
||||
|
||||
|
||||
<a href="../ch1/ch1-06.html" class="navigation navigation-prev " aria-label="Previous page: 併髮穫取多個URL"><i class="fa fa-angle-left"></i></a>
|
||||
<a href="../ch1/ch1-06.html" class="navigation navigation-prev " aria-label="Previous page: 併發穫取多個URL"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../ch1/ch1-08.html" class="navigation navigation-next " aria-label="Next page: 本章要點"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
118
ch1/ch1-08.html
118
ch1/ch1-08.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="1.8" data-chapter-title="本章要點" data-filepath="ch1/ch1-08.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="1.8" data-chapter-title="本章要點" data-filepath="ch1/ch1-08.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,8 +2024,8 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="18-本章要點">1.8. 本章要點</h2>
|
||||
<p>本章中對Go語言做了一些介紹,實際上Go語言還有很多方麫在這有限的篇幅中還沒有覆蓋到。這裏我們會把沒有講到的內容也做一些簡單的介紹,這樣讀者在之後看到完整的內容之前,也可以簡單有個印象。</p>
|
||||
<p>控製流:在本章我們隻介紹了if控製和for,但是沒有提到switch多路選擇。這裏是一個簡單的switch的例子:</p>
|
||||
<p>本章中對Go語言做了一些介紹,實際上Go語言還有很多方面在這有限的篇幅中還沒有覆蓋到。這里我們會把沒有講到的內容也做一些簡單的介紹,這樣讀者在之後看到完整的內容之前,也可以簡單有個印象。</p>
|
||||
<p>控製流:在本章我們隻介紹了if控製和for,但是沒有提到switch多路選擇。這里是一個簡單的switch的例子:</p>
|
||||
<pre><code class="lang-go"><span class="hljs-keyword">switch</span> coinflip() {
|
||||
<span class="hljs-keyword">case</span> <span class="hljs-string">"heads"</span>:
|
||||
heads++
|
||||
@@ -2071,8 +2035,8 @@
|
||||
fmt.Println(<span class="hljs-string">"landed on edge!"</span>)
|
||||
}
|
||||
</code></pre>
|
||||
<p>在翻轉硬幣的時候,例子裏的coinflip函數返迴幾種不衕的結果,每一個case都會對應個返迴結果,這裏需要註意,Go語言併不需要顯式地去在每一個case後寫break,語言默認執行完case後的邏輯語句會自動退齣。當然了,如果你想要相鄰的幾個case都執行衕一邏輯的話,需要自己顯式地寫上一個fallthrough語句來覆蓋這種默認行為。不過fallthrough語句在一般的編程中用到得很少。</p>
|
||||
<p>go裏的switch還可以不帶操作對象;可以直接羅列多種條件,像其它語言裏麫的多個if else一樣,下麫是一個例子:</p>
|
||||
<p>在翻轉硬幣的時候,例子里的coinflip函數返迴幾種不同的結果,每一個case都會對應個返迴結果,這里需要註意,Go語言併不需要顯式地去在每一個case後寫break,語言默認執行完case後的邏輯語句會自動退齣。當然了,如果你想要相鄰的幾個case都執行同一邏輯的話,需要自己顯式地寫上一個fallthrough語句來覆蓋這種默認行爲。不過fallthrough語句在一般的編程中用到得很少。</p>
|
||||
<p>go里的switch還可以不帶操作對象;可以直接羅列多種條件,像其它語言里面的多個if else一樣,下面是一個例子:</p>
|
||||
<pre><code class="lang-go"><span class="hljs-keyword">func</span> Signum(x <span class="hljs-typename">int</span>) <span class="hljs-typename">int</span> {
|
||||
<span class="hljs-keyword">switch</span> {
|
||||
<span class="hljs-keyword">case</span> x > <span class="hljs-number">0</span>:
|
||||
@@ -2085,27 +2049,27 @@
|
||||
}
|
||||
</code></pre>
|
||||
<p>這種形式叫做無tag switch(tagless switch);這和switch true是等價的。</p>
|
||||
<p>像for和if控製語句一樣,switch也可以緊跟一個簡短的變量聲明,一個自增錶達式、賦值語句,或者一個函數調用。</p>
|
||||
<p>break和continue語句會改變控製流。和其它語言中的break和continue一樣,break會中斷當前的循環,併開始執行循環之後的內容,而continue會中跳過當前循環,併開始執行下一次循環。這兩個語句除了可以控製for循環,還可以用來控製switch和select語句(之後會講到),在1.3節中我們看到,continue會跳過是內層的循環,如果我們想跳過的是更外層的循環的話,我們可以在相應的位置加上label,這樣break和continue就可以根據我們的想法來continue和break任意循環。這看起來甚至有點像goto語句的作用了。當然,一般程序員也不會用到這種操作。這兩種行為更多地被用到機器生成的代碼中。</p>
|
||||
<p>命名類型:類型聲明使得我們可以很方便地給一個特殊類型一個名字。因為struct類型聲明通常非常地長,所以我們總要給這種struct取一個名字。本章中就有這樣一個例子,2d點類型:</p>
|
||||
<p>像for和if控製語句一樣,switch也可以緊跟一個簡短的變量聲明,一個自增表達式、賦值語句,或者一個函數調用。</p>
|
||||
<p>break和continue語句會改變控製流。和其它語言中的break和continue一樣,break會中斷當前的循環,併開始執行循環之後的內容,而continue會中跳過當前循環,併開始執行下一次循環。這兩個語句除了可以控製for循環,還可以用來控製switch和select語句(之後會講到),在1.3節中我們看到,continue會跳過是內層的循環,如果我們想跳過的是更外層的循環的話,我們可以在相應的位置加上label,這樣break和continue就可以根據我們的想法來continue和break任意循環。這看起來甚至有點像goto語句的作用了。當然,一般程序員也不會用到這種操作。這兩種行爲更多地被用到機器生成的代碼中。</p>
|
||||
<p>命名類型:類型聲明使得我們可以很方便地給一個特殊類型一個名字。因爲struct類型聲明通常非常地長,所以我們總要給這種struct取一個名字。本章中就有這樣一個例子,2d點類型:</p>
|
||||
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Point <span class="hljs-keyword">struct</span> {
|
||||
X, Y <span class="hljs-typename">int</span>
|
||||
}
|
||||
<span class="hljs-keyword">var</span> p Point
|
||||
</code></pre>
|
||||
<p>類型聲明和命名類型會在第二章中介紹。</p>
|
||||
<p>指鍼:Go語言提供了指鍼。指鍼是一種直接存儲了變量的內存地址的數據結構。在其它語言中,比如C語言,指鍼是完全不受約束的。在另外一些語言中,指鍼一般被稱為“引用”,除了到處傳遞這些指鍼之外,併不能對這些指鍼做太多事情。go在這兩種範圍中取得了一個平衡。指鍼是可見的內存地址,&操作符可以返迴一個變量的內存地址,併且*操作符可以穫取指鍼指曏的變量內容,但是在go語言裏沒有指鍼運算,也就是不像c語言裏可以對指鍼進行加或減操作。我們會在2.3.2中進行詳細介紹。</p>
|
||||
<p>方法和接口:方法是和命名類型關聯的一類函數。Go語言裏比較特殊的是方法可以被關聯到任意一種命名類型。在第六章我們會詳細地講方法。接口是一種抽象類型,這種類型可以讓我們以衕樣的方式來處理不衕的固有類型,不用關心它們的具體實現,而隻需要關註它們提供的方法。第七章中會詳細說明這些內容。</p>
|
||||
<p>包(packages):Go語言提供了一些很好用的package,併且這些package是可以擴展的。Go語言社區已經創造併且分享了很多很多。所以Go語言編程大多數情況下就是用已有的package來寫我們自己的代碼。通過這本書,我們會講解一些重要的標準庫內的package,但是還是有很多我們沒有篇幅去說明,因為我們沒法在這樣的厚度的書裏去做一部代碼大全。</p>
|
||||
<p>在你開始寫一個新程序之前,最好先去檢査一下是不是已經有了現成的庫可以幫助你更高效地完成這件事情。你可以在<a href="https://golang.org/pkg" target="_blank">https://golang.org/pkg</a> 和 <a href="https://godoc.org" target="_blank">https://godoc.org</a> 中找到標準庫和社區寫的package。godoc這個工具可以讓你直接在本地命令行閱讀標準庫的文檔。比如下麫這個例子。</p>
|
||||
<p>指針:Go語言提供了指針。指針是一種直接存儲了變量的內存地址的數據結構。在其它語言中,比如C語言,指針是完全不受約束的。在另外一些語言中,指針一般被稱爲“引用”,除了到處傳遞這些指針之外,併不能對這些指針做太多事情。go在這兩種范圍中取得了一個平衡。指針是可見的內存地址,&操作符可以返迴一個變量的內存地址,併且*操作符可以穫取指針指向的變量內容,但是在go語言里沒有指針運算,也就是不像c語言里可以對指針進行加或減操作。我們會在2.3.2中進行詳細介紹。</p>
|
||||
<p>方法和接口:方法是和命名類型關聯的一類函數。Go語言里比較特殊的是方法可以被關聯到任意一種命名類型。在第六章我們會詳細地講方法。接口是一種抽象類型,這種類型可以讓我們以同樣的方式來處理不同的固有類型,不用關心它們的具體實現,而隻需要關註它們提供的方法。第七章中會詳細説明這些內容。</p>
|
||||
<p>包(packages):Go語言提供了一些很好用的package,併且這些package是可以擴展的。Go語言社區已經創造併且分享了很多很多。所以Go語言編程大多數情況下就是用已有的package來寫我們自己的代碼。通過這本書,我們會講解一些重要的標準庫內的package,但是還是有很多我們沒有篇幅去説明,因爲我們沒法在這樣的厚度的書里去做一部代碼大全。</p>
|
||||
<p>在你開始寫一個新程序之前,最好先去檢査一下是不是已經有了現成的庫可以幫助你更高效地完成這件事情。你可以在<a href="https://golang.org/pkg" target="_blank">https://golang.org/pkg</a> 和 <a href="https://godoc.org" target="_blank">https://godoc.org</a> 中找到標準庫和社區寫的package。godoc這個工具可以讓你直接在本地命令行閲讀標準庫的文檔。比如下面這個例子。</p>
|
||||
<pre><code>$ go doc http.ListenAndServe
|
||||
package http // import "net/http"
|
||||
func ListenAndServe(addr string, handler Handler) error
|
||||
ListenAndServe listens on the TCP network address addr and then
|
||||
calls Serve with handler to handle requests on incoming connections.
|
||||
...
|
||||
</code></pre><p>註釋:我們之前已經提到過了在源文件的開頭寫的註釋是這個源文件的文檔。在每一個函數之前寫一個說明函數行為的註釋也是一個好習慣。這些慣例很重要,因為這些內容會被像godoc這樣的工具檢測到,併且在執行命令時顯示這些註釋。具體可以參考10.7.4。</p>
|
||||
<p>多行註釋可以用/<em> ... </em>/來包裹,和其它大多數語言一樣。在文件一開頭的註釋一般都是這種形式,或者一大段的解釋性的註釋文字也會被這符號包住,來避免每一行都需要加//。在註釋中//和/*是沒什麼意義的,所以不要在註釋中再嵌入註釋。</p>
|
||||
</code></pre><p>註釋:我們之前已經提到過了在源文件的開頭寫的註釋是這個源文件的文檔。在每一個函數之前寫一個説明函數行爲的註釋也是一個好習慣。這些慣例很重要,因爲這些內容會被像godoc這樣的工具檢測到,併且在執行命令時顯示這些註釋。具體可以參考10.7.4。</p>
|
||||
<p>多行註釋可以用/<em> ... </em>/來包裹,和其它大多數語言一樣。在文件一開頭的註釋一般都是這種形式,或者一大段的解釋性的註釋文字也會被這符號包住,來避免每一行都需要加//。在註釋中//和/*是沒什麽意義的,所以不要在註釋中再嵌入註釋。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
98
ch1/ch1.html
98
ch1/ch1.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="1" data-chapter-title="入門" data-filepath="ch1/ch1.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="1" data-chapter-title="入門" data-filepath="ch1/ch1.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,8 +2024,8 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h1 id="第1章-入門">第1章 入門</h1>
|
||||
<p>本章會介紹Go語言裏的一些基本組件。我們希望用信息和例子盡快帶你入門。本章和之後章節的例子都是鍼對眞實的開髮案例給齣。本章我們隻是簡單地為你介紹一些Go的入門例子,從簡單的文件處理、圖像處理到互聯網併髮客戶端和服務端程序。當然,在第一章我們不會詳盡地一一去說明細枝末節,不過用這些程序來學習一門新語言肯定是很有效的。
|
||||
當你學習一門新語言時,你會有去用這門新語言去重寫自己以前熟悉語言例子的傾曏。在學習Go的過程中,盡量避免這麼做。我們會曏你演示如何纔能寫齣好的Go程序,所以請使用這裏的代碼作為你寫自己的Go程序時的指南。</p>
|
||||
<p>本章會介紹Go語言里的一些基本組件。我們希望用信息和例子盡快帶你入門。本章和之後章節的例子都是針對眞實的開發案例給齣。本章我們隻是簡單地爲你介紹一些Go的入門例子,從簡單的文件處理、圖像處理到互聯網併發客戶端和服務端程序。當然,在第一章我們不會詳盡地一一去説明細枝末節,不過用這些程序來學習一門新語言肯定是很有效的。
|
||||
當你學習一門新語言時,你會有去用這門新語言去重寫自己以前熟悉語言例子的傾向。在學習Go的過程中,盡量避免這麽做。我們會向你演示如何纔能寫齣好的Go程序,所以請使用這里的代碼作爲你寫自己的Go程序時的指南。</p>
|
||||
|
||||
|
||||
</section>
|
||||
@@ -2072,7 +2036,7 @@
|
||||
</div>
|
||||
|
||||
|
||||
<a href="../ch0/ch0-05.html" class="navigation navigation-prev " aria-label="Previous page: 緻謝"><i class="fa fa-angle-left"></i></a>
|
||||
<a href="../ch0/ch0-05.html" class="navigation navigation-prev " aria-label="Previous page: 致謝"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../ch1/ch1-01.html" class="navigation navigation-next " aria-label="Next page: Hello, World"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="10.1" data-chapter-title="簡介" data-filepath="ch10/ch10-01.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="10.1" data-chapter-title="簡介" data-filepath="ch10/ch10-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,10 +2024,10 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="101-簡介">10.1. 簡介</h2>
|
||||
<p>任何包繫統設計的目的都是為了使大型程序的設計和維護, 通過將一組相關的特性放進一個獨立的單元以便於理解和更新, 衕時保持和程序中其他單元的相對獨立性. 這種模塊化的特性允許每個包可以被其他的不衕項目共享和重用, 在項目內甚至全球統一的分髮.</p>
|
||||
<p>每個包定義了一個不衕的名稱空間用於它內部的每個標識符. 每個名稱關聯到一個特定的包, 我們最好給類型, 函數等選擇簡短清晰的名字, 這樣可以避免在我們使用它們的時候減少和其他部分名字的衝突.</p>
|
||||
<p>包還通過控製包內名字的可見性和是否導齣來實現封裝特性. 通過限製包成員的可見性併隱藏包API的具體實現, 將允許包的維護者在不影響外部包用戶的前提下調整包的內部實現. 通過限製包內變量的可見性, 還可以控製用戶通過某些特定函數來訪問和更新內部變量, 這樣可以保證內部變量的一緻性和併髮時的互斥約束.</p>
|
||||
<p>當我們脩改了一個文件, 我們必鬚重新編譯改文件對應的包和所以依賴該包的其他包.卽使是從頭構建, Go的編譯器也明顯快於其他編譯語言. Go的編譯速度主要得益於三個特性. 第一點, 所有導入的包必鬚在每個文件的開頭顯式聲明, 這樣的話編譯器就沒有必要讀取分析整個文件來判斷包的依賴關繫. 第二點, 包的依賴關繫形成一個有曏無環圖, 因為沒有循環依賴, 每個包可以被獨立編譯, 很可能是併髮編譯. 第三點, 編譯後包的目標文件不僅僅記彔包本身的導齣信息, 衕時還記彔了它的依賴關繫. 因此, 在編譯一個包的時候, 編譯器隻需要讀取每個直接導入包的目標文件, 而不是要遍歷所有依賴的的文件(譯註: 很多可能是間接依賴).</p>
|
||||
<p>任何包繫統設計的目的都是爲了使大型程序的設計和維護, 通過將一組相關的特性放進一個獨立的單元以便於理解和更新, 同時保持和程序中其他單元的相對獨立性. 這種模塊化的特性允許每個包可以被其他的不同項目共享和重用, 在項目內甚至全球統一的分發.</p>
|
||||
<p>每個包定義了一個不同的名稱空間用於它內部的每個標識符. 每個名稱關聯到一個特定的包, 我們最好給類型, 函數等選擇簡短清晰的名字, 這樣可以避免在我們使用它們的時候減少和其他部分名字的衝突.</p>
|
||||
<p>包還通過控製包內名字的可見性和是否導齣來實現封裝特性. 通過限製包成員的可見性併隱藏包API的具體實現, 將允許包的維護者在不影響外部包用戶的前提下調整包的內部實現. 通過限製包內變量的可見性, 還可以控製用戶通過某些特定函數來訪問和更新內部變量, 這樣可以保證內部變量的一致性和併發時的互斥約束.</p>
|
||||
<p>當我們脩改了一個文件, 我們必鬚重新編譯改文件對應的包和所以依賴該包的其他包.卽使是從頭構建, Go的編譯器也明顯快於其他編譯語言. Go的編譯速度主要得益於三個特性. 第一點, 所有導入的包必鬚在每個文件的開頭顯式聲明, 這樣的話編譯器就沒有必要讀取分析整個文件來判斷包的依賴關繫. 第二點, 包的依賴關繫形成一個有向無環圖, 因爲沒有循環依賴, 每個包可以被獨立編譯, 很可能是併發編譯. 第三點, 編譯後包的目標文件不僅僅記録包本身的導齣信息, 同時還記録了它的依賴關繫. 因此, 在編譯一個包的時候, 編譯器隻需要讀取每個直接導入包的目標文件, 而不是要遍歷所有依賴的的文件(譯註: 很多可能是間接依賴).</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="10.2" data-chapter-title="導入路徑" data-filepath="ch10/ch10-02.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="10.2" data-chapter-title="導入路徑" data-filepath="ch10/ch10-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,7 +2024,7 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="102-導入路徑">10.2. 導入路徑</h2>
|
||||
<p>每個包是由一個全侷唯一的字符串所標識的導入路徑定位.
|
||||
<p>每個包是由一個全局唯一的字符串所標識的導入路徑定位.
|
||||
齣現在導入聲明中的導入路徑也是字符串.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">import</span> (
|
||||
<span class="hljs-string">"fmt"</span>
|
||||
@@ -2072,8 +2036,8 @@
|
||||
<span class="hljs-string">"github.com/go-sql-driver/mysql"</span>
|
||||
)
|
||||
</code></pre>
|
||||
<p>就像我們在2.6.1節提到過的, Go語言的規範併沒有指明包導入路徑字符串的具體含義, 具體含義是由構建工具來解釋的. 在本章, 我們將深入討論Go工具箱的功能, 包括大傢經常使用的構建測試等功能. 當然, 也有第三方擴展的工具箱存在. 例如, Google公司內部的Go碼農, 就使用內部的多語言構建繫統, 用不衕的規則來處理名字和定位包, 指定單元測試等待, 這樣可以緊密適配他們內部的繫統.</p>
|
||||
<p>如果你計劃分享或髮佈包, 那麼導入路徑最好是全球唯一的. 為了避免衝突, 所有非標準庫包的導入路徑建議以所在組織的互聯網域名為前綴; 這樣也有利於包的檢索. 例如, 上麫的包導入聲明導入了Go團隊維護的HTML解析器和一個流行的第三方維護的MySQL驅動.</p>
|
||||
<p>就像我們在2.6.1節提到過的, Go語言的規范併沒有指明包導入路徑字符串的具體含義, 具體含義是由構建工具來解釋的. 在本章, 我們將深入討論Go工具箱的功能, 包括大家經常使用的構建測試等功能. 當然, 也有第三方擴展的工具箱存在. 例如, Google公司內部的Go碼農, 就使用內部的多語言構建繫統, 用不同的規則來處理名字和定位包, 指定單元測試等待, 這樣可以緊密適配他們內部的繫統.</p>
|
||||
<p>如果你計劃分享或發布包, 那麽導入路徑最好是全球唯一的. 爲了避免衝突, 所有非標準庫包的導入路徑建議以所在組織的互聯網域名爲前綴; 這樣也有利於包的檢索. 例如, 上面的包導入聲明導入了Go糰隊維護的HTML解析器和一個流行的第三方維護的MySQL驅動.</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="10.3" data-chapter-title="包聲明" data-filepath="ch10/ch10-03.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="10.3" data-chapter-title="包聲明" data-filepath="ch10/ch10-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,7 +2024,7 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="103-包聲明">10.3. 包聲明</h2>
|
||||
<p>在每個Go源文件的開頭都必鬚有包聲明. 主要的目的是確定當前包被其他包導入時默認的標識符(稱為包名).</p>
|
||||
<p>在每個Go源文件的開頭都必鬚有包聲明. 主要的目的是確定當前包被其他包導入時默認的標識符(稱爲包名).</p>
|
||||
<p>例如, math/rand 包的每個文件的開頭都是 <code>package rand</code> 包聲明, 所有 當你導入這個包, 你可以用 rand.Int, rand.Float64 的方式訪問包的成員.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">package</span> main
|
||||
|
||||
@@ -2073,9 +2037,9 @@
|
||||
fmt.Println(rand.Int())
|
||||
}
|
||||
</code></pre>
|
||||
<p>通常來說, 默認的包名就是包導入路徑名的最後一段, 因此卽使兩個包的導入路徑不衕, 它們依然可能有一個相衕的包名. 例如, math/rand 和 crypto/rand 包的名字都是 rand. 稍後我們將看到如何衕時導入兩個包名字相衕的包.</p>
|
||||
<p>關於默認包名一般採用導入路徑名的最後一段的約定有三種例外情況. 第一個例外是包對應一個可執行程序, 也就是 main 包, 這時候main包本身的導入路徑是無關緊要的. 這是給 go build (§10.7.3) 構建命令一個信息, 必鬚調用連接器生成一個可執行程序.</p>
|
||||
<p>第二個例外是包所在的目彔中可能有一些文件名是以<em>test.go為後綴的Go源文件(譯註: 前麫必鬚有其他的字符, 因為 </em> 前綴的源文件可能是被忽略的.), 併且這些源文件聲明的包名也是以_test為後綴名的. 這種目彔可以定義兩個包: 一個普通包, 加一個外部測試包. 以 _test 為後綴包名的外部測試包由 go test 命令獨立編譯, 兩個包是相互獨立的. 外部測試包一般用來避免測試代碼中的導入包的循環導入依賴, 具體細節我們將在 11.2.4 中介紹.</p>
|
||||
<p>通常來説, 默認的包名就是包導入路徑名的最後一段, 因此卽使兩個包的導入路徑不同, 它們依然可能有一個相同的包名. 例如, math/rand 和 crypto/rand 包的名字都是 rand. 稍後我們將看到如何同時導入兩個包名字相同的包.</p>
|
||||
<p>關於默認包名一般采用導入路徑名的最後一段的約定有三種例外情況. 第一個例外是包對應一個可執行程序, 也就是 main 包, 這時候main包本身的導入路徑是無關緊要的. 這是給 go build (§10.7.3) 構建命令一個信息, 必鬚調用連接器生成一個可執行程序.</p>
|
||||
<p>第二個例外是包所在的目録中可能有一些文件名是以<em>test.go爲後綴的Go源文件(譯註: 前面必鬚有其他的字符, 因爲 </em> 前綴的源文件可能是被忽略的.), 併且這些源文件聲明的包名也是以_test爲後綴名的. 這種目録可以定義兩個包: 一個普通包, 加一個外部測試包. 以 _test 爲後綴包名的外部測試包由 go test 命令獨立編譯, 兩個包是相互獨立的. 外部測試包一般用來避免測試代碼中的導入包的循環導入依賴, 具體細節我們將在 11.2.4 中介紹.</p>
|
||||
<p>第三個例外是一些依賴版本號的管理工具會在導入路徑後追加版本號信息, 例如 "gopkg.in/yaml.v2". 這種情況下包的名字併不包含版本號後綴, 隻是yaml.</p>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="10.4" data-chapter-title="導入聲明" data-filepath="ch10/ch10-04.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="10.4" data-chapter-title="導入聲明" data-filepath="ch10/ch10-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,7 +2024,7 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="104-導入聲明">10.4. 導入聲明</h2>
|
||||
<p>一個Go源文件可以在包聲明語句之後, 其他非導入聲明之前, 包含零到多個導入包聲明. 每個導入聲明可以單獨指定一個導入路徑, 通過圓括號包含指定多個導入路徑. 下麫兩個導入形式是等價的, 但是第二種形式更為常見.</p>
|
||||
<p>一個Go源文件可以在包聲明語句之後, 其他非導入聲明之前, 包含零到多個導入包聲明. 每個導入聲明可以單獨指定一個導入路徑, 通過圓括號包含指定多個導入路徑. 下面兩個導入形式是等價的, 但是第二種形式更爲常見.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>
|
||||
<span class="hljs-keyword">import</span> <span class="hljs-string">"os"</span>
|
||||
|
||||
@@ -2069,7 +2033,7 @@
|
||||
<span class="hljs-string">"os"</span>
|
||||
)
|
||||
</code></pre>
|
||||
<p>導入的包之間可以通過添加空行來分組; 通常將來自不衕組織的包獨自分組. 導入順序無關緊要, 但是一般會根據字符串順序排列. (gofmt和goimports的都可以將不衕分組的包獨立排序.)</p>
|
||||
<p>導入的包之間可以通過添加空行來分組; 通常將來自不同組織的包獨自分組. 導入順序無關緊要, 但是一般會根據字符串順序排列. (gofmt和goimports的都可以將不同分組的包獨立排序.)</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">import</span> (
|
||||
<span class="hljs-string">"fmt"</span>
|
||||
<span class="hljs-string">"html/template"</span>
|
||||
@@ -2079,14 +2043,14 @@
|
||||
<span class="hljs-string">"golang.org/x/net/ipv4"</span>
|
||||
)
|
||||
</code></pre>
|
||||
<p>如果我們想衕時導入兩個名字相衕的包, 例如 math/rand 和 crypto/rand, 導入聲明必鬚至少為一個衕名包指定一個新的包名, 以避免衝突. 這叫做導入包重命名.</p>
|
||||
<p>如果我們想同時導入兩個名字相同的包, 例如 math/rand 和 crypto/rand, 導入聲明必鬚至少爲一個同名包指定一個新的包名, 以避免衝突. 這叫做導入包重命名.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">import</span> (
|
||||
<span class="hljs-string">"crypto/rand"</span>
|
||||
mrand <span class="hljs-string">"math/rand"</span> <span class="hljs-comment">// alternative name mrand avoids conflict</span>
|
||||
)
|
||||
</code></pre>
|
||||
<p>導入包重命名隻影響當前的Go源文件. 其他的Go源文件如果導入了相衕的包, 可以用導入包原本的名字或重命名為另一個完全不衕的名字.</p>
|
||||
<p>導入包重命名是一個有用的特性, 不僅僅是為了解決名字衝突. 如果導入的一個包名很笨重, 特彆是在一些自動生成的代碼中, 這時候用一個簡短名稱會更方便. 選擇用簡短名稱重命名導入包時候最好統一, 比避免包名混亂. 選擇另一個包名稱還可以幫助避免和本地普通變量名產生衝突. 例如, 如果文件中已經有了一個名為 path 的變量, 我們可以將"path"標準包重命名為pathpkg.</p>
|
||||
<p>導入包重命名隻影響當前的Go源文件. 其他的Go源文件如果導入了相同的包, 可以用導入包原本的名字或重命名爲另一個完全不同的名字.</p>
|
||||
<p>導入包重命名是一個有用的特性, 不僅僅是爲了解決名字衝突. 如果導入的一個包名很笨重, 特别是在一些自動生成的代碼中, 這時候用一個簡短名稱會更方便. 選擇用簡短名稱重命名導入包時候最好統一, 比避免包名混亂. 選擇另一個包名稱還可以幫助避免和本地普通變量名産生衝突. 例如, 如果文件中已經有了一個名爲 path 的變量, 我們可以將"path"標準包重命名爲pathpkg.</p>
|
||||
<p>每個導入聲明明確指定了當前包和導入包之間的依賴關繫. 如果遇到包循環導入的情況, Go的構建工具將報告錯誤.</p>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="10.5" data-chapter-title="匿名導入" data-filepath="ch10/ch10-05.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="10.5" data-chapter-title="匿名導入" data-filepath="ch10/ch10-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,11 +2024,11 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="105-匿名導入">10.5. 匿名導入</h2>
|
||||
<p>如果隻是導入一個包而併不使用導入的包是一個編譯錯誤. 但是有時候我們隻是想利用導入包產生的副作用: 它會計算包級變量的初始化錶達式和執行導入包的 init 初始化函數 (§2.6.2). 這時候我們需要抑製“未使用的導入”錯誤是閤理的, 我們可以用下劃綫 <code>_</code> 來重命名導入的包. 像往常一樣, 下劃綫 <code>_</code> 為空白標識符, 併不能被訪問.</p>
|
||||
<p>如果隻是導入一個包而併不使用導入的包是一個編譯錯誤. 但是有時候我們隻是想利用導入包産生的副作用: 它會計算包級變量的初始化表達式和執行導入包的 init 初始化函數 (§2.6.2). 這時候我們需要抑製“未使用的導入”錯誤是合理的, 我們可以用下劃線 <code>_</code> 來重命名導入的包. 像往常一樣, 下劃線 <code>_</code> 爲空白標識符, 併不能被訪問.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">import</span> _ <span class="hljs-string">"image/png"</span> <span class="hljs-comment">// register PNG decoder</span>
|
||||
</code></pre>
|
||||
<p>這個被稱為匿名導入. 它通常是用來實現一個編譯時機製, 然後通過在main主程序入口選擇性地導入附加的包. 首先, 讓我們看看如何使用它, 然後再看看它是如何工作的:</p>
|
||||
<p>標準庫的 image 圖像包導入了一個 <code>Decode</code> 函數, 用於從 <code>io.Reader</code> 接口讀取數據併解碼圖像, 它調用底層註冊的圖像解碼器工作, 然後返迴 image.Image 類型的圖像. 使用 <code>image.Decode</code> 很容易編寫一個圖像格式的轉換工具, 讀取一種格式的圖像, 然後編碼為另一種圖像格式:</p>
|
||||
<p>這個被稱爲匿名導入. 它通常是用來實現一個編譯時機製, 然後通過在main主程序入口選擇性地導入附加的包. 首先, 讓我們看看如何使用它, 然後再看看它是如何工作的:</p>
|
||||
<p>標準庫的 image 圖像包導入了一個 <code>Decode</code> 函數, 用於從 <code>io.Reader</code> 接口讀取數據併解碼圖像, 它調用底層註冊的圖像解碼器工作, 然後返迴 image.Image 類型的圖像. 使用 <code>image.Decode</code> 很容易編寫一個圖像格式的轉換工具, 讀取一種格式的圖像, 然後編碼爲另一種圖像格式:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch10/jpeg
|
||||
<span class="hljs-comment">// The jpeg command reads a PNG image from the standard input</span>
|
||||
<span class="hljs-comment">// and writes it as a JPEG image to the standard output.</span>
|
||||
@@ -2095,16 +2059,16 @@
|
||||
<span class="hljs-keyword">return</span> jpeg.Encode(out, img, &jpeg.Options{Quality: <span class="hljs-number">95</span>})
|
||||
}
|
||||
</code></pre>
|
||||
<p>如果我們將 <code>gopl.io/ch3/mandelbrot</code> (§3.3) 的輸齣導入到這個工具的輸入, 它將解碼輸入的PNG格式圖像, 然後轉換為JPEG格式的圖像(圖3.3).</p>
|
||||
<p>如果我們將 <code>gopl.io/ch3/mandelbrot</code> (§3.3) 的輸齣導入到這個工具的輸入, 它將解碼輸入的PNG格式圖像, 然後轉換爲JPEG格式的圖像(圖3.3).</p>
|
||||
<pre><code>$ go build gopl.io/ch3/mandelbrot
|
||||
$ go build gopl.io/ch10/jpeg
|
||||
$ ./mandelbrot | ./jpeg >mandelbrot.jpg
|
||||
Input format = png
|
||||
</code></pre><p>要註意 image/png 包的匿名導入語句. 如果沒有這一行語句, 依然可以編譯和運行, 但是它將不能識彆 PNG 格式的圖像:</p>
|
||||
</code></pre><p>要註意 image/png 包的匿名導入語句. 如果沒有這一行語句, 依然可以編譯和運行, 但是它將不能識别 PNG 格式的圖像:</p>
|
||||
<pre><code>$ go build gopl.io/ch10/jpeg
|
||||
$ ./mandelbrot | ./jpeg >mandelbrot.jpg
|
||||
jpeg: image: unknown format
|
||||
</code></pre><p>下麫的代碼演示了它的工作機製. 標準庫提供了GIF, PNG, 和 JPEG 格式圖像的解碼器, 用戶也可以提供自己的解碼器, 但是為了保存程序體積較小, 很多解碼器併沒有被包含盡量, 除非是明確需要支持的格式. image.Decode 函數會査詢支持的格式列錶. 列錶的每個入口指定了四件事情: 格式的名稱; 一個用於描述這種圖像數據開頭部分模式的字符串, 用於解碼器檢測識彆; 一個 Decode 函數 用於解碼圖像; 一個 DecodeConfig 函數用於解碼圖像的大小和顔色空間的信息. 每個入口是通過調用 image.RegisterFormat 函數註冊, 一般是在每個格式包的初始化函數中調用, 例如 image/png 包是這樣的:</p>
|
||||
</code></pre><p>下面的代碼演示了它的工作機製. 標準庫提供了GIF, PNG, 和 JPEG 格式圖像的解碼器, 用戶也可以提供自己的解碼器, 但是爲了保存程序體積較小, 很多解碼器併沒有被包含盡量, 除非是明確需要支持的格式. image.Decode 函數會査詢支持的格式列表. 列表的每個入口指定了四件事情: 格式的名稱; 一個用於描述這種圖像數據開頭部分模式的字符串, 用於解碼器檢測識别; 一個 Decode 函數 用於解碼圖像; 一個 DecodeConfig 函數用於解碼圖像的大小和顔色空間的信息. 每個入口是通過調用 image.RegisterFormat 函數註冊, 一般是在每個格式包的初始化函數中調用, 例如 image/png 包是這樣的:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">package</span> png <span class="hljs-comment">// image/png</span>
|
||||
|
||||
<span class="hljs-keyword">func</span> Decode(r io.Reader) (image.Image, error)
|
||||
@@ -2116,7 +2080,7 @@ jpeg: image: unknown format
|
||||
}
|
||||
</code></pre>
|
||||
<p>最終的效果是, 主程序值需要匿名導入需要 image.Decode 支持的格式對應解碼包就可以解碼圖像了.</p>
|
||||
<p>數據庫包 database/sql 也是採用了類似的技朮, 讓用戶可以根據自己需要選擇導入必要的數據庫驅動. 例如:</p>
|
||||
<p>數據庫包 database/sql 也是采用了類似的技術, 讓用戶可以根據自己需要選擇導入必要的數據庫驅動. 例如:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">import</span> (
|
||||
<span class="hljs-string">"database/mysql"</span>
|
||||
_ <span class="hljs-string">"github.com/lib/pq"</span> <span class="hljs-comment">// enable support for Postgres</span>
|
||||
@@ -2127,8 +2091,8 @@ db, err = sql.Open(<span class="hljs-string">"postgres"</span>, dbname
|
||||
db, err = sql.Open(<span class="hljs-string">"mysql"</span>, dbname) <span class="hljs-comment">// OK</span>
|
||||
db, err = sql.Open(<span class="hljs-string">"sqlite3"</span>, dbname) <span class="hljs-comment">// returns error: unknown driver "sqlite3"</span>
|
||||
</code></pre>
|
||||
<p><strong>練習 10.1:</strong> 擴展 jpeg 程序, 支持任意圖像格式之間的相互轉換, 使用 image.Decode 檢測支持的格式類型, 然後衕步 flag 命令行標誌參數選擇輸齣的格式.</p>
|
||||
<p><strong>練習 10.2:</strong> 設計一個通用的壓縮文件讀取框架, 用來讀取 ZIP(archive/zip) 和 POSIX tar(archive/tar) 格式壓縮的文檔. 使用類似上麫的註冊機製來擴展支持不衕的壓縮格式, 然後根據需要通過匿名導入選擇支持的格式.</p>
|
||||
<p><strong>練習 10.1:</strong> 擴展 jpeg 程序, 支持任意圖像格式之間的相互轉換, 使用 image.Decode 檢測支持的格式類型, 然後同步 flag 命令行標誌參數選擇輸齣的格式.</p>
|
||||
<p><strong>練習 10.2:</strong> 設計一個通用的壓縮文件讀取框架, 用來讀取 ZIP(archive/zip) 和 POSIX tar(archive/tar) 格式壓縮的文檔. 使用類似上面的註冊機製來擴展支持不同的壓縮格式, 然後根據需要通過匿名導入選擇支持的格式.</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="10.6" data-chapter-title="包和命名" data-filepath="ch10/ch10-06.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="10.6" data-chapter-title="包和命名" data-filepath="ch10/ch10-06.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2061,13 +2025,13 @@
|
||||
|
||||
<h2 id="106-包和命名">10.6. 包和命名</h2>
|
||||
<p>在本節中, 我們將提供一些關於如何遵循Go語言獨特的包和成員的命名約定.</p>
|
||||
<p>當創建一個包, 一般要用短小的包名, 但也不能太短導緻難以理解.
|
||||
<p>當創建一個包, 一般要用短小的包名, 但也不能太短導致難以理解.
|
||||
標準庫中最常用的包有 bufio, bytes, flag, fmt, http, io, json, os, sort, sync, 和 time 等包.</p>
|
||||
<p>它們的名字都簡潔明了. 例如, 不要將一個類似 imageutil 或 ioutilis 的通用包命名為 util,
|
||||
雖然它看起來很短小. 要盡量避免包名使用經常被用於侷部變量的名字, 這樣可能導緻用戶重命名導入包, 例如前麫看到的 path 包.</p>
|
||||
<p>包名衕時採用單數的形式. 標準庫的 bytes, errors, 和 strings 使用了復數是為了避免和預定義的類型衝突, 衕樣還有 go/types 是為了避免和關鍵字衝突.</p>
|
||||
<p>要避免包名有其他的含義. 例如, 2.5節中我們的溫度轉換包最初使用了 temp 包名, 雖然併沒有持續多久. 這是一個糟糕的做法, 因為 <code>temp</code> 幾乎是臨時變量的衕義詞. 然後我們有一段時間使用了 temperature 作為包名, 雖然名字併沒有錶達包的眞是用途. 最後我們改成了 tempconv 包名, 和 strconv 類似也很簡潔明了.</p>
|
||||
<p>現在讓我們看看如何命名包的襯衣. 由於是通過包的導入名字引入包裏麫的成員, 例如 fmt.Println, 衕時包含了包和成名的描述信息(翻譯障礙). 我們併不需要關註Println的具體內容, 因為 fmt 已經包含了這個信息. 當設計一個包的時候, 需要考慮包名和成員名兩個部分如何配閤. 下麫有一些例子:</p>
|
||||
<p>它們的名字都簡潔明了. 例如, 不要將一個類似 imageutil 或 ioutilis 的通用包命名爲 util,
|
||||
雖然它看起來很短小. 要盡量避免包名使用經常被用於局部變量的名字, 這樣可能導致用戶重命名導入包, 例如前面看到的 path 包.</p>
|
||||
<p>包名同時采用單數的形式. 標準庫的 bytes, errors, 和 strings 使用了複數是爲了避免和預定義的類型衝突, 同樣還有 go/types 是爲了避免和關鍵字衝突.</p>
|
||||
<p>要避免包名有其他的含義. 例如, 2.5節中我們的溫度轉換包最初使用了 temp 包名, 雖然併沒有持續多久. 這是一個糟糕的做法, 因爲 <code>temp</code> 幾乎是臨時變量的同義詞. 然後我們有一段時間使用了 temperature 作爲包名, 雖然名字併沒有表達包的眞是用途. 最後我們改成了 tempconv 包名, 和 strconv 類似也很簡潔明了.</p>
|
||||
<p>現在讓我們看看如何命名包的襯衣. 由於是通過包的導入名字引入包里面的成員, 例如 fmt.Println, 同時包含了包和成名的描述信息(翻譯障礙). 我們併不需要關註Println的具體內容, 因爲 fmt 已經包含了這個信息. 當設計一個包的時候, 需要考慮包名和成員名兩個部分如何配合. 下面有一些例子:</p>
|
||||
<pre><code>bytes.Equal flag.Int http.Get json.Marshal
|
||||
</code></pre><p>我們可以看到一些常用的命名模式. strings 包提供了字符串相關的諸多操作:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">package</span> strings
|
||||
@@ -2080,16 +2044,16 @@
|
||||
<span class="hljs-keyword">type</span> Reader <span class="hljs-keyword">struct</span>{ <span class="hljs-comment">/* ... */</span> }
|
||||
<span class="hljs-keyword">func</span> NewReader(s <span class="hljs-typename">string</span>) *Reader
|
||||
</code></pre>
|
||||
<p>string 本身併沒有齣現在每個成員名字中. 因為用戶會這樣引用這些成員 strings.Index, strings.Replacer 等.</p>
|
||||
<p>string 本身併沒有齣現在每個成員名字中. 因爲用戶會這樣引用這些成員 strings.Index, strings.Replacer 等.</p>
|
||||
<p>其他一些包, 可能隻描述了單一的數據類型, 例如 html/template 和 math/rand 等, 隻暴露一個主要的數據結構和與它相關的方法, 還有一個 New 名字的函數用於創建實例.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">package</span> rand <span class="hljs-comment">// "math/rand"</span>
|
||||
|
||||
<span class="hljs-keyword">type</span> Rand <span class="hljs-keyword">struct</span>{ <span class="hljs-comment">/* ... */</span> }
|
||||
<span class="hljs-keyword">func</span> New(source Source) *Rand
|
||||
</code></pre>
|
||||
<p>這可能導緻一些名字重復, 例如 template.Template 或 rand.Rand, 這就是為什麼這些種類的包的名稱往往特彆短.</p>
|
||||
<p>另一個極端, 還有像 net/http 包那樣含有非常多的名字和不多的數據類型, 因為它們是要執行一個復雜的復閤任務. 盡管有將近二十種類型和更多的函數, 包中最重要的成員名字卻是簡單明了的: Get, Post, Handle, Error, Client, Server.</p>
|
||||
<p>有包net/http這樣有很多名字沒有很多結構,因為他們執行一個復雜任務。盡管二十類型和更多的功能,包最重要的成員最簡單的名字:Get、Post、處理、錯誤,客戶端,服務器。</p>
|
||||
<p>這可能導致一些名字重複, 例如 template.Template 或 rand.Rand, 這就是爲什麽這些種類的包的名稱往往特别短.</p>
|
||||
<p>另一個極端, 還有像 net/http 包那樣含有非常多的名字和不多的數據類型, 因爲它們是要執行一個複雜的複合任務. 盡管有將近二十種類型和更多的函數, 包中最重要的成員名字卻是簡單明了的: Get, Post, Handle, Error, Client, Server.</p>
|
||||
<p>有包net/http這樣有很多名字沒有很多結構,因爲他們執行一個複雜任務。盡管二十類型和更多的功能,包最重要的成員最簡單的名字:Get、Post、處理、錯誤,客戶端,服務器。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
### 10.7.1. 工作區結構
|
||||
|
||||
|
||||
對於大多數的Go用戶, 隻需要配置一個名叫GOPATH的環境變量, 用來指定根工作目彔卽可. 當需要切換到不衕工作區的時候, 隻要更新GOPATH就可以了. 例如, 我們在編寫本書時, 將GOPATH設置為 `$HOME/gobook`:
|
||||
對於大多數的Go用戶, 隻需要配置一個名叫GOPATH的環境變量, 用來指定根工作目録卽可. 當需要切換到不同工作區的時候, 隻要更新GOPATH就可以了. 例如, 我們在編寫本書時, 將GOPATH設置爲 `$HOME/gobook`:
|
||||
|
||||
```
|
||||
$ export GOPATH=$HOME/gobook
|
||||
$ go get gopl.io/...
|
||||
```
|
||||
|
||||
當你用前麫介紹的命令下載本書全部的程序之後, 你的當前工作區的目彔結構是這樣的:
|
||||
當你用前面介紹的命令下載本書全部的程序之後, 你的當前工作區的目録結構是這樣的:
|
||||
|
||||
```
|
||||
GOPATH/
|
||||
@@ -35,9 +35,9 @@ GOPATH/
|
||||
...
|
||||
```
|
||||
|
||||
GOPATH對應的目彔有三個子目彔. 其中 src 子目彔用於存儲源代碼. 每個包保存在$GOPATH/src的相對路徑為包導入路徑的子目彔中, 例如 gopl.io/ch1/helloworld 相對路徑. 我們看到, 一個GOPATH工作區的src目彔中可能有多個獨立的版本控製, 例如 gopl.io 或 golang.org. 其中 pkg 子目彔用於保存編譯後的包的目標文件, bin 子目彔用於保存編譯後的可執行程序, 例如 helloworld 程序.
|
||||
GOPATH對應的目録有三個子目録. 其中 src 子目録用於存儲源代碼. 每個包保存在$GOPATH/src的相對路徑爲包導入路徑的子目録中, 例如 gopl.io/ch1/helloworld 相對路徑. 我們看到, 一個GOPATH工作區的src目録中可能有多個獨立的版本控製, 例如 gopl.io 或 golang.org. 其中 pkg 子目録用於保存編譯後的包的目標文件, bin 子目録用於保存編譯後的可執行程序, 例如 helloworld 程序.
|
||||
|
||||
第二個環境變量 GOROOT 用來指定Go的安裝目彔, 還有它自帶的標準庫包的位置. GOROOT 的目彔結構和 GOPATH 類似, 因此存放 fmt 包的源代碼目彔為 $GOROOT/src/fmt. 用戶一般不需要設置 GOROOT, 默認情況下, Go工具會設置為安裝的位置.
|
||||
第二個環境變量 GOROOT 用來指定Go的安裝目録, 還有它自帶的標準庫包的位置. GOROOT 的目録結構和 GOPATH 類似, 因此存放 fmt 包的源代碼目録爲 $GOROOT/src/fmt. 用戶一般不需要設置 GOROOT, 默認情況下, Go工具會設置爲安裝的位置.
|
||||
|
||||
其中 `go env` 命令用於査看工具涉及的所有環境變量的值, 包括未設置環境變量的默認值. GOOS 用於指定目標操作繫統(例如 android, linux, darwin, 或 windows), GOARCH 用於指定處理器的類型, 例如 amd64, 386, 或 arm. 雖然 GOPATH 是唯一必需要設置的, 但是其它的也有偶爾用到.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
使用Go工具, 不僅可以根據包導入路徑找到本地工作區的包, 甚至可以從互聯網上找到和更新包.
|
||||
|
||||
使用命令 `go get` 可以下載一個單一的包或者用 `...` 下載整個子目彔裏麫的每個包. Go工具衕時計算併下載所依賴的每個包, 這也是前一個例子中 golang.org/x/net/html 自動齣現在本地工作區目彔的原因.
|
||||
使用命令 `go get` 可以下載一個單一的包或者用 `...` 下載整個子目録里面的每個包. Go工具同時計算併下載所依賴的每個包, 這也是前一個例子中 golang.org/x/net/html 自動齣現在本地工作區目録的原因.
|
||||
|
||||
一旦 `go get` 命令下載了包, 然後就是安裝包或包對應的命令. 我們將在下一節再關註它的細節, 現在隻是展示下整個過程是如何的簡單. 第一個命令是穫取 golint 工具, 用於檢測Go源代碼的編程風格是否有問題. 第二個命令是用 golint 對 2.6.2節的 gopl.io/ch2/popcount 包代碼進行編碼風格檢査. 它友好地報告了忘記了包的文檔:
|
||||
|
||||
@@ -15,7 +15,7 @@ src/gopl.io/ch2/popcount/main.go:1:1:
|
||||
|
||||
`go get` 命令支持當前流行的託管網站 GitHub, Bitbucket, 和 Launchpad, 可以直接從它們的版本控製繫統請求代碼. 對於其他的網站, 你可能需要指定版本控製繫統的具體路徑和協議, 例如 Git 或 Mercurial. 運行 `go help importpath` 穫取更新的信息.
|
||||
|
||||
`go get` 穫取的代碼是眞實的本地存儲倉庫, 不僅僅隻是復製文件, 因此你依然可以使用版本管理工具比較本地代碼的變更, 或者切換到其他的版本. 例如 golang.org/x/net 目彔對應一個 Git 倉庫:
|
||||
`go get` 穫取的代碼是眞實的本地存儲倉庫, 不僅僅隻是複製文件, 因此你依然可以使用版本管理工具比較本地代碼的變更, 或者切換到其他的版本. 例如 golang.org/x/net 目録對應一個 Git 倉庫:
|
||||
|
||||
```
|
||||
$ cd $GOPATH/src/golang.org/x/net
|
||||
@@ -24,7 +24,7 @@ origin https://go.googlesource.com/net (fetch)
|
||||
origin https://go.googlesource.com/net (push)
|
||||
```
|
||||
|
||||
需要註意的是導入路徑含有的網站域名和本地Git倉庫遠程的Git服務地址併不相衕, 眞實的Git地址是 go.googlesource.com. 這其實是Go工具箱的一個特性, 可以讓包用一個自定義的導入路徑, 但是眞實的代碼卻是由更通用的服務提供, 例如 googlesource.com 或 github.com. 頁麫 https://golang.org/x/net/html 包含了如下的元數據, 告訴 Go 工具Git倉庫的眞實託管地址:
|
||||
需要註意的是導入路徑含有的網站域名和本地Git倉庫遠程的Git服務地址併不相同, 眞實的Git地址是 go.googlesource.com. 這其實是Go工具箱的一個特性, 可以讓包用一個自定義的導入路徑, 但是眞實的代碼卻是由更通用的服務提供, 例如 googlesource.com 或 github.com. 頁面 https://golang.org/x/net/html 包含了如下的元數據, 告訴 Go 工具Git倉庫的眞實託管地址:
|
||||
|
||||
```
|
||||
$ go build gopl.io/ch1/fetch
|
||||
@@ -33,9 +33,9 @@ $ ./fetch https://golang.org/x/net/html | grep go-import
|
||||
content="golang.org/x/net git https://go.googlesource.com/net">
|
||||
```
|
||||
|
||||
如果指定 `-u` 命令行標誌參數, `go get` 將確保所有的包和依賴的包的版本都是最新的, 然後編譯和安裝它們. 如果不包含該標誌參數, 如果包已經在本地存在, 那麼將不會被更新.
|
||||
如果指定 `-u` 命令行標誌參數, `go get` 將確保所有的包和依賴的包的版本都是最新的, 然後編譯和安裝它們. 如果不包含該標誌參數, 如果包已經在本地存在, 那麽將不會被更新.
|
||||
|
||||
`go get -u` 命令隻是簡單地保證每個包是最新版本, 如果你是第一次下載則比較很方便的; 但是如果是髮佈程序則可能是不閤適的, 因為本地程序可能需要對依賴的包做精確的版本依賴管理. 通常的解決方案是使用 vendor 目彔存儲固定版本的代碼, 對本地依賴的包的版本更新也是謹慎和持續可控的. 在 Go 1.5 之前, 一般需要脩改包的導入路徑, 所以復製後 golang.org/x/net/html 導入路徑可能會變為 gopl.io/vendor/golang.org/x/net/html. 最新的Go工具已經支持 vendor 特性, 但限於篇幅這裏併不討論細節. 不過可以通過 `go help gopath` 目彔査看 Vendor 目彔的幫助.
|
||||
`go get -u` 命令隻是簡單地保證每個包是最新版本, 如果你是第一次下載則比較很方便的; 但是如果是發布程序則可能是不合適的, 因爲本地程序可能需要對依賴的包做精確的版本依賴管理. 通常的解決方案是使用 vendor 目録存儲固定版本的代碼, 對本地依賴的包的版本更新也是謹慎和持續可控的. 在 Go 1.5 之前, 一般需要脩改包的導入路徑, 所以複製後 golang.org/x/net/html 導入路徑可能會變爲 gopl.io/vendor/golang.org/x/net/html. 最新的Go工具已經支持 vendor 特性, 但限於篇幅這里併不討論細節. 不過可以通過 `go help gopath` 目録査看 Vendor 目録的幫助.
|
||||
|
||||
**練習 10.3:** 從 http://gopl.io/ch1/helloworld?go-get=1 穫取內容, 査看本書的代碼的眞實託管的網址(`go get`請求HTML頁麫時包含了 `go-get` 參數, 以區彆普通的瀏覽器請求.)
|
||||
**練習 10.3:** 從 http://gopl.io/ch1/helloworld?go-get=1 穫取內容, 査看本書的代碼的眞實託管的網址(`go get`請求HTML頁面時包含了 `go-get` 參數, 以區别普通的瀏覽器請求.)
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
### 10.7.3. 構建包
|
||||
|
||||
`go build` 命令編譯參數指定的每個包. 如果包是一個庫, 則忽略輸齣結果; 這可以用於檢測包的可以正確編譯的.
|
||||
如果包的名字是 main, `go build` 將調用連接器在當前目彔創建一個可執行程序; 導入路徑的最後一段作為可執行程序的名字.
|
||||
如果包的名字是 main, `go build` 將調用連接器在當前目録創建一個可執行程序; 導入路徑的最後一段作爲可執行程序的名字.
|
||||
|
||||
因為每個目彔隻包含一個包, 因此每個可執行程序後者叫Unix朮語中的命令, 會要求放到一個獨立的目彔. 這些目彔有時候會放在名叫 cmd 目彔的子目彔下麫, 例如用於提供Go文檔服務的 golang.org/x/tools/cmd/godoc 命令 (§10.7.4).
|
||||
因爲每個目録隻包含一個包, 因此每個可執行程序後者叫Unix術語中的命令, 會要求放到一個獨立的目録. 這些目録有時候會放在名叫 cmd 目録的子目録下面, 例如用於提供Go文檔服務的 golang.org/x/tools/cmd/godoc 命令 (§10.7.4).
|
||||
|
||||
每個包可以由它們的導入路徑指定, 就像前麫看到的那樣, 或者有一個相對目彔的路徑知道, 必鬚以 `.` 或 `..` 開頭. 如果沒有指定參數, 那麼默認指定為當前的目彔. 下麫的命令用於構建衕一個包, 雖然它們的寫法各不相衕:
|
||||
每個包可以由它們的導入路徑指定, 就像前面看到的那樣, 或者有一個相對目録的路徑知道, 必鬚以 `.` 或 `..` 開頭. 如果沒有指定參數, 那麽默認指定爲當前的目録. 下面的命令用於構建同一個包, 雖然它們的寫法各不相同:
|
||||
|
||||
```
|
||||
$ cd $GOPATH/src/gopl.io/ch1/helloworld
|
||||
@@ -34,7 +34,7 @@ $ go build src/gopl.io/ch1/helloworld
|
||||
Error: cannot find package "src/gopl.io/ch1/helloworld".
|
||||
```
|
||||
|
||||
也可以指定包的源文件列錶, 一般這隻用於構建一些小程序或臨時性的實驗. 如果是main包, 將以第一個Go源文件的基礎文件名作為可執行程序的名字.
|
||||
也可以指定包的源文件列表, 一般這隻用於構建一些小程序或臨時性的實驗. 如果是main包, 將以第一個Go源文件的基礎文件名作爲可執行程序的名字.
|
||||
|
||||
```
|
||||
$ cat quoteargs.go
|
||||
@@ -53,22 +53,22 @@ $ ./quoteargs one "two three" four\ five
|
||||
["one" "two three" "four five"]
|
||||
```
|
||||
|
||||
特彆是對於這類一次性的程序, 我們繫統盡快的構建併運行它. `go run` 命令結閤了構建和運行的兩個步驟:
|
||||
特别是對於這類一次性的程序, 我們繫統盡快的構建併運行它. `go run` 命令結合了構建和運行的兩個步驟:
|
||||
|
||||
```
|
||||
$ go run quoteargs.go one "two three" four\ five
|
||||
["one" "two three" "four five"]
|
||||
```
|
||||
|
||||
第一行的參數列錶中第一個不是以 .go 結尾的將作為可執行程序的參數運行.
|
||||
第一行的參數列表中第一個不是以 .go 結尾的將作爲可執行程序的參數運行.
|
||||
|
||||
默認情況下, `go build` 命令構建指定的包和它依賴的包, 然後丟棄所有除了最後的可執行文件之外的中間編譯結果. 依賴分析和編譯都是很快的, 但是隨着項目增加到幾十個包和成韆上萬行代碼, 依賴關繫分析和編譯時間的消耗將變的可觀, 可能需要幾秒種, 卽使這些依賴項沒有改變.
|
||||
|
||||
`go install` 命令和 `go build` 命令很相似, 但是它保存每個包的編譯成果, 而不是將它們都丟棄. 被編譯的包被保存到 $GOPATH/pkg 目彔下和 src 目彔對應, 可執行程序被保存到 $GOPATH/bin 目彔. (很多用戶將 $GOPATH/bin 添加到可執行程序的蒐索列錶中.) 還有, `go install` 命令和 `go build` 命令都不會重新編譯沒有髮生變化的包, 這可以使後續構建更快捷. 為了方便, `go build -i` 將安裝每個目標所依賴的包.
|
||||
`go install` 命令和 `go build` 命令很相似, 但是它保存每個包的編譯成果, 而不是將它們都丟棄. 被編譯的包被保存到 $GOPATH/pkg 目録下和 src 目録對應, 可執行程序被保存到 $GOPATH/bin 目録. (很多用戶將 $GOPATH/bin 添加到可執行程序的蒐索列表中.) 還有, `go install` 命令和 `go build` 命令都不會重新編譯沒有發生變化的包, 這可以使後續構建更快捷. 爲了方便, `go build -i` 將安裝每個目標所依賴的包.
|
||||
|
||||
因為編譯對應不衕的操作繫統平颱和CPU架構, `go install` 會將編譯結果安裝到 GOOS 和 GOARCH 對應的目彔. 例如, 在 Mac 繫統 golang.org/x/net/html 包將被安裝到 $GOPATH/pkg/darwin_amd64 目彔下的 golang.org/x/net/html.a 文件.
|
||||
因爲編譯對應不同的操作繫統平颱和CPU架構, `go install` 會將編譯結果安裝到 GOOS 和 GOARCH 對應的目録. 例如, 在 Mac 繫統 golang.org/x/net/html 包將被安裝到 $GOPATH/pkg/darwin_amd64 目録下的 golang.org/x/net/html.a 文件.
|
||||
|
||||
鍼對不衕操作繫統或CPU的交叉構建也是很簡單的. 隻需要設置好目標對應的GOOS 和 GOARCH, 然後運行構建目彔卽可. 下麫交叉編譯的程序將輸齣它在編譯時操作繫統和CPU類型:
|
||||
針對不同操作繫統或CPU的交叉構建也是很簡單的. 隻需要設置好目標對應的GOOS 和 GOARCH, 然後運行構建目録卽可. 下面交叉編譯的程序將輸齣它在編譯時操作繫統和CPU類型:
|
||||
|
||||
```Go
|
||||
gopl.io/ch10/cross
|
||||
@@ -78,7 +78,7 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
下麫以64位和32位環境分彆執行程序:
|
||||
下面以64位和32位環境分别執行程序:
|
||||
|
||||
```
|
||||
$ go build gopl.io/ch10/cross
|
||||
@@ -89,13 +89,13 @@ $ ./cross
|
||||
darwin 386
|
||||
```
|
||||
|
||||
有些包可能需要鍼對不衕平颱和處理器類型輸齣不衕版本的代碼, 以便於處理底層的可移植性問題或提供為一些特點代碼提供優化. 如果一個文件名包含了一個操作繫統或處理器類型名字, 例如 net_linux.go 或 asm_amd64.s, Go工具將隻在對應的平颱編譯這些文件. 還有一個特彆的構建註釋註釋可以提供更多的構建控製. 例如, 文件中如果包含下麫的註釋:
|
||||
有些包可能需要針對不同平颱和處理器類型輸齣不同版本的代碼, 以便於處理底層的可移植性問題或提供爲一些特點代碼提供優化. 如果一個文件名包含了一個操作繫統或處理器類型名字, 例如 net_linux.go 或 asm_amd64.s, Go工具將隻在對應的平颱編譯這些文件. 還有一個特别的構建註釋註釋可以提供更多的構建控製. 例如, 文件中如果包含下面的註釋:
|
||||
|
||||
```Go
|
||||
// +build linux darwin
|
||||
```
|
||||
|
||||
在包聲明的前麫(含包的註釋), 告訴 `go build` 隻在鍼對 Linux 或 Mac OS X 是纔編譯這個文件. 下麫的構建註釋錶示不編譯這個文件:
|
||||
在包聲明的前面(含包的註釋), 告訴 `go build` 隻在針對 Linux 或 Mac OS X 是纔編譯這個文件. 下面的構建註釋表示不編譯這個文件:
|
||||
|
||||
```Go
|
||||
// +build ignore
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
### 10.7.4. 包文檔
|
||||
|
||||
Go的編碼風格鼓勵為每個包提供良好的文檔. 包中每個導齣的成員和包聲明前都應該包含添加目的和用法說明的註釋.
|
||||
Go的編碼風格鼓勵爲每個包提供良好的文檔. 包中每個導齣的成員和包聲明前都應該包含添加目的和用法説明的註釋.
|
||||
|
||||
Go中包文檔註釋一般是完整的句子, 第一行是包的摘要說明, 註釋後僅跟着包聲明語句. 函數的參數或其他的標識符併不需要額外的引號或其他標記註明. 例如, 下麫是 fmt.Fprintf 的文檔註釋.
|
||||
Go中包文檔註釋一般是完整的句子, 第一行是包的摘要説明, 註釋後僅跟着包聲明語句. 函數的參數或其他的標識符併不需要額外的引號或其他標記註明. 例如, 下面是 fmt.Fprintf 的文檔註釋.
|
||||
|
||||
```Go
|
||||
// Fprintf formats according to a format specifier and writes to w.
|
||||
@@ -10,13 +10,13 @@ Go中包文檔註釋一般是完整的句子, 第一行是包的摘要說明,
|
||||
func Fprintf(w io.Writer, format string, a ...interface{}) (int, error)
|
||||
```
|
||||
|
||||
Fprintf 函數格式化的細節在 fmt 包文檔中描述. 如果註釋後僅跟着包聲明語句, 那註釋對應整個包的文檔. 包文檔對應的註釋隻能有一個(譯註: 其實可以多個, 它們會組閤成一個包文檔註釋.), 可以齣現在任何一個源文件中. 如果包的註釋內容比較長, 可以當到一個獨立的文件中; fmt 包註釋就有 300 行之多. 這個專門用於保證包文檔的文件通常叫 doc.go.
|
||||
Fprintf 函數格式化的細節在 fmt 包文檔中描述. 如果註釋後僅跟着包聲明語句, 那註釋對應整個包的文檔. 包文檔對應的註釋隻能有一個(譯註: 其實可以多個, 它們會組合成一個包文檔註釋.), 可以齣現在任何一個源文件中. 如果包的註釋內容比較長, 可以當到一個獨立的文件中; fmt 包註釋就有 300 行之多. 這個專門用於保證包文檔的文件通常叫 doc.go.
|
||||
|
||||
好的文檔併不需要麫麫俱到, 文檔本身應該是簡潔但可不忽略的. 事實上, Go的風格喜歡簡潔的文檔, 併且文檔也是需要想代碼一樣維護的. 對於一組聲明語句, 可以衕一個精鍊的句子描述, 如果是顯而易見的功能則併不需要註釋.
|
||||
好的文檔併不需要面面俱到, 文檔本身應該是簡潔但可不忽略的. 事實上, Go的風格喜歡簡潔的文檔, 併且文檔也是需要想代碼一樣維護的. 對於一組聲明語句, 可以同一個精鍊的句子描述, 如果是顯而易見的功能則併不需要註釋.
|
||||
|
||||
在本書中, 隻要空間允許, 我們之前很多包聲明都包含了註釋文檔, 但你可以從標準庫中髮現很多更好的例子. 有兩個工具可以幫到你.
|
||||
在本書中, 隻要空間允許, 我們之前很多包聲明都包含了註釋文檔, 但你可以從標準庫中發現很多更好的例子. 有兩個工具可以幫到你.
|
||||
|
||||
`go doc` 命令打印包的聲明和每個成員的文檔註釋, 下麫是整個包的文檔:
|
||||
`go doc` 命令打印包的聲明和每個成員的文檔註釋, 下面是整個包的文檔:
|
||||
|
||||
```
|
||||
$ go doc time
|
||||
@@ -53,7 +53,7 @@ func (d Duration) Seconds() float64
|
||||
Seconds returns the duration as a floating-point number of seconds.
|
||||
```
|
||||
|
||||
該工具併不需要輸入完整的包導入路徑或正確的大小寫. 下麫的命令打印 encoding/json 包的 (*json.Decoder).Decode 方法的文檔:
|
||||
該工具併不需要輸入完整的包導入路徑或正確的大小寫. 下面的命令打印 encoding/json 包的 (*json.Decoder).Decode 方法的文檔:
|
||||
|
||||
```
|
||||
$ go doc json.decode
|
||||
@@ -63,11 +63,11 @@ func (dec *Decoder) Decode(v interface{}) error
|
||||
it in the value pointed to by v.
|
||||
```
|
||||
|
||||
第二個工具, 令人睏惑的也是名叫 godoc, 提供可以相互交叉引用的 HTML 頁麫, 但是包含和 `go doc` 相衕以及更多的信息. 10.1 節演示了 time 包的文檔, 11.6 節將看到godoc演示可以交互的示例程序. godoc 的在綫服務 https://godoc.org, 包含了成韆上萬的開源包的檢索工具.
|
||||
第二個工具, 令人睏惑的也是名叫 godoc, 提供可以相互交叉引用的 HTML 頁面, 但是包含和 `go doc` 相同以及更多的信息. 10.1 節演示了 time 包的文檔, 11.6 節將看到godoc演示可以交互的示例程序. godoc 的在線服務 https://godoc.org, 包含了成韆上萬的開源包的檢索工具.
|
||||
|
||||
You can also run an instance of godoc in your workspace if you want to browse your own packages. Visit http://localhost:8000/pkg in your browser while running this command:
|
||||
|
||||
你也可以在自己的工作區目彔允許 godoc 服務. 運行下麫的命令, 然後在瀏覽器査看 http://localhost:8000/pkg 頁麫:
|
||||
你也可以在自己的工作區目録允許 godoc 服務. 運行下面的命令, 然後在瀏覽器査看 http://localhost:8000/pkg 頁面:
|
||||
|
||||
```
|
||||
$ godoc -http :8000
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
### 10.7.5. 內部包
|
||||
|
||||
在Go程序中, 包的封裝機製是一個重要的特性. 為導齣的標識符隻在衕一個包內部可以訪問, 導齣的標識符則是麫曏全世界可見.
|
||||
在Go程序中, 包的封裝機製是一個重要的特性. 爲導齣的標識符隻在同一個包內部可以訪問, 導齣的標識符則是面向全世界可見.
|
||||
|
||||
有時候, 一個中間的狀態可能也是有用的, 對於一小部分信任的包是可見的, 但併不是對所有調用者都可見. 例如, 當我們計劃將一個大的包拆分為很多小的更容易管理的子包, 但是我們併不想將內部的子包結構也完全暴露齣去. 衕時, 我們肯呢個還希望在內部子包之間共享一些通用的處理包. 或者我們隻是想實驗一個新包的還併不穩定的接口, 暫時隻暴露給一些受限製的客戶端.
|
||||
有時候, 一個中間的狀態可能也是有用的, 對於一小部分信任的包是可見的, 但併不是對所有調用者都可見. 例如, 當我們計劃將一個大的包拆分爲很多小的更容易管理的子包, 但是我們併不想將內部的子包結構也完全暴露齣去. 同時, 我們肯呢個還希望在內部子包之間共享一些通用的處理包. 或者我們隻是想實驗一個新包的還併不穩定的接口, 暫時隻暴露給一些受限製的客戶端.
|
||||
|
||||

|
||||
|
||||
為了滿足這些需求, Go構建工具支持包含 internal 名字的路徑段的包導入路徑. 這種包叫 internal 包, 一個 internal 包隻能被有和internal目彔有衕一個父目彔的包所導入. 例如, net/http/internal/chunked 內部包隻能被 net/http/httputil 或 net/http 導入, 但是不能被 net/url 包導入. 但是 net/url 包 可以導入 net/http/httputil.
|
||||
爲了滿足這些需求, Go構建工具支持包含 internal 名字的路徑段的包導入路徑. 這種包叫 internal 包, 一個 internal 包隻能被有和internal目録有同一個父目録的包所導入. 例如, net/http/internal/chunked 內部包隻能被 net/http/httputil 或 net/http 導入, 但是不能被 net/url 包導入. 但是 net/url 包 可以導入 net/http/httputil.
|
||||
|
||||
```
|
||||
net/http
|
||||
|
||||
@@ -7,7 +7,7 @@ $ go list github.com/go-sql-driver/mysql
|
||||
github.com/go-sql-driver/mysql
|
||||
```
|
||||
|
||||
`go list` 參數還可以用 `"..."` 錶示匹配任意的包的導入路徑. 我們可以用它來列錶工作區中的所有包:
|
||||
`go list` 參數還可以用 `"..."` 表示匹配任意的包的導入路徑. 我們可以用它來列表工作區中的所有包:
|
||||
|
||||
```
|
||||
$ go list ...
|
||||
@@ -20,7 +20,7 @@ cmd/api
|
||||
...many more...
|
||||
```
|
||||
|
||||
或者是特定子目彔下的所有包:
|
||||
或者是特定子目録下的所有包:
|
||||
|
||||
```
|
||||
$ go list gopl.io/ch3/...
|
||||
@@ -41,7 +41,7 @@ encoding/xml
|
||||
gopl.io/ch7/xmlselect
|
||||
```
|
||||
|
||||
`go list` 可以穫取每個包完整的元信息, 而不僅僅隻是導入路徑, 這些信息可以以不衕格式提供給用戶. 其中 `-json` 標誌參數錶示用JSON格式打印每個包的元信息.
|
||||
`go list` 可以穫取每個包完整的元信息, 而不僅僅隻是導入路徑, 這些信息可以以不同格式提供給用戶. 其中 `-json` 標誌參數表示用JSON格式打印每個包的元信息.
|
||||
|
||||
```
|
||||
$ go list -json hash
|
||||
@@ -71,7 +71,7 @@ $ go list -json hash
|
||||
}
|
||||
```
|
||||
|
||||
參數 `-f` 允許用戶使用 text/template (§4.6) 的模闆語言定義輸齣文本的格式. 下麫的命令打印 strconv 包的依賴的包, 然後用 join 模闆函數鏈接為一行, 用一個空格分隔:
|
||||
參數 `-f` 允許用戶使用 text/template (§4.6) 的模闆語言定義輸齣文本的格式. 下面的命令打印 strconv 包的依賴的包, 然後用 join 模闆函數鏈接爲一行, 用一個空格分隔:
|
||||
|
||||
{% raw %}
|
||||
```
|
||||
@@ -80,7 +80,7 @@ errors math runtime unicode/utf8 unsafe
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
譯註: 上麫的命令在 Windows 的命令行運行會遇到 `template: main:1: unclosed action` 的錯誤. 產生錯誤的原因是因為命令行對裏麫的 `" "` 參數進行轉義了. 按照下麫的方法解決轉義字符串的問題:
|
||||
譯註: 上面的命令在 Windows 的命令行運行會遇到 `template: main:1: unclosed action` 的錯誤. 産生錯誤的原因是因爲命令行對里面的 `" "` 參數進行轉義了. 按照下面的方法解決轉義字符串的問題:
|
||||
|
||||
{% raw %}
|
||||
```
|
||||
@@ -88,7 +88,7 @@ $ go list -f "{{join .Deps \" \"}}" strconv
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
下麫的命令打印 compress 子目彔下所有包的依賴包列錶:
|
||||
下面的命令打印 compress 子目録下所有包的依賴包列表:
|
||||
|
||||
{% raw %}
|
||||
```
|
||||
@@ -101,7 +101,7 @@ compress/zlib -> bufio compress/flate errors fmt hash hash/adler32 io
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
譯註: Windows 下衕樣有問題, 要避免轉義字符串的問題:
|
||||
譯註: Windows 下同樣有問題, 要避免轉義字符串的問題:
|
||||
|
||||
{% raw %}
|
||||
```
|
||||
@@ -113,7 +113,7 @@ go list 命令對於一次性的交互式査詢或自動化構建和測試腳本
|
||||
|
||||
在本章, 我們解釋了Go工具箱除了測試命令之外的所有重要的命令. 在下一章, 我們將看到如何用 `go test` 命令去測試Go程序.
|
||||
|
||||
**練習10.4:** 創建一個工具, 根據命令行指定的參數, 報告工作區所有依賴指定包的其他包集閤. 提示: 你需要運行 `go list` 命令兩次, 一次用於初始化包, 一次用於所有包. 你可能需要用 encoding/json (§4.5) 包來分析輸齣的 JSON 格式的信息.
|
||||
**練習10.4:** 創建一個工具, 根據命令行指定的參數, 報告工作區所有依賴指定包的其他包集合. 提示: 你需要運行 `go list` 命令兩次, 一次用於初始化包, 一次用於所有包. 你可能需要用 encoding/json (§4.5) 包來分析輸齣的 JSON 格式的信息.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="10.7" data-chapter-title="工具" data-filepath="ch10/ch10-07.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="10.7" data-chapter-title="工具" data-filepath="ch10/ch10-07.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2061,8 +2025,8 @@
|
||||
|
||||
<h2 id="107-工具">10.7. 工具</h2>
|
||||
<p>本章剩下的部分將討論Go工具箱的特性, 包括如何 下載, 格式化, 構建, 測試 和 安裝 Go 程序.</p>
|
||||
<p>Go的工具箱集閤了一繫列的功能到一個命令集. 它可以看作是一個包管理器(類似於Linux中的apt和rpm工具), 用於包的査詢, 計算的包依賴關繫, 從遠程版本控製繫統和下載它們等任務. 它也是一個構建繫統, 計算文件的依賴關繫, 然後調用編譯器, 滙編器 和 連接器 構建程序, 雖然它故意被設計成沒有標準的make命令那麼復雜. 它也是一個測試驅動程序, 我們在第11章討論測試話題.</p>
|
||||
<p>Go工具箱的命令有着類似"瑞士軍刀"的風格, 帶着一打子的子命令, 有一些我們經常用到, 例如 get, run, build, 和 fmt 等. 你可以運行 <code>go help</code> 命令査看內置的溫度, 為了査詢方便, 我們列齣了最常用的命令:</p>
|
||||
<p>Go的工具箱集合了一繫列的功能到一個命令集. 它可以看作是一個包管理器(類似於Linux中的apt和rpm工具), 用於包的査詢, 計算的包依賴關繫, 從遠程版本控製繫統和下載它們等任務. 它也是一個構建繫統, 計算文件的依賴關繫, 然後調用編譯器, 滙編器 和 連接器 構建程序, 雖然它故意被設計成沒有標準的make命令那麽複雜. 它也是一個測試驅動程序, 我們在第11章討論測試話題.</p>
|
||||
<p>Go工具箱的命令有着類似"瑞士軍刀"的風格, 帶着一打子的子命令, 有一些我們經常用到, 例如 get, run, build, 和 fmt 等. 你可以運行 <code>go help</code> 命令査看內置的溫度, 爲了査詢方便, 我們列齣了最常用的命令:</p>
|
||||
<pre><code>$ go
|
||||
...
|
||||
build compile packages and dependencies
|
||||
@@ -2080,12 +2044,12 @@
|
||||
|
||||
Use "go help [command]" for more information about a command.
|
||||
...
|
||||
</code></pre><p>為了達到零配置的目標, Go的工具箱很多地方都依賴各種約定. 例如, 給定的源文件的名稱, Go工具可以找到對應的包, 因為每個目彔隻包含了單一的包, 併且到的導入路徑和工作區的目彔結構是對應的. 給定一個包的導入路徑, Go工具可以找到對應的目彔中保存對象的文件. 它還可以髮現存儲代碼倉庫的遠程服務器的URL.</p>
|
||||
</code></pre><p>爲了達到零配置的目標, Go的工具箱很多地方都依賴各種約定. 例如, 給定的源文件的名稱, Go工具可以找到對應的包, 因爲每個目録隻包含了單一的包, 併且到的導入路徑和工作區的目録結構是對應的. 給定一個包的導入路徑, Go工具可以找到對應的目録中保存對象的文件. 它還可以發現存儲代碼倉庫的遠程服務器的URL.</p>
|
||||
<h3 id="1071-工作區結構">10.7.1. 工作區結構</h3>
|
||||
<p>對於大多數的Go用戶, 隻需要配置一個名叫GOPATH的環境變量, 用來指定根工作目彔卽可. 當需要切換到不衕工作區的時候, 隻要更新GOPATH就可以了. 例如, 我們在編寫本書時, 將GOPATH設置為 <code>$HOME/gobook</code>:</p>
|
||||
<p>對於大多數的Go用戶, 隻需要配置一個名叫GOPATH的環境變量, 用來指定根工作目録卽可. 當需要切換到不同工作區的時候, 隻要更新GOPATH就可以了. 例如, 我們在編寫本書時, 將GOPATH設置爲 <code>$HOME/gobook</code>:</p>
|
||||
<pre><code>$ export GOPATH=$HOME/gobook
|
||||
$ go get gopl.io/...
|
||||
</code></pre><p>當你用前麫介紹的命令下載本書全部的程序之後, 你的當前工作區的目彔結構是這樣的:</p>
|
||||
</code></pre><p>當你用前面介紹的命令下載本書全部的程序之後, 你的當前工作區的目録結構是這樣的:</p>
|
||||
<pre><code>GOPATH/
|
||||
src/
|
||||
gopl.io/
|
||||
@@ -2108,8 +2072,8 @@ $ go get gopl.io/...
|
||||
pkg/
|
||||
darwin_amd64/
|
||||
...
|
||||
</code></pre><p>GOPATH對應的目彔有三個子目彔. 其中 src 子目彔用於存儲源代碼. 每個包保存在$GOPATH/src的相對路徑為包導入路徑的子目彔中, 例如 gopl.io/ch1/helloworld 相對路徑. 我們看到, 一個GOPATH工作區的src目彔中可能有多個獨立的版本控製, 例如 gopl.io 或 golang.org. 其中 pkg 子目彔用於保存編譯後的包的目標文件, bin 子目彔用於保存編譯後的可執行程序, 例如 helloworld 程序.</p>
|
||||
<p>第二個環境變量 GOROOT 用來指定Go的安裝目彔, 還有它自帶的標準庫包的位置. GOROOT 的目彔結構和 GOPATH 類似, 因此存放 fmt 包的源代碼目彔為 $GOROOT/src/fmt. 用戶一般不需要設置 GOROOT, 默認情況下, Go工具會設置為安裝的位置.</p>
|
||||
</code></pre><p>GOPATH對應的目録有三個子目録. 其中 src 子目録用於存儲源代碼. 每個包保存在$GOPATH/src的相對路徑爲包導入路徑的子目録中, 例如 gopl.io/ch1/helloworld 相對路徑. 我們看到, 一個GOPATH工作區的src目録中可能有多個獨立的版本控製, 例如 gopl.io 或 golang.org. 其中 pkg 子目録用於保存編譯後的包的目標文件, bin 子目録用於保存編譯後的可執行程序, 例如 helloworld 程序.</p>
|
||||
<p>第二個環境變量 GOROOT 用來指定Go的安裝目録, 還有它自帶的標準庫包的位置. GOROOT 的目録結構和 GOPATH 類似, 因此存放 fmt 包的源代碼目録爲 $GOROOT/src/fmt. 用戶一般不需要設置 GOROOT, 默認情況下, Go工具會設置爲安裝的位置.</p>
|
||||
<p>其中 <code>go env</code> 命令用於査看工具涉及的所有環境變量的值, 包括未設置環境變量的默認值. GOOS 用於指定目標操作繫統(例如 android, linux, darwin, 或 windows), GOARCH 用於指定處理器的類型, 例如 amd64, 386, 或 arm. 雖然 GOPATH 是唯一必需要設置的, 但是其它的也有偶爾用到.</p>
|
||||
<pre><code>$ go env
|
||||
GOPATH="/home/gopher/gobook"
|
||||
@@ -2119,31 +2083,31 @@ GOOS="darwin"
|
||||
...
|
||||
</code></pre><h3 id="1072-下載包">10.7.2. 下載包</h3>
|
||||
<p>使用Go工具, 不僅可以根據包導入路徑找到本地工作區的包, 甚至可以從互聯網上找到和更新包.</p>
|
||||
<p>使用命令 <code>go get</code> 可以下載一個單一的包或者用 <code>...</code> 下載整個子目彔裏麫的每個包. Go工具衕時計算併下載所依賴的每個包, 這也是前一個例子中 golang.org/x/net/html 自動齣現在本地工作區目彔的原因.</p>
|
||||
<p>使用命令 <code>go get</code> 可以下載一個單一的包或者用 <code>...</code> 下載整個子目録里面的每個包. Go工具同時計算併下載所依賴的每個包, 這也是前一個例子中 golang.org/x/net/html 自動齣現在本地工作區目録的原因.</p>
|
||||
<p>一旦 <code>go get</code> 命令下載了包, 然後就是安裝包或包對應的命令. 我們將在下一節再關註它的細節, 現在隻是展示下整個過程是如何的簡單. 第一個命令是穫取 golint 工具, 用於檢測Go源代碼的編程風格是否有問題. 第二個命令是用 golint 對 2.6.2節的 gopl.io/ch2/popcount 包代碼進行編碼風格檢査. 它友好地報告了忘記了包的文檔:</p>
|
||||
<pre><code>$ go get github.com/golang/lint/golint
|
||||
$ $GOPATH/bin/golint gopl.io/ch2/popcount
|
||||
src/gopl.io/ch2/popcount/main.go:1:1:
|
||||
package comment should be of the form "Package popcount ..."
|
||||
</code></pre><p><code>go get</code> 命令支持當前流行的託管網站 GitHub, Bitbucket, 和 Launchpad, 可以直接從它們的版本控製繫統請求代碼. 對於其他的網站, 你可能需要指定版本控製繫統的具體路徑和協議, 例如 Git 或 Mercurial. 運行 <code>go help importpath</code> 穫取更新的信息.</p>
|
||||
<p><code>go get</code> 穫取的代碼是眞實的本地存儲倉庫, 不僅僅隻是復製文件, 因此你依然可以使用版本管理工具比較本地代碼的變更, 或者切換到其他的版本. 例如 golang.org/x/net 目彔對應一個 Git 倉庫:</p>
|
||||
<p><code>go get</code> 穫取的代碼是眞實的本地存儲倉庫, 不僅僅隻是複製文件, 因此你依然可以使用版本管理工具比較本地代碼的變更, 或者切換到其他的版本. 例如 golang.org/x/net 目録對應一個 Git 倉庫:</p>
|
||||
<pre><code>$ cd $GOPATH/src/golang.org/x/net
|
||||
$ git remote -v
|
||||
origin https://go.googlesource.com/net (fetch)
|
||||
origin https://go.googlesource.com/net (push)
|
||||
</code></pre><p>需要註意的是導入路徑含有的網站域名和本地Git倉庫遠程的Git服務地址併不相衕, 眞實的Git地址是 go.googlesource.com. 這其實是Go工具箱的一個特性, 可以讓包用一個自定義的導入路徑, 但是眞實的代碼卻是由更通用的服務提供, 例如 googlesource.com 或 github.com. 頁麫 <a href="https://golang.org/x/net/html" target="_blank">https://golang.org/x/net/html</a> 包含了如下的元數據, 告訴 Go 工具Git倉庫的眞實託管地址:</p>
|
||||
</code></pre><p>需要註意的是導入路徑含有的網站域名和本地Git倉庫遠程的Git服務地址併不相同, 眞實的Git地址是 go.googlesource.com. 這其實是Go工具箱的一個特性, 可以讓包用一個自定義的導入路徑, 但是眞實的代碼卻是由更通用的服務提供, 例如 googlesource.com 或 github.com. 頁面 <a href="https://golang.org/x/net/html" target="_blank">https://golang.org/x/net/html</a> 包含了如下的元數據, 告訴 Go 工具Git倉庫的眞實託管地址:</p>
|
||||
<pre><code>$ go build gopl.io/ch1/fetch
|
||||
$ ./fetch https://golang.org/x/net/html | grep go-import
|
||||
<meta name="go-import"
|
||||
content="golang.org/x/net git https://go.googlesource.com/net">
|
||||
</code></pre><p>如果指定 <code>-u</code> 命令行標誌參數, <code>go get</code> 將確保所有的包和依賴的包的版本都是最新的, 然後編譯和安裝它們. 如果不包含該標誌參數, 如果包已經在本地存在, 那麼將不會被更新.</p>
|
||||
<p><code>go get -u</code> 命令隻是簡單地保證每個包是最新版本, 如果你是第一次下載則比較很方便的; 但是如果是髮佈程序則可能是不閤適的, 因為本地程序可能需要對依賴的包做精確的版本依賴管理. 通常的解決方案是使用 vendor 目彔存儲固定版本的代碼, 對本地依賴的包的版本更新也是謹慎和持續可控的. 在 Go 1.5 之前, 一般需要脩改包的導入路徑, 所以復製後 golang.org/x/net/html 導入路徑可能會變為 gopl.io/vendor/golang.org/x/net/html. 最新的Go工具已經支持 vendor 特性, 但限於篇幅這裏併不討論細節. 不過可以通過 <code>go help gopath</code> 目彔査看 Vendor 目彔的幫助.</p>
|
||||
<p><strong>練習 10.3:</strong> 從 <a href="http://gopl.io/ch1/helloworld?go-get=1" target="_blank">http://gopl.io/ch1/helloworld?go-get=1</a> 穫取內容, 査看本書的代碼的眞實託管的網址(<code>go get</code>請求HTML頁麫時包含了 <code>go-get</code> 參數, 以區彆普通的瀏覽器請求.)</p>
|
||||
</code></pre><p>如果指定 <code>-u</code> 命令行標誌參數, <code>go get</code> 將確保所有的包和依賴的包的版本都是最新的, 然後編譯和安裝它們. 如果不包含該標誌參數, 如果包已經在本地存在, 那麽將不會被更新.</p>
|
||||
<p><code>go get -u</code> 命令隻是簡單地保證每個包是最新版本, 如果你是第一次下載則比較很方便的; 但是如果是發布程序則可能是不合適的, 因爲本地程序可能需要對依賴的包做精確的版本依賴管理. 通常的解決方案是使用 vendor 目録存儲固定版本的代碼, 對本地依賴的包的版本更新也是謹慎和持續可控的. 在 Go 1.5 之前, 一般需要脩改包的導入路徑, 所以複製後 golang.org/x/net/html 導入路徑可能會變爲 gopl.io/vendor/golang.org/x/net/html. 最新的Go工具已經支持 vendor 特性, 但限於篇幅這里併不討論細節. 不過可以通過 <code>go help gopath</code> 目録査看 Vendor 目録的幫助.</p>
|
||||
<p><strong>練習 10.3:</strong> 從 <a href="http://gopl.io/ch1/helloworld?go-get=1" target="_blank">http://gopl.io/ch1/helloworld?go-get=1</a> 穫取內容, 査看本書的代碼的眞實託管的網址(<code>go get</code>請求HTML頁面時包含了 <code>go-get</code> 參數, 以區别普通的瀏覽器請求.)</p>
|
||||
<h3 id="1073-構建包">10.7.3. 構建包</h3>
|
||||
<p><code>go build</code> 命令編譯參數指定的每個包. 如果包是一個庫, 則忽略輸齣結果; 這可以用於檢測包的可以正確編譯的.
|
||||
如果包的名字是 main, <code>go build</code> 將調用連接器在當前目彔創建一個可執行程序; 導入路徑的最後一段作為可執行程序的名字.</p>
|
||||
<p>因為每個目彔隻包含一個包, 因此每個可執行程序後者叫Unix朮語中的命令, 會要求放到一個獨立的目彔. 這些目彔有時候會放在名叫 cmd 目彔的子目彔下麫, 例如用於提供Go文檔服務的 golang.org/x/tools/cmd/godoc 命令 (§10.7.4).</p>
|
||||
<p>每個包可以由它們的導入路徑指定, 就像前麫看到的那樣, 或者有一個相對目彔的路徑知道, 必鬚以 <code>.</code> 或 <code>..</code> 開頭. 如果沒有指定參數, 那麼默認指定為當前的目彔. 下麫的命令用於構建衕一個包, 雖然它們的寫法各不相衕:</p>
|
||||
如果包的名字是 main, <code>go build</code> 將調用連接器在當前目録創建一個可執行程序; 導入路徑的最後一段作爲可執行程序的名字.</p>
|
||||
<p>因爲每個目録隻包含一個包, 因此每個可執行程序後者叫Unix術語中的命令, 會要求放到一個獨立的目録. 這些目録有時候會放在名叫 cmd 目録的子目録下面, 例如用於提供Go文檔服務的 golang.org/x/tools/cmd/godoc 命令 (§10.7.4).</p>
|
||||
<p>每個包可以由它們的導入路徑指定, 就像前面看到的那樣, 或者有一個相對目録的路徑知道, 必鬚以 <code>.</code> 或 <code>..</code> 開頭. 如果沒有指定參數, 那麽默認指定爲當前的目録. 下面的命令用於構建同一個包, 雖然它們的寫法各不相同:</p>
|
||||
<pre><code>$ cd $GOPATH/src/gopl.io/ch1/helloworld
|
||||
$ go build
|
||||
</code></pre><p>或者:</p>
|
||||
@@ -2156,7 +2120,7 @@ $ go build ./src/gopl.io/ch1/helloworld
|
||||
<pre><code>$ cd $GOPATH
|
||||
$ go build src/gopl.io/ch1/helloworld
|
||||
Error: cannot find package "src/gopl.io/ch1/helloworld".
|
||||
</code></pre><p>也可以指定包的源文件列錶, 一般這隻用於構建一些小程序或臨時性的實驗. 如果是main包, 將以第一個Go源文件的基礎文件名作為可執行程序的名字.</p>
|
||||
</code></pre><p>也可以指定包的源文件列表, 一般這隻用於構建一些小程序或臨時性的實驗. 如果是main包, 將以第一個Go源文件的基礎文件名作爲可執行程序的名字.</p>
|
||||
<pre><code>$ cat quoteargs.go
|
||||
package main
|
||||
|
||||
@@ -2171,47 +2135,47 @@ func main() {
|
||||
$ go build quoteargs.go
|
||||
$ ./quoteargs one "two three" four\ five
|
||||
["one" "two three" "four five"]
|
||||
</code></pre><p>特彆是對於這類一次性的程序, 我們繫統盡快的構建併運行它. <code>go run</code> 命令結閤了構建和運行的兩個步驟:</p>
|
||||
</code></pre><p>特别是對於這類一次性的程序, 我們繫統盡快的構建併運行它. <code>go run</code> 命令結合了構建和運行的兩個步驟:</p>
|
||||
<pre><code>$ go run quoteargs.go one "two three" four\ five
|
||||
["one" "two three" "four five"]
|
||||
</code></pre><p>第一行的參數列錶中第一個不是以 .go 結尾的將作為可執行程序的參數運行.</p>
|
||||
</code></pre><p>第一行的參數列表中第一個不是以 .go 結尾的將作爲可執行程序的參數運行.</p>
|
||||
<p>默認情況下, <code>go build</code> 命令構建指定的包和它依賴的包, 然後丟棄所有除了最後的可執行文件之外的中間編譯結果. 依賴分析和編譯都是很快的, 但是隨着項目增加到幾十個包和成韆上萬行代碼, 依賴關繫分析和編譯時間的消耗將變的可觀, 可能需要幾秒種, 卽使這些依賴項沒有改變.</p>
|
||||
<p><code>go install</code> 命令和 <code>go build</code> 命令很相似, 但是它保存每個包的編譯成果, 而不是將它們都丟棄. 被編譯的包被保存到 $GOPATH/pkg 目彔下和 src 目彔對應, 可執行程序被保存到 $GOPATH/bin 目彔. (很多用戶將 $GOPATH/bin 添加到可執行程序的蒐索列錶中.) 還有, <code>go install</code> 命令和 <code>go build</code> 命令都不會重新編譯沒有髮生變化的包, 這可以使後續構建更快捷. 為了方便, <code>go build -i</code> 將安裝每個目標所依賴的包.</p>
|
||||
<p>因為編譯對應不衕的操作繫統平颱和CPU架構, <code>go install</code> 會將編譯結果安裝到 GOOS 和 GOARCH 對應的目彔. 例如, 在 Mac 繫統 golang.org/x/net/html 包將被安裝到 $GOPATH/pkg/darwin_amd64 目彔下的 golang.org/x/net/html.a 文件.</p>
|
||||
<p>鍼對不衕操作繫統或CPU的交叉構建也是很簡單的. 隻需要設置好目標對應的GOOS 和 GOARCH, 然後運行構建目彔卽可. 下麫交叉編譯的程序將輸齣它在編譯時操作繫統和CPU類型:</p>
|
||||
<p><code>go install</code> 命令和 <code>go build</code> 命令很相似, 但是它保存每個包的編譯成果, 而不是將它們都丟棄. 被編譯的包被保存到 $GOPATH/pkg 目録下和 src 目録對應, 可執行程序被保存到 $GOPATH/bin 目録. (很多用戶將 $GOPATH/bin 添加到可執行程序的蒐索列表中.) 還有, <code>go install</code> 命令和 <code>go build</code> 命令都不會重新編譯沒有發生變化的包, 這可以使後續構建更快捷. 爲了方便, <code>go build -i</code> 將安裝每個目標所依賴的包.</p>
|
||||
<p>因爲編譯對應不同的操作繫統平颱和CPU架構, <code>go install</code> 會將編譯結果安裝到 GOOS 和 GOARCH 對應的目録. 例如, 在 Mac 繫統 golang.org/x/net/html 包將被安裝到 $GOPATH/pkg/darwin_amd64 目録下的 golang.org/x/net/html.a 文件.</p>
|
||||
<p>針對不同操作繫統或CPU的交叉構建也是很簡單的. 隻需要設置好目標對應的GOOS 和 GOARCH, 然後運行構建目録卽可. 下面交叉編譯的程序將輸齣它在編譯時操作繫統和CPU類型:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch10/cross
|
||||
|
||||
<span class="hljs-keyword">func</span> main() {
|
||||
fmt.Println(runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
</code></pre>
|
||||
<p>下麫以64位和32位環境分彆執行程序:</p>
|
||||
<p>下面以64位和32位環境分别執行程序:</p>
|
||||
<pre><code>$ go build gopl.io/ch10/cross
|
||||
$ ./cross
|
||||
darwin amd64
|
||||
$ GOARCH=386 go build gopl.io/ch10/cross
|
||||
$ ./cross
|
||||
darwin 386
|
||||
</code></pre><p>有些包可能需要鍼對不衕平颱和處理器類型輸齣不衕版本的代碼, 以便於處理底層的可移植性問題或提供為一些特點代碼提供優化. 如果一個文件名包含了一個操作繫統或處理器類型名字, 例如 net_linux.go 或 asm_amd64.s, Go工具將隻在對應的平颱編譯這些文件. 還有一個特彆的構建註釋註釋可以提供更多的構建控製. 例如, 文件中如果包含下麫的註釋:</p>
|
||||
</code></pre><p>有些包可能需要針對不同平颱和處理器類型輸齣不同版本的代碼, 以便於處理底層的可移植性問題或提供爲一些特點代碼提供優化. 如果一個文件名包含了一個操作繫統或處理器類型名字, 例如 net_linux.go 或 asm_amd64.s, Go工具將隻在對應的平颱編譯這些文件. 還有一個特别的構建註釋註釋可以提供更多的構建控製. 例如, 文件中如果包含下面的註釋:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// +build linux darwin</span>
|
||||
</code></pre>
|
||||
<p>在包聲明的前麫(含包的註釋), 告訴 <code>go build</code> 隻在鍼對 Linux 或 Mac OS X 是纔編譯這個文件. 下麫的構建註釋錶示不編譯這個文件:</p>
|
||||
<p>在包聲明的前面(含包的註釋), 告訴 <code>go build</code> 隻在針對 Linux 或 Mac OS X 是纔編譯這個文件. 下面的構建註釋表示不編譯這個文件:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// +build ignore</span>
|
||||
</code></pre>
|
||||
<p>For more details, see the Build Constraints section of the go/build package’s documentation:</p>
|
||||
<p>更多細節, 可以參考 go/build 包的構建約束部分的文檔.</p>
|
||||
<pre><code>$ go doc go/build
|
||||
</code></pre><h3 id="1074-包文檔">10.7.4. 包文檔</h3>
|
||||
<p>Go的編碼風格鼓勵為每個包提供良好的文檔. 包中每個導齣的成員和包聲明前都應該包含添加目的和用法說明的註釋.</p>
|
||||
<p>Go中包文檔註釋一般是完整的句子, 第一行是包的摘要說明, 註釋後僅跟着包聲明語句. 函數的參數或其他的標識符併不需要額外的引號或其他標記註明. 例如, 下麫是 fmt.Fprintf 的文檔註釋.</p>
|
||||
<p>Go的編碼風格鼓勵爲每個包提供良好的文檔. 包中每個導齣的成員和包聲明前都應該包含添加目的和用法説明的註釋.</p>
|
||||
<p>Go中包文檔註釋一般是完整的句子, 第一行是包的摘要説明, 註釋後僅跟着包聲明語句. 函數的參數或其他的標識符併不需要額外的引號或其他標記註明. 例如, 下面是 fmt.Fprintf 的文檔註釋.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// Fprintf formats according to a format specifier and writes to w.</span>
|
||||
<span class="hljs-comment">// It returns the number of bytes written and any write error encountered.</span>
|
||||
<span class="hljs-keyword">func</span> Fprintf(w io.Writer, format <span class="hljs-typename">string</span>, a ...<span class="hljs-keyword">interface</span>{}) (<span class="hljs-typename">int</span>, error)
|
||||
</code></pre>
|
||||
<p>Fprintf 函數格式化的細節在 fmt 包文檔中描述. 如果註釋後僅跟着包聲明語句, 那註釋對應整個包的文檔. 包文檔對應的註釋隻能有一個(譯註: 其實可以多個, 它們會組閤成一個包文檔註釋.), 可以齣現在任何一個源文件中. 如果包的註釋內容比較長, 可以當到一個獨立的文件中; fmt 包註釋就有 300 行之多. 這個專門用於保證包文檔的文件通常叫 doc.go.</p>
|
||||
<p>好的文檔併不需要麫麫俱到, 文檔本身應該是簡潔但可不忽略的. 事實上, Go的風格喜歡簡潔的文檔, 併且文檔也是需要想代碼一樣維護的. 對於一組聲明語句, 可以衕一個精鍊的句子描述, 如果是顯而易見的功能則併不需要註釋.</p>
|
||||
<p>在本書中, 隻要空間允許, 我們之前很多包聲明都包含了註釋文檔, 但你可以從標準庫中髮現很多更好的例子. 有兩個工具可以幫到你.</p>
|
||||
<p><code>go doc</code> 命令打印包的聲明和每個成員的文檔註釋, 下麫是整個包的文檔:</p>
|
||||
<p>Fprintf 函數格式化的細節在 fmt 包文檔中描述. 如果註釋後僅跟着包聲明語句, 那註釋對應整個包的文檔. 包文檔對應的註釋隻能有一個(譯註: 其實可以多個, 它們會組合成一個包文檔註釋.), 可以齣現在任何一個源文件中. 如果包的註釋內容比較長, 可以當到一個獨立的文件中; fmt 包註釋就有 300 行之多. 這個專門用於保證包文檔的文件通常叫 doc.go.</p>
|
||||
<p>好的文檔併不需要面面俱到, 文檔本身應該是簡潔但可不忽略的. 事實上, Go的風格喜歡簡潔的文檔, 併且文檔也是需要想代碼一樣維護的. 對於一組聲明語句, 可以同一個精鍊的句子描述, 如果是顯而易見的功能則併不需要註釋.</p>
|
||||
<p>在本書中, 隻要空間允許, 我們之前很多包聲明都包含了註釋文檔, 但你可以從標準庫中發現很多更好的例子. 有兩個工具可以幫到你.</p>
|
||||
<p><code>go doc</code> 命令打印包的聲明和每個成員的文檔註釋, 下面是整個包的文檔:</p>
|
||||
<pre><code>$ go doc time
|
||||
package time // import "time"
|
||||
|
||||
@@ -2236,22 +2200,22 @@ func Since(t Time) Duration
|
||||
func (d Duration) Seconds() float64
|
||||
|
||||
Seconds returns the duration as a floating-point number of seconds.
|
||||
</code></pre><p>該工具併不需要輸入完整的包導入路徑或正確的大小寫. 下麫的命令打印 encoding/json 包的 (*json.Decoder).Decode 方法的文檔:</p>
|
||||
</code></pre><p>該工具併不需要輸入完整的包導入路徑或正確的大小寫. 下面的命令打印 encoding/json 包的 (*json.Decoder).Decode 方法的文檔:</p>
|
||||
<pre><code>$ go doc json.decode
|
||||
func (dec *Decoder) Decode(v interface{}) error
|
||||
|
||||
Decode reads the next JSON-encoded value from its input and stores
|
||||
it in the value pointed to by v.
|
||||
</code></pre><p>第二個工具, 令人睏惑的也是名叫 godoc, 提供可以相互交叉引用的 HTML 頁麫, 但是包含和 <code>go doc</code> 相衕以及更多的信息. 10.1 節演示了 time 包的文檔, 11.6 節將看到godoc演示可以交互的示例程序. godoc 的在綫服務 <a href="https://godoc.org" target="_blank">https://godoc.org</a>, 包含了成韆上萬的開源包的檢索工具.</p>
|
||||
</code></pre><p>第二個工具, 令人睏惑的也是名叫 godoc, 提供可以相互交叉引用的 HTML 頁面, 但是包含和 <code>go doc</code> 相同以及更多的信息. 10.1 節演示了 time 包的文檔, 11.6 節將看到godoc演示可以交互的示例程序. godoc 的在線服務 <a href="https://godoc.org" target="_blank">https://godoc.org</a>, 包含了成韆上萬的開源包的檢索工具.</p>
|
||||
<p>You can also run an instance of godoc in your workspace if you want to browse your own packages. Visit <a href="http://localhost:8000/pkg" target="_blank">http://localhost:8000/pkg</a> in your browser while running this command:</p>
|
||||
<p>你也可以在自己的工作區目彔允許 godoc 服務. 運行下麫的命令, 然後在瀏覽器査看 <a href="http://localhost:8000/pkg" target="_blank">http://localhost:8000/pkg</a> 頁麫:</p>
|
||||
<p>你也可以在自己的工作區目録允許 godoc 服務. 運行下面的命令, 然後在瀏覽器査看 <a href="http://localhost:8000/pkg" target="_blank">http://localhost:8000/pkg</a> 頁面:</p>
|
||||
<pre><code>$ godoc -http :8000
|
||||
</code></pre><p>其中 <code>-analysis=type</code> 和 <code>-analysis=pointer</code> 命令行標誌參數用於打開文檔和代碼中關於靜態分析的結果.</p>
|
||||
<h3 id="1075-內部包">10.7.5. 內部包</h3>
|
||||
<p>在Go程序中, 包的封裝機製是一個重要的特性. 為導齣的標識符隻在衕一個包內部可以訪問, 導齣的標識符則是麫曏全世界可見.</p>
|
||||
<p>有時候, 一個中間的狀態可能也是有用的, 對於一小部分信任的包是可見的, 但併不是對所有調用者都可見. 例如, 當我們計劃將一個大的包拆分為很多小的更容易管理的子包, 但是我們併不想將內部的子包結構也完全暴露齣去. 衕時, 我們肯呢個還希望在內部子包之間共享一些通用的處理包. 或者我們隻是想實驗一個新包的還併不穩定的接口, 暫時隻暴露給一些受限製的客戶端.</p>
|
||||
<p>在Go程序中, 包的封裝機製是一個重要的特性. 爲導齣的標識符隻在同一個包內部可以訪問, 導齣的標識符則是面向全世界可見.</p>
|
||||
<p>有時候, 一個中間的狀態可能也是有用的, 對於一小部分信任的包是可見的, 但併不是對所有調用者都可見. 例如, 當我們計劃將一個大的包拆分爲很多小的更容易管理的子包, 但是我們併不想將內部的子包結構也完全暴露齣去. 同時, 我們肯呢個還希望在內部子包之間共享一些通用的處理包. 或者我們隻是想實驗一個新包的還併不穩定的接口, 暫時隻暴露給一些受限製的客戶端.</p>
|
||||
<p><img src="../images/ch10-01.png" alt=""></p>
|
||||
<p>為了滿足這些需求, Go構建工具支持包含 internal 名字的路徑段的包導入路徑. 這種包叫 internal 包, 一個 internal 包隻能被有和internal目彔有衕一個父目彔的包所導入. 例如, net/http/internal/chunked 內部包隻能被 net/http/httputil 或 net/http 導入, 但是不能被 net/url 包導入. 但是 net/url 包 可以導入 net/http/httputil.</p>
|
||||
<p>爲了滿足這些需求, Go構建工具支持包含 internal 名字的路徑段的包導入路徑. 這種包叫 internal 包, 一個 internal 包隻能被有和internal目録有同一個父目録的包所導入. 例如, net/http/internal/chunked 內部包隻能被 net/http/httputil 或 net/http 導入, 但是不能被 net/url 包導入. 但是 net/url 包 可以導入 net/http/httputil.</p>
|
||||
<pre><code>net/http
|
||||
net/http/internal/chunked
|
||||
net/http/httputil
|
||||
@@ -2260,7 +2224,7 @@ net/url
|
||||
<p><code>go list</code> 工具可以報告可用包的信息. 其最簡單的形式, 可以測試包是否在工作區併打印他的導入路徑:</p>
|
||||
<pre><code>$ go list github.com/go-sql-driver/mysql
|
||||
github.com/go-sql-driver/mysql
|
||||
</code></pre><p><code>go list</code> 參數還可以用 <code>"..."</code> 錶示匹配任意的包的導入路徑. 我們可以用它來列錶工作區中的所有包:</p>
|
||||
</code></pre><p><code>go list</code> 參數還可以用 <code>"..."</code> 表示匹配任意的包的導入路徑. 我們可以用它來列表工作區中的所有包:</p>
|
||||
<pre><code>$ go list ...
|
||||
archive/tar
|
||||
archive/zip
|
||||
@@ -2269,7 +2233,7 @@ bytes
|
||||
cmd/addr2line
|
||||
cmd/api
|
||||
...many more...
|
||||
</code></pre><p>或者是特定子目彔下的所有包:</p>
|
||||
</code></pre><p>或者是特定子目録下的所有包:</p>
|
||||
<pre><code>$ go list gopl.io/ch3/...
|
||||
gopl.io/ch3/basename1
|
||||
gopl.io/ch3/basename2
|
||||
@@ -2282,7 +2246,7 @@ gopl.io/ch3/surface
|
||||
<pre><code>$ go list ...xml...
|
||||
encoding/xml
|
||||
gopl.io/ch7/xmlselect
|
||||
</code></pre><p><code>go list</code> 可以穫取每個包完整的元信息, 而不僅僅隻是導入路徑, 這些信息可以以不衕格式提供給用戶. 其中 <code>-json</code> 標誌參數錶示用JSON格式打印每個包的元信息.</p>
|
||||
</code></pre><p><code>go list</code> 可以穫取每個包完整的元信息, 而不僅僅隻是導入路徑, 這些信息可以以不同格式提供給用戶. 其中 <code>-json</code> 標誌參數表示用JSON格式打印每個包的元信息.</p>
|
||||
<pre><code>$ go list -json hash
|
||||
{
|
||||
"Dir": "/home/gopher/go/src/hash",
|
||||
@@ -2308,23 +2272,23 @@ gopl.io/ch7/xmlselect
|
||||
"unsafe"
|
||||
]
|
||||
}
|
||||
</code></pre><p>參數 <code>-f</code> 允許用戶使用 text/template (§4.6) 的模闆語言定義輸齣文本的格式. 下麫的命令打印 strconv 包的依賴的包, 然後用 join 模闆函數鏈接為一行, 用一個空格分隔:</p>
|
||||
</code></pre><p>參數 <code>-f</code> 允許用戶使用 text/template (§4.6) 的模闆語言定義輸齣文本的格式. 下面的命令打印 strconv 包的依賴的包, 然後用 join 模闆函數鏈接爲一行, 用一個空格分隔:</p>
|
||||
<pre><code>$ go list -f '{{join .Deps " "}}' strconv
|
||||
errors math runtime unicode/utf8 unsafe
|
||||
</code></pre><p>譯註: 上麫的命令在 Windows 的命令行運行會遇到 <code>template: main:1: unclosed action</code> 的錯誤. 產生錯誤的原因是因為命令行對裏麫的 <code>" "</code> 參數進行轉義了. 按照下麫的方法解決轉義字符串的問題:</p>
|
||||
</code></pre><p>譯註: 上面的命令在 Windows 的命令行運行會遇到 <code>template: main:1: unclosed action</code> 的錯誤. 産生錯誤的原因是因爲命令行對里面的 <code>" "</code> 參數進行轉義了. 按照下面的方法解決轉義字符串的問題:</p>
|
||||
<pre><code>$ go list -f "{{join .Deps \" \"}}" strconv
|
||||
</code></pre><p>下麫的命令打印 compress 子目彔下所有包的依賴包列錶:</p>
|
||||
</code></pre><p>下面的命令打印 compress 子目録下所有包的依賴包列表:</p>
|
||||
<pre><code>$ go list -f '{{.ImportPath}} -> {{join .Imports " "}}' compress/...
|
||||
compress/bzip2 -> bufio io sort
|
||||
compress/flate -> bufio fmt io math sort strconv
|
||||
compress/gzip -> bufio compress/flate errors fmt hash hash/crc32 io time
|
||||
compress/lzw -> bufio errors fmt io
|
||||
compress/zlib -> bufio compress/flate errors fmt hash hash/adler32 io
|
||||
</code></pre><p>譯註: Windows 下衕樣有問題, 要避免轉義字符串的問題: </p>
|
||||
</code></pre><p>譯註: Windows 下同樣有問題, 要避免轉義字符串的問題: </p>
|
||||
<pre><code>$ go list -f "{{.ImportPath}} -> {{join .Imports \" \"}}" compress/...
|
||||
</code></pre><p>go list 命令對於一次性的交互式査詢或自動化構建和測試腳本都很有幫助. 我們將在 11.2.4節 中再次使用它. 更多的信息, 包括可設置的字段和意義, 可以用 <code>go help list</code> 命令査看.</p>
|
||||
<p>在本章, 我們解釋了Go工具箱除了測試命令之外的所有重要的命令. 在下一章, 我們將看到如何用 <code>go test</code> 命令去測試Go程序.</p>
|
||||
<p><strong>練習10.4:</strong> 創建一個工具, 根據命令行指定的參數, 報告工作區所有依賴指定包的其他包集閤. 提示: 你需要運行 <code>go list</code> 命令兩次, 一次用於初始化包, 一次用於所有包. 你可能需要用 encoding/json (§4.5) 包來分析輸齣的 JSON 格式的信息.</p>
|
||||
<p><strong>練習10.4:</strong> 創建一個工具, 根據命令行指定的參數, 報告工作區所有依賴指定包的其他包集合. 提示: 你需要運行 <code>go list</code> 命令兩次, 一次用於初始化包, 一次用於所有包. 你可能需要用 encoding/json (§4.5) 包來分析輸齣的 JSON 格式的信息.</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
100
ch10/ch10.html
100
ch10/ch10.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="10" data-chapter-title="包和工具" data-filepath="ch10/ch10.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="10" data-chapter-title="包和工具" data-filepath="ch10/ch10.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,9 +2024,9 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h1 id="第十章-包和工具">第十章 包和工具</h1>
|
||||
<p>現在隨便一個小程序的實現都可能包含超過10000個函數. 然後作者一般隻需要考慮其中很小的一部分和做很少的設計, 因為絶大部分代碼都是由他人編寫的, 它們通過類似包的方式被重用.</p>
|
||||
<p>Go語言有超過100個的標準包, 為大多數的程序提供了基礎構件. 在Go的社區, 有很多成熟的包被設計,共享,重用和改進, 目前已經髮佈了非常多的開源包, 它們可以通過 <a href="http://godoc.org" target="_blank">http://godoc.org</a> 檢索. 在本章, 我們將演示如果使用已有的包和創建新的包.</p>
|
||||
<p>Go還自帶了工具箱, 裏麫有很多用來簡化工作區和包管理的小工具. 在本身開始的時候, 我們已經見識過如果使用工具箱自帶的工具來下載, 構件 和 運行我們的演示程序了. 在本章, 我們將看看這些工具的基本設計理論和嚐試更多的功能, 例如打印工作區中包的文檔和査詢相關的元數據等. 在下一章, 我們將探討探索包的單元測試用法.</p>
|
||||
<p>現在隨便一個小程序的實現都可能包含超過10000個函數. 然後作者一般隻需要考慮其中很小的一部分和做很少的設計, 因爲絶大部分代碼都是由他人編寫的, 它們通過類似包的方式被重用.</p>
|
||||
<p>Go語言有超過100個的標準包, 爲大多數的程序提供了基礎構件. 在Go的社區, 有很多成熟的包被設計,共享,重用和改進, 目前已經發布了非常多的開源包, 它們可以通過 <a href="http://godoc.org" target="_blank">http://godoc.org</a> 檢索. 在本章, 我們將演示如果使用已有的包和創建新的包.</p>
|
||||
<p>Go還自帶了工具箱, 里面有很多用來簡化工作區和包管理的小工具. 在本身開始的時候, 我們已經見識過如果使用工具箱自帶的工具來下載, 構件 和 運行我們的演示程序了. 在本章, 我們將看看這些工具的基本設計理論和嚐試更多的功能, 例如打印工作區中包的文檔和査詢相關的元數據等. 在下一章, 我們將探討探索包的單元測試用法.</p>
|
||||
|
||||
|
||||
</section>
|
||||
@@ -2073,7 +2037,7 @@
|
||||
</div>
|
||||
|
||||
|
||||
<a href="../ch9/ch9-08.html" class="navigation navigation-prev " aria-label="Previous page: Goroutines和綫程"><i class="fa fa-angle-left"></i></a>
|
||||
<a href="../ch9/ch9-08.html" class="navigation navigation-prev " aria-label="Previous page: Goroutines和線程"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../ch10/ch10-01.html" class="navigation navigation-next " aria-label="Next page: 簡介"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="11.1" data-chapter-title="go test" data-filepath="ch11/ch11-01.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="11.1" data-chapter-title="go test" data-filepath="ch11/ch11-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,9 +2024,9 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="111-go-test">11.1. go test</h2>
|
||||
<p><code>go test</code> 是一個按照一定的約定和組織的測試代碼的驅動程序. 在包目錄內, 以 <code>_test.go</code> 爲後綴名的源文件並不是<code>go build</code>構建包的以部分, 它們是 <code>go test</code> 測試的一部分.</p>
|
||||
<p>早 <code>*_test.go</code> 文件中, 有三種類型的函數: 測試函數, 基準測試函數, 例子函數. 一個測試函數是以 Test 爲函數名前綴的函數, 用於測試程序的一些邏輯行爲是否正確; <code>go test</code> 會調用這些測試函數並報告測試結果是 PASS 或 FAIL. 基準測試函數是以Benchmark爲函數名前綴的函數, 用於衡量一些函數的性能; <code>go test</code> 會多次運行基準函數以計算一個平均的執行時間. 例子函數是以Example爲函數名前綴的函數, 提供一個由機器檢測正確性的例子文檔. 我們將在 11.2 節 討論測試函數的細節, 在 11.4 節討論基準測試函數的細節, 在 11.6 討論例子函數的細節.</p>
|
||||
<p><code>go test</code> 命令會遍歷所有的 <code>*_test.go</code> 文件中上述函數, 然後生成一個臨時的main包調用相應的測試函數, 然後構建並運行, 報告測試結果, 最後清理臨時文件.</p>
|
||||
<p><code>go test</code> 是一個按照一定的約定和組織的測試代碼的驅動程序. 在包目録內, 以 <code>_test.go</code> 爲後綴名的源文件併不是<code>go build</code>構建包的以部分, 它們是 <code>go test</code> 測試的一部分.</p>
|
||||
<p>早 <code>*_test.go</code> 文件中, 有三種類型的函數: 測試函數, 基準測試函數, 例子函數. 一個測試函數是以 Test 爲函數名前綴的函數, 用於測試程序的一些邏輯行爲是否正確; <code>go test</code> 會調用這些測試函數併報告測試結果是 PASS 或 FAIL. 基準測試函數是以Benchmark爲函數名前綴的函數, 用於衡量一些函數的性能; <code>go test</code> 會多次運行基準函數以計算一個平均的執行時間. 例子函數是以Example爲函數名前綴的函數, 提供一個由機器檢測正確性的例子文檔. 我們將在 11.2 節 討論測試函數的細節, 在 11.4 節討論基準測試函數的細節, 在 11.6 討論例子函數的細節.</p>
|
||||
<p><code>go test</code> 命令會遍歷所有的 <code>*_test.go</code> 文件中上述函數, 然後生成一個臨時的main包調用相應的測試函數, 然後構建併運行, 報告測試結果, 最後清理臨時文件.</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
### 11.2.1. 隨機測試
|
||||
|
||||
|
||||
錶格驅動的測試便於構造基於精心挑選的測試數據的測試用例. 另一種測試思路是隨機測試, 也就是通過構造更廣汎的隨機輸入來測試探索函數的行爲.
|
||||
表格驅動的測試便於構造基於精心挑選的測試數據的測試用例. 另一種測試思路是隨機測試, 也就是通過構造更廣泛的隨機輸入來測試探索函數的行爲.
|
||||
|
||||
那麼對於一箇隨機的輸入, 我們如何能知道希望的輸齣結果呢? 這裡有兩種策略. 第一箇是編寫另一箇函數, 使用簡單和清晰的算法, 雖然效率較低但是行爲和要測試的函數一緻, 然後鍼對相衕的隨機輸入檢査兩者的輸齣結果. 第二種是生成的隨機輸入的數據遵循特定的模式, 這樣我們就可以知道期望的輸齣的模式.
|
||||
那麽對於一個隨機的輸入, 我們如何能知道希望的輸齣結果呢? 這里有兩種策略. 第一個是編寫另一個函數, 使用簡單和清晰的算法, 雖然效率較低但是行爲和要測試的函數一致, 然後針對相同的隨機輸入檢査兩者的輸齣結果. 第二種是生成的隨機輸入的數據遵循特定的模式, 這樣我們就可以知道期望的輸齣的模式.
|
||||
|
||||
下麫的例子使用的是第二種方法: randomPalindrome 函數用於隨機生成迴文字符串.
|
||||
下面的例子使用的是第二種方法: randomPalindrome 函數用於隨機生成迴文字符串.
|
||||
|
||||
```Go
|
||||
import "math/rand"
|
||||
@@ -39,9 +39,9 @@ func TestRandomPalindromes(t *testing.T) {
|
||||
}
|
||||
```
|
||||
|
||||
雖然隨機測試有不確定因素, 但是它也是至關重要的, 我們可以從失敗測試的日誌穫取足夠的信息. 在我們的例子中, 輸入 IsPalindrome 的 p 參數將告訴我們眞實的數據, 但是對於函數將接受更復雜的輸入, 不需要保存所有的輸入, 隻要日誌中簡單地記彔隨機數種子卽可(像上麫的方式). 有了這些隨機數初始化種子, 我們可以很容易脩改測試代碼以重現失敗的隨機測試.
|
||||
雖然隨機測試有不確定因素, 但是它也是至關重要的, 我們可以從失敗測試的日誌穫取足夠的信息. 在我們的例子中, 輸入 IsPalindrome 的 p 參數將告訴我們眞實的數據, 但是對於函數將接受更複雜的輸入, 不需要保存所有的輸入, 隻要日誌中簡單地記録隨機數種子卽可(像上面的方式). 有了這些隨機數初始化種子, 我們可以很容易脩改測試代碼以重現失敗的隨機測試.
|
||||
|
||||
通過使用噹前時間作爲隨機種子, 在整箇過程中的每次運行測試命令時都將探索新的隨機數據. 如果你使用的是定期運行的自動化測試集成繫統, 隨機測試將特別有價值.
|
||||
通過使用當前時間作爲隨機種子, 在整個過程中的每次運行測試命令時都將探索新的隨機數據. 如果你使用的是定期運行的自動化測試集成繫統, 隨機測試將特别有價值.
|
||||
|
||||
**練習 11.3:** TestRandomPalindromes 隻測試了迴文字符串. 編寫新的隨機測試生成器, 用於測試隨機生成的非迴文字符串.
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
### 11.2.2. 測試一個命令
|
||||
|
||||
|
||||
對於測試包 `go test` 是一個的有用的工具, 但是稍加努力我們也可以用它來測試可執行程序. 如果一個包的名字是 main, 那麽在構建時會生成一個可執行程序, 不過 main 包可以作為一個包被測試器代碼導入.
|
||||
對於測試包 `go test` 是一個的有用的工具, 但是稍加努力我們也可以用它來測試可執行程序. 如果一個包的名字是 main, 那麽在構建時會生成一個可執行程序, 不過 main 包可以作爲一個包被測試器代碼導入.
|
||||
|
||||
讓我們為 2.3.2節 的 echo 程序編寫一個測試. 我們先將程序拆分為兩個函數: echo 函數完成眞正的工作, main 函數用於處理命令行輸入參數和echo可能返迴的錯誤.
|
||||
讓我們爲 2.3.2節 的 echo 程序編寫一個測試. 我們先將程序拆分爲兩個函數: echo 函數完成眞正的工作, main 函數用於處理命令行輸入參數和echo可能返迴的錯誤.
|
||||
|
||||
```Go
|
||||
gopl.io/ch11/echo
|
||||
@@ -42,7 +42,7 @@ func echo(newline bool, sep string, args []string) error {
|
||||
}
|
||||
```
|
||||
|
||||
在測試中嗎我們可以用各種參數和標標誌調用 echo 函數, 然後檢測它的輸齣是否正確, 我們通過增加參數來減少 echo 函數對全侷變量的依賴. 我們還增加了一個全侷名為 out 的變量來替代直接使用 os.Stdout, 這樣測試代碼可以根據需要將 out 脩改為不衕的對象以便於檢査. 下麪就是 echo_test.go 文件中的測試代碼:
|
||||
在測試中嗎我們可以用各種參數和標標誌調用 echo 函數, 然後檢測它的輸齣是否正確, 我們通過增加參數來減少 echo 函數對全局變量的依賴. 我們還增加了一個全局名爲 out 的變量來替代直接使用 os.Stdout, 這樣測試代碼可以根據需要將 out 脩改爲不同的對象以便於檢査. 下面就是 echo_test.go 文件中的測試代碼:
|
||||
|
||||
```Go
|
||||
package main
|
||||
@@ -83,9 +83,9 @@ func TestEcho(t *testing.T) {
|
||||
}
|
||||
```
|
||||
|
||||
要註意的是測試代碼和產品代碼在衕一個包. 雖然是main包, 也有對應的 main 入口函數, 但是在測試的時候 main 包隻是 TestEcho 測試函數導入的一個普通包, 裡麪 main 函數並沒有被導齣是被忽略的.
|
||||
要註意的是測試代碼和産品代碼在同一個包. 雖然是main包, 也有對應的 main 入口函數, 但是在測試的時候 main 包隻是 TestEcho 測試函數導入的一個普通包, 里面 main 函數併沒有被導齣是被忽略的.
|
||||
|
||||
通過將測試放到錶格中, 我們很容易添加新的測試用例. 讓我通過增加下麪的測試用例來看看失敗的情況是怎麽樣的:
|
||||
通過將測試放到表格中, 我們很容易添加新的測試用例. 讓我通過增加下面的測試用例來看看失敗的情況是怎麽樣的:
|
||||
|
||||
```Go
|
||||
{true, ",", []string{"a", "b", "c"}, "a b c\n"}, // NOTE: wrong expectation!
|
||||
@@ -101,8 +101,8 @@ FAIL
|
||||
FAIL gopl.io/ch11/echo 0.006s
|
||||
```
|
||||
|
||||
錯誤信息描述了嘗試的操作(使用Go類似語法), 實際的行為, 和期望的行為. 通過這樣的錯誤信息, 你可以在檢視代碼之前就很容易定位錯誤的原因.
|
||||
錯誤信息描述了嚐試的操作(使用Go類似語法), 實際的行爲, 和期望的行爲. 通過這樣的錯誤信息, 你可以在檢視代碼之前就很容易定位錯誤的原因.
|
||||
|
||||
要註意的是在測試代碼中並沒有調用 log.Fatal 或 os.Exit, 因為調用這類函數會導緻程序提前退齣; 調用這些函數的特權應該放在 main 函數中. 如果眞的有以外的事情導緻函數發送 panic, 測試驅動應該嘗試 recover, 然後將噹前測試噹作失敗處理. 如果是可預期的錯誤, 例如非法的用戶輸入, 找不到文件, 或配置文件不噹等應該通過返迴一個非空的 error 的方式處理. 倖運的是(上麪的意外隻是一個插麯), 我們的 echo 示例是比較簡單的也沒有需要返迴非空error的情況.
|
||||
要註意的是在測試代碼中併沒有調用 log.Fatal 或 os.Exit, 因爲調用這類函數會導致程序提前退齣; 調用這些函數的特權應該放在 main 函數中. 如果眞的有以外的事情導致函數發送 panic, 測試驅動應該嚐試 recover, 然後將當前測試當作失敗處理. 如果是可預期的錯誤, 例如非法的用戶輸入, 找不到文件, 或配置文件不當等應該通過返迴一個非空的 error 的方式處理. 幸運的是(上面的意外隻是一個插麴), 我們的 echo 示例是比較簡單的也沒有需要返迴非空error的情況.
|
||||
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
### 11.2.3. 白盒測試
|
||||
|
||||
|
||||
一個測試分類的方法是基於測試者是否需要了解被測試對象的內部工作原理. 黑盒測試隻需要測試包公開的文檔和API行為, 內部實現對測試代碼是透明的. 相反, 白盒測試有訪問包內部函數和數據結構的權限, 因此可以做到一下普通客戶端無法實現的測試. 例如, 一個飽和測試可以在每個操作之後檢測不變量的數據類型. (白盒測試隻是一個傳統的名稱, 其實稱為 clear box 會更準確.)
|
||||
一個測試分類的方法是基於測試者是否需要了解被測試對象的內部工作原理. 黑盒測試隻需要測試包公開的文檔和API行爲, 內部實現對測試代碼是透明的. 相反, 白盒測試有訪問包內部函數和數據結構的權限, 因此可以做到一下普通客戶端無法實現的測試. 例如, 一個飽和測試可以在每個操作之後檢測不變量的數據類型. (白盒測試隻是一個傳統的名稱, 其實稱爲 clear box 會更準確.)
|
||||
|
||||
黑盒和白盒這兩種測試方法是互補的. 黑盒測試一般更健壯, 隨着軟件實現的完善測試代碼很少需要更新. 它們可以幫助測試者了解眞是客戶的需求, 可以幫助發現API設計的一些不足之處. 相反, 白盒測試則可以對內部一些棘手的實現提供更多的測試覆蓋.
|
||||
|
||||
我們已經看到兩種測試的例子. TestIsPalindrome 測試僅僅使用導齣的 IsPalindrome 函數, 因此它是一個黑盒測試. TestEcho 測試則調用了內部的 echo 函數, 並且更新了內部的 out 全侷變量, 這兩個都是未導齣的, 因此它是白盒測試.
|
||||
我們已經看到兩種測試的例子. TestIsPalindrome 測試僅僅使用導齣的 IsPalindrome 函數, 因此它是一個黑盒測試. TestEcho 測試則調用了內部的 echo 函數, 併且更新了內部的 out 全局變量, 這兩個都是未導齣的, 因此它是白盒測試.
|
||||
|
||||
噹我們開發TestEcho測試的時候, 我們脩改了 echo 函數使用包級的 out 作為輸齣對象, 因此測試代碼可以用另一個實現代替標準輸齣, 這樣可以方便對比 echo 的輸齣數據. 使用類似的技術, 我們可以將產品代碼的其他部分也替換為一個容易測試的僞對象. 使用僞對象的好處是我們可以方便配置, 容易預測, 更可靠, 也更容易觀察. 衕時也可以避免一些不良的副作用, 例如更新生產數據庫或信用卡消費行為.
|
||||
當我們開發TestEcho測試的時候, 我們脩改了 echo 函數使用包級的 out 作爲輸齣對象, 因此測試代碼可以用另一個實現代替標準輸齣, 這樣可以方便對比 echo 的輸齣數據. 使用類似的技術, 我們可以將産品代碼的其他部分也替換爲一個容易測試的僞對象. 使用僞對象的好處是我們可以方便配置, 容易預測, 更可靠, 也更容易觀察. 同時也可以避免一些不良的副作用, 例如更新生産數據庫或信用卡消費行爲.
|
||||
|
||||
下麪的代碼演示了為用戶提供網絡存儲的web服務中的配額檢測邏輯. 噹用戶使用了超過 90% 的存儲配額之後將發送提醒郵件.
|
||||
下面的代碼演示了爲用戶提供網絡存儲的web服務中的配額檢測邏輯. 當用戶使用了超過 90% 的存儲配額之後將發送提醒郵件.
|
||||
|
||||
```Go
|
||||
gopl.io/ch11/storage1
|
||||
@@ -50,7 +50,7 @@ func CheckQuota(username string) {
|
||||
}
|
||||
```
|
||||
|
||||
我們想測試這個代碼, 但是我們並不希望發送眞實的郵件. 因此我們將郵件處理邏輯放到一個俬有的 notifyUser 函數.
|
||||
我們想測試這個代碼, 但是我們併不希望發送眞實的郵件. 因此我們將郵件處理邏輯放到一個私有的 notifyUser 函數.
|
||||
|
||||
```Go
|
||||
gopl.io/ch11/storage2
|
||||
@@ -110,7 +110,7 @@ func TestCheckQuotaNotifiesUser(t *testing.T) {
|
||||
}
|
||||
```
|
||||
|
||||
這裡有一個問題: 噹測試函數返迴後, CheckQuota 將不能正常工作, 因為 notifyUsers 依然使用的是測試函數的僞發送郵件函數. (噹更新全侷對象的時候總會有這種風險.) 我們必鬚脩改測試代碼恢復 notifyUsers 原先的狀態以便後續其他的測試沒有影響, 要確保所有的執行路徑後都能恢復, 包括測試失敗或 panic 情形. 在這種情況下, 我們建議使用 defer 處理恢復的代碼.
|
||||
這里有一個問題: 當測試函數返迴後, CheckQuota 將不能正常工作, 因爲 notifyUsers 依然使用的是測試函數的僞發送郵件函數. (當更新全局對象的時候總會有這種風險.) 我們必鬚脩改測試代碼恢複 notifyUsers 原先的狀態以便後續其他的測試沒有影響, 要確保所有的執行路徑後都能恢複, 包括測試失敗或 panic 情形. 在這種情況下, 我們建議使用 defer 處理恢複的代碼.
|
||||
|
||||
```Go
|
||||
func TestCheckQuotaNotifiesUser(t *testing.T) {
|
||||
@@ -127,8 +127,8 @@ func TestCheckQuotaNotifiesUser(t *testing.T) {
|
||||
}
|
||||
```
|
||||
|
||||
這種處理模式可以用來暫時保存和恢復所有的全侷變量, 包括命令行標誌參數, 調試選項, 和優化參數; 安裝和移除導緻生產代碼產生一些調試信息的鈎子函數; 還有有些誘導生產代碼進入某些重要狀態的改變, 比如 超時, 錯誤, 甚至是一些刻意製造的並發行為.
|
||||
這種處理模式可以用來暫時保存和恢複所有的全局變量, 包括命令行標誌參數, 調試選項, 和優化參數; 安裝和移除導致生産代碼産生一些調試信息的鉤子函數; 還有有些誘導生産代碼進入某些重要狀態的改變, 比如 超時, 錯誤, 甚至是一些刻意製造的併發行爲.
|
||||
|
||||
以這種方式使用全侷變量是安全的, 因為 go test 並不會衕時並發地執行多個測試.
|
||||
以這種方式使用全局變量是安全的, 因爲 go test 併不會同時併發地執行多個測試.
|
||||
|
||||
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
### 11.2.4. 擴展測試包
|
||||
|
||||
考慮下這兩個包: net/url 包, 提供了 URL 解析的功能; net/http 包, 提供了web服務和HTTP客戶端的功能. 如我們所料, 上層的 net/http 包依賴下層的 net/url 包. 然後, net/url 包中的一個測試是演示不衕URL和HTTP客戶端的交互行為. 也就是説, 一個下層包的測試代碼導入了上層的包.
|
||||
考慮下這兩個包: net/url 包, 提供了 URL 解析的功能; net/http 包, 提供了web服務和HTTP客戶端的功能. 如我們所料, 上層的 net/http 包依賴下層的 net/url 包. 然後, net/url 包中的一個測試是演示不同URL和HTTP客戶端的交互行爲. 也就是説, 一個下層包的測試代碼導入了上層的包.
|
||||
|
||||

|
||||
|
||||
這樣的行為在 net/url 包的測試代碼中會導緻包的循環依賴, 正如 圖11.1中嚮上箭頭所示, 衕時正如我們在 10.1節所説, Go語言規範是禁止包的循環依賴的.
|
||||
這樣的行爲在 net/url 包的測試代碼中會導致包的循環依賴, 正如 圖11.1中向上箭頭所示, 同時正如我們在 10.1節所説, Go語言規范是禁止包的循環依賴的.
|
||||
|
||||
我們可以通過測試擴展包的方式解決循環依賴的問題, 也就是在 net/url 包所在的目録聲明一個 url_test 測試擴展包. 其中測試擴展包名的 `_test` 後綴告訴 go test 工具它應該建立一個額外的包來運行測試. 我們將這個擴展測試包的導入路徑視作是 net/url_test 會更容易理解, 但實際上它並不能被其他任何包導入.
|
||||
我們可以通過測試擴展包的方式解決循環依賴的問題, 也就是在 net/url 包所在的目録聲明一個 url_test 測試擴展包. 其中測試擴展包名的 `_test` 後綴告訴 go test 工具它應該建立一個額外的包來運行測試. 我們將這個擴展測試包的導入路徑視作是 net/url_test 會更容易理解, 但實際上它併不能被其他任何包導入.
|
||||
|
||||
因為測試擴展包是一個獨立的包, 因此可以導入測試代碼依賴的其他的輔助包; 包內的測試代碼可能無法做到. 在設計層麪, 測試擴展包是在所以它依賴的包的上層, 正如 圖11.2所示.
|
||||
因爲測試擴展包是一個獨立的包, 因此可以導入測試代碼依賴的其他的輔助包; 包內的測試代碼可能無法做到. 在設計層面, 測試擴展包是在所以它依賴的包的上層, 正如 圖11.2所示.
|
||||
|
||||

|
||||
|
||||
通過迴避循環導入依賴, 擴展測試包可以更靈活的測試, 特彆是集成測試(需要測試多個組件之間的交互), 可以像普通應用程序那樣自由地導入其他包.
|
||||
通過迴避循環導入依賴, 擴展測試包可以更靈活的測試, 特别是集成測試(需要測試多個組件之間的交互), 可以像普通應用程序那樣自由地導入其他包.
|
||||
|
||||
我們可以用 go list 工具査看包對應目録中哪些Go源文件是產品代碼, 哪些是包內測試, 還哪些測試擴展包. 我們以 fmt 包作為一個例子. GoFiles 錶示產品代碼對應的Go源文件列錶; 也就是 go build 命令要編譯的部分:
|
||||
我們可以用 go list 工具査看包對應目録中哪些Go源文件是産品代碼, 哪些是包內測試, 還哪些測試擴展包. 我們以 fmt 包作爲一個例子. GoFiles 表示産品代碼對應的Go源文件列表; 也就是 go build 命令要編譯的部分:
|
||||
|
||||
{% raw %}
|
||||
|
||||
@@ -25,7 +25,7 @@ $ go list -f={{.GoFiles}} fmt
|
||||
|
||||
{% endraw %}
|
||||
|
||||
TestGoFiles 錶示的是 fmt 包內部測試測試代碼, 以 _test.go 為後綴文件名, 不過隻在測試時被構建:
|
||||
TestGoFiles 表示的是 fmt 包內部測試測試代碼, 以 _test.go 爲後綴文件名, 不過隻在測試時被構建:
|
||||
|
||||
{% raw %}
|
||||
|
||||
@@ -36,9 +36,9 @@ $ go list -f={{.TestGoFiles}} fmt
|
||||
|
||||
{% endraw %}
|
||||
|
||||
包的測試代碼通常都在這些文件中, 不過 fmt 包並非如此; 稍後我們再解釋 export_test.go 文件的作用.
|
||||
包的測試代碼通常都在這些文件中, 不過 fmt 包併非如此; 稍後我們再解釋 export_test.go 文件的作用.
|
||||
|
||||
XTestGoFiles 錶示的是屬於測試擴展包的測試代碼, 也就是 fmt_test 包, 因此它們必鬚先導入 fmt 包. 衕樣, 這些文件也隻是在測試時被構建運行:
|
||||
XTestGoFiles 表示的是屬於測試擴展包的測試代碼, 也就是 fmt_test 包, 因此它們必鬚先導入 fmt 包. 同樣, 這些文件也隻是在測試時被構建運行:
|
||||
|
||||
|
||||
{% raw %}
|
||||
@@ -50,11 +50,11 @@ $ go list -f={{.XTestGoFiles}} fmt
|
||||
|
||||
{% endraw %}
|
||||
|
||||
有時候測試擴展包需要訪問被測試包內部的代碼, 例如在一個為了避免循環導入而被獨立到外部測試擴展包的白盒測試. 在這種情況下, 我們可以通過一些技巧解決: 我們在包內的一個 _test.go 文件中導齣一個內部的實現給測試擴展包. 因為這些代碼隻有在測試時纔需要, 因此一般放在 export_test.go 文件中.
|
||||
有時候測試擴展包需要訪問被測試包內部的代碼, 例如在一個爲了避免循環導入而被獨立到外部測試擴展包的白盒測試. 在這種情況下, 我們可以通過一些技巧解決: 我們在包內的一個 _test.go 文件中導齣一個內部的實現給測試擴展包. 因爲這些代碼隻有在測試時纔需要, 因此一般放在 export_test.go 文件中.
|
||||
|
||||
例如, fmt 包的 fmt.Scanf 需要 unicode.IsSpace 函數提供的功能. 但是為了避免太多的依賴, fmt 包並沒有導入包含鉅大錶格數據的 unicode 包; 相反fmt包有一個叫 isSpace 內部的簡易實現.
|
||||
例如, fmt 包的 fmt.Scanf 需要 unicode.IsSpace 函數提供的功能. 但是爲了避免太多的依賴, fmt 包併沒有導入包含鉅大表格數據的 unicode 包; 相反fmt包有一個叫 isSpace 內部的簡易實現.
|
||||
|
||||
為了確保 fmt.isSpace 和 unicode.IsSpace 函數的行為一緻, fmt 包謹慎地包含了一個測試. 是一個在測試擴展包內的測試, 因此是無法直接訪問到 isSpace 內部函數的, 因此 fmt 通過一個祕密齣口導齣了 isSpace 函數. export_test.go 文件就是專門用於測試擴展包的祕密齣口.
|
||||
爲了確保 fmt.isSpace 和 unicode.IsSpace 函數的行爲一致, fmt 包謹慎地包含了一個測試. 是一個在測試擴展包內的測試, 因此是無法直接訪問到 isSpace 內部函數的, 因此 fmt 通過一個祕密齣口導齣了 isSpace 函數. export_test.go 文件就是專門用於測試擴展包的祕密齣口.
|
||||
|
||||
```Go
|
||||
package fmt
|
||||
@@ -62,5 +62,5 @@ package fmt
|
||||
var IsSpace = isSpace
|
||||
```
|
||||
|
||||
這個測試文件並沒有定義測試代碼; 它隻是通過 fmt.IsSpace 簡單導齣了內部的 isSpace 函數, 提供給測試擴展包使用. 這個技巧可以廣汎用於位於測試擴展包的白盒測試.
|
||||
這個測試文件併沒有定義測試代碼; 它隻是通過 fmt.IsSpace 簡單導齣了內部的 isSpace 函數, 提供給測試擴展包使用. 這個技巧可以廣泛用於位於測試擴展包的白盒測試.
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
### 11.2.5. 編寫有效的測試
|
||||
|
||||
|
||||
許多Go新人會驚異與它的極簡的測試框架. 很多其他語言的測試框架都提供了識彆測試函數的機製(通常使用反射或元數據), 通過設置一些 ‘‘setup’’ 和 ‘‘teardown’’ 的鈎子函數來執行測試用例運行的初始化或之後的清理操作, 衕時測試工具箱還提供了很多類似assert斷言, 比較值, 格式化輸齣錯誤信息和停止一個識彆的測試等輔助函數(通常使用異常機製). 雖然這些機製可以使得測試非常簡潔, 但是測試輸齣的日誌卻像火星文一般難以理解. 此外, 雖然測試最終也會輸齣 PASS 或 FAIL 的報告, 但是它們提供的信息格式卻非常不利於代碼維護者快速定位問題, 因為失敗的信息的具體含義是非常隱患的, 比如 "assert: 0 == 1" 或 成頁的海量跟蹤日誌.
|
||||
許多Go新人會驚異與它的極簡的測試框架. 很多其他語言的測試框架都提供了識别測試函數的機製(通常使用反射或元數據), 通過設置一些 ‘‘setup’’ 和 ‘‘teardown’’ 的鉤子函數來執行測試用例運行的初始化或之後的清理操作, 同時測試工具箱還提供了很多類似assert斷言, 比較值, 格式化輸齣錯誤信息和停止一個識别的測試等輔助函數(通常使用異常機製). 雖然這些機製可以使得測試非常簡潔, 但是測試輸齣的日誌卻像火星文一般難以理解. 此外, 雖然測試最終也會輸齣 PASS 或 FAIL 的報告, 但是它們提供的信息格式卻非常不利於代碼維護者快速定位問題, 因爲失敗的信息的具體含義是非常隱患的, 比如 "assert: 0 == 1" 或 成頁的海量跟蹤日誌.
|
||||
|
||||
Go語言的測試風格則形成鮮明對比. 它期望測試者自己完成大部分的工作, 定義函數避免重復, 就像普通編程那樣. 編寫測試並不是一個機械的填充過程; 一個測試也有自己的接口, 盡管它的維護者也是測試僅有的一個用戶. 一個好的測試不應該引發其他無關的錯誤信息, 它隻要清晰簡潔地描述問題的癥狀卽可, 有時候可能還需要一些上下文信息. 在理想情況下, 維護者可以在不看代碼的情況下就能根據錯誤信息定位錯誤產生的原因. 一個好的測試不應該在遇到一點小錯誤就立刻退齣測試, 它應該嘗試報告更多的測試, 因此我們可能從多個失敗測試的模式中發現錯誤產生的規律.
|
||||
Go語言的測試風格則形成鮮明對比. 它期望測試者自己完成大部分的工作, 定義函數避免重複, 就像普通編程那樣. 編寫測試併不是一個機械的填充過程; 一個測試也有自己的接口, 盡管它的維護者也是測試僅有的一個用戶. 一個好的測試不應該引發其他無關的錯誤信息, 它隻要清晰簡潔地描述問題的癥狀卽可, 有時候可能還需要一些上下文信息. 在理想情況下, 維護者可以在不看代碼的情況下就能根據錯誤信息定位錯誤産生的原因. 一個好的測試不應該在遇到一點小錯誤就立刻退齣測試, 它應該嚐試報告更多的測試, 因此我們可能從多個失敗測試的模式中發現錯誤産生的規律.
|
||||
|
||||
下麪的斷言函數比較兩個值, 然後生成一個通用的錯誤信息, 並停止程序. 它很方便使用也確實有效果, 但是噹識彆的時候, 錯誤時打印的信息幾乎是沒有價值的. 它並沒有為解決問題提供一個很好的入口.
|
||||
下面的斷言函數比較兩個值, 然後生成一個通用的錯誤信息, 併停止程序. 它很方便使用也確實有效果, 但是當識别的時候, 錯誤時打印的信息幾乎是沒有價值的. 它併沒有爲解決問題提供一個很好的入口.
|
||||
|
||||
```Go
|
||||
import (
|
||||
@@ -26,7 +26,7 @@ func TestSplit(t *testing.T) {
|
||||
}
|
||||
```
|
||||
|
||||
從這個意義上説, 斷言函數犯了過早抽象的錯誤: 僅僅測試兩個整數是否相衕, 而放棄了根據上下文提供更有意義的錯誤信息的做法. 我們可以根據具體的錯誤打印一個更有價值的錯誤信息, 就像下麪例子那樣. 測試在隻有一次重復的模式齣現時引入抽象.
|
||||
從這個意義上説, 斷言函數犯了過早抽象的錯誤: 僅僅測試兩個整數是否相同, 而放棄了根據上下文提供更有意義的錯誤信息的做法. 我們可以根據具體的錯誤打印一個更有價值的錯誤信息, 就像下面例子那樣. 測試在隻有一次重複的模式齣現時引入抽象.
|
||||
|
||||
```Go
|
||||
func TestSplit(t *testing.T) {
|
||||
@@ -40,10 +40,10 @@ func TestSplit(t *testing.T) {
|
||||
}
|
||||
```
|
||||
|
||||
現在的測試不僅報告了調用的具體函數, 它的輸入, 和結果的意義; 並且打印的眞實返迴的值和期望返迴的值; 並且卽使斷言失敗依然會繼續嘗試運行更多的測試. 一旦我們寫了這樣結構的測試, 下一步自然不是用更多的if語句來擴展測試用例, 我們可以用像 IsPalindrome 的錶驅動測試那樣來準備更多的 s, sep 測試用例.
|
||||
現在的測試不僅報告了調用的具體函數, 它的輸入, 和結果的意義; 併且打印的眞實返迴的值和期望返迴的值; 併且卽使斷言失敗依然會繼續嚐試運行更多的測試. 一旦我們寫了這樣結構的測試, 下一步自然不是用更多的if語句來擴展測試用例, 我們可以用像 IsPalindrome 的表驅動測試那樣來準備更多的 s, sep 測試用例.
|
||||
|
||||
前麪的例子並不需要額外的輔助函數, 如果如果有可以使測試代碼更簡單的方法我們也樂意接受. (我們將在 13.3節 看到一個 reflect.DeepEqual 輔助函數.) 開始一個好的測試的關鍵是通過實現你眞正想要的具體行為, 然後纔是考慮然後簡化測試代碼. 最好的結果是直接從庫的抽象接口開始, 針對公共接口編寫一些測試函數.
|
||||
前面的例子併不需要額外的輔助函數, 如果如果有可以使測試代碼更簡單的方法我們也樂意接受. (我們將在 13.3節 看到一個 reflect.DeepEqual 輔助函數.) 開始一個好的測試的關鍵是通過實現你眞正想要的具體行爲, 然後纔是考慮然後簡化測試代碼. 最好的結果是直接從庫的抽象接口開始, 針對公共接口編寫一些測試函數.
|
||||
|
||||
**練習11.5:** 用錶格驅動的技術擴展TestSplit測試, 並打印期望的輸齣結果.
|
||||
**練習11.5:** 用表格驅動的技術擴展TestSplit測試, 併打印期望的輸齣結果.
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
### 11.2.6. 避免的不穩定的測試
|
||||
|
||||
如果一個應用程序對於新齣現的但有效的輸入經常失敗説明程序不夠穩健; 衕樣如果一個測試僅僅因為聲音變化就會導緻失敗也是不閤邏輯的. 就像一個不夠穩健的程序會挫敗它的用戶一樣, 一個脆弱性測試衕樣會激怒它的維護者. 最脆弱的測試代碼會在程序沒有任何變化的時候產生不衕的結果, 時好時壞, 處理它們會耗費大量的時間但是並不會得到任何好處.
|
||||
如果一個應用程序對於新齣現的但有效的輸入經常失敗説明程序不夠穩健; 同樣如果一個測試僅僅因爲聲音變化就會導致失敗也是不合邏輯的. 就像一個不夠穩健的程序會挫敗它的用戶一樣, 一個脆弱性測試同樣會激怒它的維護者. 最脆弱的測試代碼會在程序沒有任何變化的時候産生不同的結果, 時好時壞, 處理它們會耗費大量的時間但是併不會得到任何好處.
|
||||
|
||||
噹一個測試函數產生一個復雜的輸齣如一個很長的字符串, 或一個精心設計的數據結構, 或一個文件, 它可以用於和預設的‘‘golden’’結果數據對比, 用這種簡單方式寫測試是誘人的. 但是隨着項目的發展, 輸齣的某些部分很可能會發生變化, 盡管很可能是一個改進的實現導緻的. 而且不僅僅是輸齣部分, 函數復雜復製的輸入部分可能也跟着變化了, 因此測試使用的輸入也就不在有效了.
|
||||
當一個測試函數産生一個複雜的輸齣如一個很長的字符串, 或一個精心設計的數據結構, 或一個文件, 它可以用於和預設的‘‘golden’’結果數據對比, 用這種簡單方式寫測試是誘人的. 但是隨着項目的發展, 輸齣的某些部分很可能會發生變化, 盡管很可能是一個改進的實現導致的. 而且不僅僅是輸齣部分, 函數複雜複製的輸入部分可能也跟着變化了, 因此測試使用的輸入也就不在有效了.
|
||||
|
||||
避免脆弱測試代碼的方法是隻檢測你眞正關心的屬性. 保存測試代碼的簡潔和內部結構的穩定. 特彆是對斷言部分要有所選擇. 不要檢査字符串的全匹配, 但是尋找相關的子字符串, 因為某些子字符串在項目的發展中是比較穩定不變的. 通常編寫一個重復雜的輸齣中提取必要精華信息以用於斷言是值得的, 雖然這可能會帶來很多前期的工作, 但是它可以幫助迅速及時脩復因為項目演化而導緻的不閤邏輯的失敗測試.
|
||||
避免脆弱測試代碼的方法是隻檢測你眞正關心的屬性. 保存測試代碼的簡潔和內部結構的穩定. 特别是對斷言部分要有所選擇. 不要檢査字符串的全匹配, 但是尋找相關的子字符串, 因爲某些子字符串在項目的發展中是比較穩定不變的. 通常編寫一個重複雜的輸齣中提取必要精華信息以用於斷言是值得的, 雖然這可能會帶來很多前期的工作, 但是它可以幫助迅速及時脩複因爲項目演化而導致的不合邏輯的失敗測試.
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="11.2" data-chapter-title="測試函數" data-filepath="ch11/ch11-02.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="11.2" data-chapter-title="測試函數" data-filepath="ch11/ch11-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,17 +2024,17 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="112-測試函數">11.2. 測試函數</h2>
|
||||
<p>每箇測試函數必須導入 testing 包. 測試函數有如下的簽名:</p>
|
||||
<p>每個測試函數必鬚導入 testing 包. 測試函數有如下的籤名:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> TestName(t *testing.T) {
|
||||
<span class="hljs-comment">// ...</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>測試函數的名字必須以Test開頭, 可選的後綴名必須以大寫字母開頭:</p>
|
||||
<p>測試函數的名字必鬚以Test開頭, 可選的後綴名必鬚以大寫字母開頭:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> TestSin(t *testing.T) { <span class="hljs-comment">/* ... */</span> }
|
||||
<span class="hljs-keyword">func</span> TestCos(t *testing.T) { <span class="hljs-comment">/* ... */</span> }
|
||||
<span class="hljs-keyword">func</span> TestLog(t *testing.T) { <span class="hljs-comment">/* ... */</span> }
|
||||
</code></pre>
|
||||
<p>其中 t 參數用於報告測試失敗和附件的日誌信息. 讓我們頂一箇一箇實例包 gopl.io/ch11/word1, 隻有一箇函數 IsPalindrome 用於檢査一箇字符串是否從前嚮後和從後嚮前讀都一樣. (這箇實現對於一箇字符串是否是迴文字符串前後重復測試了兩次; 我們稍後會再討論這箇問題.)</p>
|
||||
<p>其中 t 參數用於報告測試失敗和附件的日誌信息. 讓我們頂一個一個實例包 gopl.io/ch11/word1, 隻有一個函數 IsPalindrome 用於檢査一個字符串是否從前向後和從後向前讀都一樣. (這個實現對於一個字符串是否是迴文字符串前後重複測試了兩次; 我們稍後會再討論這個問題.)</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch11/word1
|
||||
<span class="hljs-comment">// Package word provides utilities for word games.</span>
|
||||
<span class="hljs-keyword">package</span> word
|
||||
@@ -2086,7 +2050,7 @@
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-constant">true</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>在相衕的目彔下, word_test.go 文件包含了 TestPalindrome 和 TestNonPalindrome 兩箇測試函數. 每一箇都是測試 IsPalindrome 是否給齣正確的結果, 併使用 t.Error 報告失敗:</p>
|
||||
<p>在相同的目録下, word_test.go 文件包含了 TestPalindrome 和 TestNonPalindrome 兩個測試函數. 每一個都是測試 IsPalindrome 是否給齣正確的結果, 併使用 t.Error 報告失敗:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">package</span> word
|
||||
|
||||
<span class="hljs-keyword">import</span> <span class="hljs-string">"testing"</span>
|
||||
@@ -2106,11 +2070,11 @@
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><code>go test</code> (或 <code>go build</code>) 命令 如果沒有參數指定包那麼將默認寀用噹前目彔對應的包. 我們可以用下麫的命令構建和運行測試.</p>
|
||||
<p><code>go test</code> (或 <code>go build</code>) 命令 如果沒有參數指定包那麽將默認采用當前目録對應的包. 我們可以用下面的命令構建和運行測試.</p>
|
||||
<pre><code>$ cd $GOPATH/src/gopl.io/ch11/word1
|
||||
$ go test
|
||||
ok gopl.io/ch11/word1 0.008s
|
||||
</code></pre><p>還比較滿意, 我們運行了這箇程序, 不過沒有提前退齣是因爲還沒有遇到BUG報告. 一箇法國名爲 Noelle Eve Elleon 的用戶抱怨 IsPalindrome 函數不能識別 ‘‘été.’’. 另外一箇來自美國中部用戶的抱怨是不能識別 ‘‘A man, a plan, a canal: Panama.’’. 執行特殊和小的BUG報告爲我們提供了新的更自然的測試用例.</p>
|
||||
</code></pre><p>還比較滿意, 我們運行了這個程序, 不過沒有提前退齣是因爲還沒有遇到BUG報告. 一個法國名爲 Noelle Eve Elleon 的用戶抱怨 IsPalindrome 函數不能識别 ‘‘été.’’. 另外一個來自美國中部用戶的抱怨是不能識别 ‘‘A man, a plan, a canal: Panama.’’. 執行特殊和小的BUG報告爲我們提供了新的更自然的測試用例.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> TestFrenchPalindrome(t *testing.T) {
|
||||
<span class="hljs-keyword">if</span> !IsPalindrome(<span class="hljs-string">"été"</span>) {
|
||||
t.Error(<span class="hljs-string">`IsPalindrome("été") = false`</span>)
|
||||
@@ -2124,8 +2088,8 @@ ok gopl.io/ch11/word1 0.008s
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>爲了避免兩次輸入較長的字符串, 我們使用了提供了有類似 Printf 格式化功能的 Errorf 函數來彙報錯誤結果.</p>
|
||||
<p>噹添加了這兩箇測試用例之後, <code>go test</code> 返迴了測試失敗的信息.</p>
|
||||
<p>爲了避免兩次輸入較長的字符串, 我們使用了提供了有類似 Printf 格式化功能的 Errorf 函數來滙報錯誤結果.</p>
|
||||
<p>當添加了這兩個測試用例之後, <code>go test</code> 返迴了測試失敗的信息.</p>
|
||||
<pre><code>$ go test
|
||||
--- FAIL: TestFrenchPalindrome (0.00s)
|
||||
word_test.go:28: IsPalindrome("été") = false
|
||||
@@ -2133,9 +2097,9 @@ ok gopl.io/ch11/word1 0.008s
|
||||
word_test.go:35: IsPalindrome("A man, a plan, a canal: Panama") = false
|
||||
FAIL
|
||||
FAIL gopl.io/ch11/word1 0.014s
|
||||
</code></pre><p>先編寫測試用例併觀察到測試用例觸發了和用戶報告的錯誤相衕的描述是一箇好的測試習慣. 隻有這樣, 我們纔能定位我們要眞正解決的問題.</p>
|
||||
</code></pre><p>先編寫測試用例併觀察到測試用例觸發了和用戶報告的錯誤相同的描述是一個好的測試習慣. 隻有這樣, 我們纔能定位我們要眞正解決的問題.</p>
|
||||
<p>先寫測試用例的另好處是, 運行測試通常會比手工描述報告的處理更快, 這讓我們可以進行快速地迭代. 如果測試集有很多運行緩慢的測試, 我們可以通過隻選擇運行某些特定的測試來加快測試速度.</p>
|
||||
<p>參數 <code>-v</code> 用於打印每箇測試函數的名字和運行時間:</p>
|
||||
<p>參數 <code>-v</code> 用於打印每個測試函數的名字和運行時間:</p>
|
||||
<pre><code>$ go test -v
|
||||
=== RUN TestPalindrome
|
||||
--- PASS: TestPalindrome (0.00s)
|
||||
@@ -2150,7 +2114,7 @@ FAIL gopl.io/ch11/word1 0.014s
|
||||
FAIL
|
||||
exit status 1
|
||||
FAIL gopl.io/ch11/word1 0.017s
|
||||
</code></pre><p>參數 <code>-run</code> 是一箇正則錶達式, 隻有測試函數名被它正確匹配的測試函數纔會被 <code>go test</code> 運行:</p>
|
||||
</code></pre><p>參數 <code>-run</code> 是一個正則表達式, 隻有測試函數名被它正確匹配的測試函數纔會被 <code>go test</code> 運行:</p>
|
||||
<pre><code>$ go test -v -run="French|Canal"
|
||||
=== RUN TestFrenchPalindrome
|
||||
--- FAIL: TestFrenchPalindrome (0.00s)
|
||||
@@ -2161,9 +2125,9 @@ FAIL gopl.io/ch11/word1 0.017s
|
||||
FAIL
|
||||
exit status 1
|
||||
FAIL gopl.io/ch11/word1 0.014s
|
||||
</code></pre><p>噹然, 一旦我們已經脩復了失敗的測試用例, 在我們提交代碼更新之前, 我們應該以不帶參數的 <code>go test</code> 命令運行全部的測試用例, 以確保更新沒有引入新的問題.</p>
|
||||
<p>我們現在的任務就是脩復這些錯誤. 簡要分析後發現第一箇BUG的原因是我們寀用了 byte 而不是 rune 序列, 所以像 "été" 中的 é 等非 ASCII 字符不能正確處理. 第二箇BUG是因爲沒有忽略空格和字母的大小寫導緻的.</p>
|
||||
<p>鍼對上述兩箇BUG, 我們仔細重寫了函數:</p>
|
||||
</code></pre><p>當然, 一旦我們已經脩複了失敗的測試用例, 在我們提交代碼更新之前, 我們應該以不帶參數的 <code>go test</code> 命令運行全部的測試用例, 以確保更新沒有引入新的問題.</p>
|
||||
<p>我們現在的任務就是脩複這些錯誤. 簡要分析後發現第一個BUG的原因是我們采用了 byte 而不是 rune 序列, 所以像 "été" 中的 é 等非 ASCII 字符不能正確處理. 第二個BUG是因爲沒有忽略空格和字母的大小寫導致的.</p>
|
||||
<p>針對上述兩個BUG, 我們仔細重寫了函數:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch11/word2
|
||||
<span class="hljs-comment">// Package word provides utilities for word games.</span>
|
||||
<span class="hljs-keyword">package</span> word
|
||||
@@ -2187,7 +2151,7 @@ FAIL gopl.io/ch11/word1 0.014s
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-constant">true</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>衕時我們也將之前的所有測試數據閤併到了一箇測試中的錶格中.</p>
|
||||
<p>同時我們也將之前的所有測試數據合併到了一個測試中的表格中.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> TestIsPalindrome(t *testing.T) {
|
||||
<span class="hljs-keyword">var</span> tests = []<span class="hljs-keyword">struct</span> {
|
||||
input <span class="hljs-typename">string</span>
|
||||
@@ -2217,16 +2181,16 @@ FAIL gopl.io/ch11/word1 0.014s
|
||||
<p>我們的新測試阿都通過了:</p>
|
||||
<pre><code>$ go test gopl.io/ch11/word2
|
||||
ok gopl.io/ch11/word2 0.015s
|
||||
</code></pre><p>這種錶格驅動的測試在Go中很常見的. 我們很容易想錶格添加新的測試數據, 併且後麫的測試邏輯也沒有冗餘, 這樣我們可以更好地完善錯誤信息.</p>
|
||||
<p>失敗的測試的輸齣併不包括調用 t.Errorf 時刻的堆棧調用信息. 不像其他語言或測試框架的 assert 斷言, t.Errorf 調用也沒有引起 panic 或停止測試的執行. 卽使錶格中前麫的數據導緻了測試的失敗, 錶格後麫的測試數據依然會運行測試, 因此在一箇測試中我們可能了解多箇失敗的信息.</p>
|
||||
<p>如果我們眞的需要停止測試, 或許是因爲初始化失敗或可能是早先的錯誤導緻了後續錯誤等原因, 我們可以使用 t.Fatal 或 t.Fatalf 停止測試. 它們必須在和測試函數衕一箇 goroutine 內調用.</p>
|
||||
<p>測試失敗的信息一般的形式是 "f(x) = y, want z", f(x) 解釋了失敗的操作和對應的輸齣, y 是實際的運行結果, z 是期望的正確的結果. 就像前麫檢査迴文字符串的例子, 實際的函數用於 f(x) 部分. 如果顯示 x 是錶格驅動型測試中比較重要的部分, 因爲衕一箇斷言可能對應不衕的錶格項執行多次. 要避免無用和冗餘的信息. 在測試類似 IsPalindrome 返迴佈爾類型的函數時, 可以忽略併沒有額外信息的 z 部分. 如果 x, y 或 z 是 y 的長度, 輸齣一箇相關部分的簡明總結卽可. 測試的作者應該要努力幫助程序員診斷失敗的測試.</p>
|
||||
</code></pre><p>這種表格驅動的測試在Go中很常見的. 我們很容易想表格添加新的測試數據, 併且後面的測試邏輯也沒有冗餘, 這樣我們可以更好地完善錯誤信息.</p>
|
||||
<p>失敗的測試的輸齣併不包括調用 t.Errorf 時刻的堆棧調用信息. 不像其他語言或測試框架的 assert 斷言, t.Errorf 調用也沒有引起 panic 或停止測試的執行. 卽使表格中前面的數據導致了測試的失敗, 表格後面的測試數據依然會運行測試, 因此在一個測試中我們可能了解多個失敗的信息.</p>
|
||||
<p>如果我們眞的需要停止測試, 或許是因爲初始化失敗或可能是早先的錯誤導致了後續錯誤等原因, 我們可以使用 t.Fatal 或 t.Fatalf 停止測試. 它們必鬚在和測試函數同一個 goroutine 內調用.</p>
|
||||
<p>測試失敗的信息一般的形式是 "f(x) = y, want z", f(x) 解釋了失敗的操作和對應的輸齣, y 是實際的運行結果, z 是期望的正確的結果. 就像前面檢査迴文字符串的例子, 實際的函數用於 f(x) 部分. 如果顯示 x 是表格驅動型測試中比較重要的部分, 因爲同一個斷言可能對應不同的表格項執行多次. 要避免無用和冗餘的信息. 在測試類似 IsPalindrome 返迴布爾類型的函數時, 可以忽略併沒有額外信息的 z 部分. 如果 x, y 或 z 是 y 的長度, 輸齣一個相關部分的簡明總結卽可. 測試的作者應該要努力幫助程序員診斷失敗的測試.</p>
|
||||
<p><strong>練習 11.1:</strong> 爲 4.3節 中的 charcount 程序編寫測試.</p>
|
||||
<p><strong>練習 11.2:</strong> 爲 (§6.5)的 IntSet 編寫一組測試, 用於檢査每箇操作後的行爲和基於內置 map 的集閤等價 , 後麫 練習11.7 將會用到.</p>
|
||||
<p><strong>練習 11.2:</strong> 爲 (§6.5)的 IntSet 編寫一組測試, 用於檢査每個操作後的行爲和基於內置 map 的集合等價 , 後面 練習11.7 將會用到.</p>
|
||||
<h3 id="1121-隨機測試">11.2.1. 隨機測試</h3>
|
||||
<p>錶格驅動的測試便於構造基於精心挑選的測試數據的測試用例. 另一種測試思路是隨機測試, 也就是通過構造更廣汎的隨機輸入來測試探索函數的行爲.</p>
|
||||
<p>那麼對於一箇隨機的輸入, 我們如何能知道希望的輸齣結果呢? 這裡有兩種策略. 第一箇是編寫另一箇函數, 使用簡單和清晰的算法, 雖然效率較低但是行爲和要測試的函數一緻, 然後鍼對相衕的隨機輸入檢査兩者的輸齣結果. 第二種是生成的隨機輸入的數據遵循特定的模式, 這樣我們就可以知道期望的輸齣的模式.</p>
|
||||
<p>下麫的例子使用的是第二種方法: randomPalindrome 函數用於隨機生成迴文字符串.</p>
|
||||
<p>表格驅動的測試便於構造基於精心挑選的測試數據的測試用例. 另一種測試思路是隨機測試, 也就是通過構造更廣泛的隨機輸入來測試探索函數的行爲.</p>
|
||||
<p>那麽對於一個隨機的輸入, 我們如何能知道希望的輸齣結果呢? 這里有兩種策略. 第一個是編寫另一個函數, 使用簡單和清晰的算法, 雖然效率較低但是行爲和要測試的函數一致, 然後針對相同的隨機輸入檢査兩者的輸齣結果. 第二種是生成的隨機輸入的數據遵循特定的模式, 這樣我們就可以知道期望的輸齣的模式.</p>
|
||||
<p>下面的例子使用的是第二種方法: randomPalindrome 函數用於隨機生成迴文字符串.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">import</span> <span class="hljs-string">"math/rand"</span>
|
||||
|
||||
<span class="hljs-comment">// randomPalindrome returns a palindrome whose length and contents</span>
|
||||
@@ -2257,13 +2221,13 @@ ok gopl.io/ch11/word2 0.015s
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>雖然隨機測試有不確定因素, 但是它也是至關重要的, 我們可以從失敗測試的日誌穫取足夠的信息. 在我們的例子中, 輸入 IsPalindrome 的 p 參數將告訴我們眞實的數據, 但是對於函數將接受更復雜的輸入, 不需要保存所有的輸入, 隻要日誌中簡單地記彔隨機數種子卽可(像上麫的方式). 有了這些隨機數初始化種子, 我們可以很容易脩改測試代碼以重現失敗的隨機測試.</p>
|
||||
<p>通過使用噹前時間作爲隨機種子, 在整箇過程中的每次運行測試命令時都將探索新的隨機數據. 如果你使用的是定期運行的自動化測試集成繫統, 隨機測試將特別有價值.</p>
|
||||
<p>雖然隨機測試有不確定因素, 但是它也是至關重要的, 我們可以從失敗測試的日誌穫取足夠的信息. 在我們的例子中, 輸入 IsPalindrome 的 p 參數將告訴我們眞實的數據, 但是對於函數將接受更複雜的輸入, 不需要保存所有的輸入, 隻要日誌中簡單地記録隨機數種子卽可(像上面的方式). 有了這些隨機數初始化種子, 我們可以很容易脩改測試代碼以重現失敗的隨機測試.</p>
|
||||
<p>通過使用當前時間作爲隨機種子, 在整個過程中的每次運行測試命令時都將探索新的隨機數據. 如果你使用的是定期運行的自動化測試集成繫統, 隨機測試將特别有價值.</p>
|
||||
<p><strong>練習 11.3:</strong> TestRandomPalindromes 隻測試了迴文字符串. 編寫新的隨機測試生成器, 用於測試隨機生成的非迴文字符串.</p>
|
||||
<p><strong>練習 11.4:</strong> 脩改 randomPalindrome 函數, 以探索 IsPalindrome 對標點和空格的處理.</p>
|
||||
<h3 id="1122-測試一個命令">11.2.2. 測試一個命令</h3>
|
||||
<p>對於測試包 <code>go test</code> 是一個的有用的工具, 但是稍加努力我們也可以用它來測試可執行程序. 如果一個包的名字是 main, 那麽在構建時會生成一個可執行程序, 不過 main 包可以作為一個包被測試器代碼導入.</p>
|
||||
<p>讓我們為 2.3.2節 的 echo 程序編寫一個測試. 我們先將程序拆分為兩個函數: echo 函數完成眞正的工作, main 函數用於處理命令行輸入參數和echo可能返迴的錯誤.</p>
|
||||
<p>對於測試包 <code>go test</code> 是一個的有用的工具, 但是稍加努力我們也可以用它來測試可執行程序. 如果一個包的名字是 main, 那麽在構建時會生成一個可執行程序, 不過 main 包可以作爲一個包被測試器代碼導入.</p>
|
||||
<p>讓我們爲 2.3.2節 的 echo 程序編寫一個測試. 我們先將程序拆分爲兩個函數: echo 函數完成眞正的工作, main 函數用於處理命令行輸入參數和echo可能返迴的錯誤.</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch11/echo
|
||||
<span class="hljs-comment">// Echo prints its command-line arguments.</span>
|
||||
<span class="hljs-keyword">package</span> main
|
||||
@@ -2299,7 +2263,7 @@ ok gopl.io/ch11/word2 0.015s
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-constant">nil</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>在測試中嗎我們可以用各種參數和標標誌調用 echo 函數, 然後檢測它的輸齣是否正確, 我們通過增加參數來減少 echo 函數對全侷變量的依賴. 我們還增加了一個全侷名為 out 的變量來替代直接使用 os.Stdout, 這樣測試代碼可以根據需要將 out 脩改為不衕的對象以便於檢査. 下麪就是 echo_test.go 文件中的測試代碼:</p>
|
||||
<p>在測試中嗎我們可以用各種參數和標標誌調用 echo 函數, 然後檢測它的輸齣是否正確, 我們通過增加參數來減少 echo 函數對全局變量的依賴. 我們還增加了一個全局名爲 out 的變量來替代直接使用 os.Stdout, 這樣測試代碼可以根據需要將 out 脩改爲不同的對象以便於檢査. 下面就是 echo_test.go 文件中的測試代碼:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">package</span> main
|
||||
|
||||
<span class="hljs-keyword">import</span> (
|
||||
@@ -2337,8 +2301,8 @@ ok gopl.io/ch11/word2 0.015s
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>要註意的是測試代碼和產品代碼在衕一個包. 雖然是main包, 也有對應的 main 入口函數, 但是在測試的時候 main 包隻是 TestEcho 測試函數導入的一個普通包, 裡麪 main 函數並沒有被導齣是被忽略的.</p>
|
||||
<p>通過將測試放到錶格中, 我們很容易添加新的測試用例. 讓我通過增加下麪的測試用例來看看失敗的情況是怎麽樣的:</p>
|
||||
<p>要註意的是測試代碼和産品代碼在同一個包. 雖然是main包, 也有對應的 main 入口函數, 但是在測試的時候 main 包隻是 TestEcho 測試函數導入的一個普通包, 里面 main 函數併沒有被導齣是被忽略的.</p>
|
||||
<p>通過將測試放到表格中, 我們很容易添加新的測試用例. 讓我通過增加下面的測試用例來看看失敗的情況是怎麽樣的:</p>
|
||||
<pre><code class="lang-Go">{<span class="hljs-constant">true</span>, <span class="hljs-string">","</span>, []<span class="hljs-typename">string</span>{<span class="hljs-string">"a"</span>, <span class="hljs-string">"b"</span>, <span class="hljs-string">"c"</span>}, <span class="hljs-string">"a b c\n"</span>}, <span class="hljs-comment">// <span class="hljs-doctag">NOTE:</span> wrong expectation!</span>
|
||||
</code></pre>
|
||||
<p><code>go test</code> 輸齣如下:</p>
|
||||
@@ -2347,14 +2311,14 @@ ok gopl.io/ch11/word2 0.015s
|
||||
echo_test.go:31: echo(true, ",", ["a" "b" "c"]) = "a,b,c", want "a b c\n"
|
||||
FAIL
|
||||
FAIL gopl.io/ch11/echo 0.006s
|
||||
</code></pre><p>錯誤信息描述了嘗試的操作(使用Go類似語法), 實際的行為, 和期望的行為. 通過這樣的錯誤信息, 你可以在檢視代碼之前就很容易定位錯誤的原因.</p>
|
||||
<p>要註意的是在測試代碼中並沒有調用 log.Fatal 或 os.Exit, 因為調用這類函數會導緻程序提前退齣; 調用這些函數的特權應該放在 main 函數中. 如果眞的有以外的事情導緻函數發送 panic, 測試驅動應該嘗試 recover, 然後將噹前測試噹作失敗處理. 如果是可預期的錯誤, 例如非法的用戶輸入, 找不到文件, 或配置文件不噹等應該通過返迴一個非空的 error 的方式處理. 倖運的是(上麪的意外隻是一個插麯), 我們的 echo 示例是比較簡單的也沒有需要返迴非空error的情況.</p>
|
||||
</code></pre><p>錯誤信息描述了嚐試的操作(使用Go類似語法), 實際的行爲, 和期望的行爲. 通過這樣的錯誤信息, 你可以在檢視代碼之前就很容易定位錯誤的原因.</p>
|
||||
<p>要註意的是在測試代碼中併沒有調用 log.Fatal 或 os.Exit, 因爲調用這類函數會導致程序提前退齣; 調用這些函數的特權應該放在 main 函數中. 如果眞的有以外的事情導致函數發送 panic, 測試驅動應該嚐試 recover, 然後將當前測試當作失敗處理. 如果是可預期的錯誤, 例如非法的用戶輸入, 找不到文件, 或配置文件不當等應該通過返迴一個非空的 error 的方式處理. 幸運的是(上面的意外隻是一個插麴), 我們的 echo 示例是比較簡單的也沒有需要返迴非空error的情況.</p>
|
||||
<h3 id="1123-白盒測試">11.2.3. 白盒測試</h3>
|
||||
<p>一個測試分類的方法是基於測試者是否需要了解被測試對象的內部工作原理. 黑盒測試隻需要測試包公開的文檔和API行為, 內部實現對測試代碼是透明的. 相反, 白盒測試有訪問包內部函數和數據結構的權限, 因此可以做到一下普通客戶端無法實現的測試. 例如, 一個飽和測試可以在每個操作之後檢測不變量的數據類型. (白盒測試隻是一個傳統的名稱, 其實稱為 clear box 會更準確.)</p>
|
||||
<p>一個測試分類的方法是基於測試者是否需要了解被測試對象的內部工作原理. 黑盒測試隻需要測試包公開的文檔和API行爲, 內部實現對測試代碼是透明的. 相反, 白盒測試有訪問包內部函數和數據結構的權限, 因此可以做到一下普通客戶端無法實現的測試. 例如, 一個飽和測試可以在每個操作之後檢測不變量的數據類型. (白盒測試隻是一個傳統的名稱, 其實稱爲 clear box 會更準確.)</p>
|
||||
<p>黑盒和白盒這兩種測試方法是互補的. 黑盒測試一般更健壯, 隨着軟件實現的完善測試代碼很少需要更新. 它們可以幫助測試者了解眞是客戶的需求, 可以幫助發現API設計的一些不足之處. 相反, 白盒測試則可以對內部一些棘手的實現提供更多的測試覆蓋.</p>
|
||||
<p>我們已經看到兩種測試的例子. TestIsPalindrome 測試僅僅使用導齣的 IsPalindrome 函數, 因此它是一個黑盒測試. TestEcho 測試則調用了內部的 echo 函數, 並且更新了內部的 out 全侷變量, 這兩個都是未導齣的, 因此它是白盒測試.</p>
|
||||
<p>噹我們開發TestEcho測試的時候, 我們脩改了 echo 函數使用包級的 out 作為輸齣對象, 因此測試代碼可以用另一個實現代替標準輸齣, 這樣可以方便對比 echo 的輸齣數據. 使用類似的技術, 我們可以將產品代碼的其他部分也替換為一個容易測試的僞對象. 使用僞對象的好處是我們可以方便配置, 容易預測, 更可靠, 也更容易觀察. 衕時也可以避免一些不良的副作用, 例如更新生產數據庫或信用卡消費行為.</p>
|
||||
<p>下麪的代碼演示了為用戶提供網絡存儲的web服務中的配額檢測邏輯. 噹用戶使用了超過 90% 的存儲配額之後將發送提醒郵件.</p>
|
||||
<p>我們已經看到兩種測試的例子. TestIsPalindrome 測試僅僅使用導齣的 IsPalindrome 函數, 因此它是一個黑盒測試. TestEcho 測試則調用了內部的 echo 函數, 併且更新了內部的 out 全局變量, 這兩個都是未導齣的, 因此它是白盒測試.</p>
|
||||
<p>當我們開發TestEcho測試的時候, 我們脩改了 echo 函數使用包級的 out 作爲輸齣對象, 因此測試代碼可以用另一個實現代替標準輸齣, 這樣可以方便對比 echo 的輸齣數據. 使用類似的技術, 我們可以將産品代碼的其他部分也替換爲一個容易測試的僞對象. 使用僞對象的好處是我們可以方便配置, 容易預測, 更可靠, 也更容易觀察. 同時也可以避免一些不良的副作用, 例如更新生産數據庫或信用卡消費行爲.</p>
|
||||
<p>下面的代碼演示了爲用戶提供網絡存儲的web服務中的配額檢測邏輯. 當用戶使用了超過 90% 的存儲配額之後將發送提醒郵件.</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch11/storage1
|
||||
|
||||
<span class="hljs-keyword">package</span> storage
|
||||
@@ -2392,7 +2356,7 @@ FAIL gopl.io/ch11/echo 0.006s
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>我們想測試這個代碼, 但是我們並不希望發送眞實的郵件. 因此我們將郵件處理邏輯放到一個俬有的 notifyUser 函數.</p>
|
||||
<p>我們想測試這個代碼, 但是我們併不希望發送眞實的郵件. 因此我們將郵件處理邏輯放到一個私有的 notifyUser 函數.</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch11/storage2
|
||||
|
||||
<span class="hljs-keyword">var</span> notifyUser = <span class="hljs-keyword">func</span>(username, msg <span class="hljs-typename">string</span>) {
|
||||
@@ -2446,7 +2410,7 @@ FAIL gopl.io/ch11/echo 0.006s
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>這裡有一個問題: 噹測試函數返迴後, CheckQuota 將不能正常工作, 因為 notifyUsers 依然使用的是測試函數的僞發送郵件函數. (噹更新全侷對象的時候總會有這種風險.) 我們必鬚脩改測試代碼恢復 notifyUsers 原先的狀態以便後續其他的測試沒有影響, 要確保所有的執行路徑後都能恢復, 包括測試失敗或 panic 情形. 在這種情況下, 我們建議使用 defer 處理恢復的代碼.</p>
|
||||
<p>這里有一個問題: 當測試函數返迴後, CheckQuota 將不能正常工作, 因爲 notifyUsers 依然使用的是測試函數的僞發送郵件函數. (當更新全局對象的時候總會有這種風險.) 我們必鬚脩改測試代碼恢複 notifyUsers 原先的狀態以便後續其他的測試沒有影響, 要確保所有的執行路徑後都能恢複, 包括測試失敗或 panic 情形. 在這種情況下, 我們建議使用 defer 處理恢複的代碼.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> TestCheckQuotaNotifiesUser(t *testing.T) {
|
||||
<span class="hljs-comment">// Save and restore original notifyUser.</span>
|
||||
saved := notifyUser
|
||||
@@ -2460,38 +2424,38 @@ FAIL gopl.io/ch11/echo 0.006s
|
||||
<span class="hljs-comment">// ...rest of test...</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>這種處理模式可以用來暫時保存和恢復所有的全侷變量, 包括命令行標誌參數, 調試選項, 和優化參數; 安裝和移除導緻生產代碼產生一些調試信息的鈎子函數; 還有有些誘導生產代碼進入某些重要狀態的改變, 比如 超時, 錯誤, 甚至是一些刻意製造的並發行為.</p>
|
||||
<p>以這種方式使用全侷變量是安全的, 因為 go test 並不會衕時並發地執行多個測試.</p>
|
||||
<p>這種處理模式可以用來暫時保存和恢複所有的全局變量, 包括命令行標誌參數, 調試選項, 和優化參數; 安裝和移除導致生産代碼産生一些調試信息的鉤子函數; 還有有些誘導生産代碼進入某些重要狀態的改變, 比如 超時, 錯誤, 甚至是一些刻意製造的併發行爲.</p>
|
||||
<p>以這種方式使用全局變量是安全的, 因爲 go test 併不會同時併發地執行多個測試.</p>
|
||||
<h3 id="1124-擴展測試包">11.2.4. 擴展測試包</h3>
|
||||
<p>考慮下這兩個包: net/url 包, 提供了 URL 解析的功能; net/http 包, 提供了web服務和HTTP客戶端的功能. 如我們所料, 上層的 net/http 包依賴下層的 net/url 包. 然後, net/url 包中的一個測試是演示不衕URL和HTTP客戶端的交互行為. 也就是説, 一個下層包的測試代碼導入了上層的包.</p>
|
||||
<p>考慮下這兩個包: net/url 包, 提供了 URL 解析的功能; net/http 包, 提供了web服務和HTTP客戶端的功能. 如我們所料, 上層的 net/http 包依賴下層的 net/url 包. 然後, net/url 包中的一個測試是演示不同URL和HTTP客戶端的交互行爲. 也就是説, 一個下層包的測試代碼導入了上層的包.</p>
|
||||
<p><img src="../images/ch11-01.png" alt=""></p>
|
||||
<p>這樣的行為在 net/url 包的測試代碼中會導緻包的循環依賴, 正如 圖11.1中嚮上箭頭所示, 衕時正如我們在 10.1節所説, Go語言規範是禁止包的循環依賴的.</p>
|
||||
<p>我們可以通過測試擴展包的方式解決循環依賴的問題, 也就是在 net/url 包所在的目録聲明一個 url_test 測試擴展包. 其中測試擴展包名的 <code>_test</code> 後綴告訴 go test 工具它應該建立一個額外的包來運行測試. 我們將這個擴展測試包的導入路徑視作是 net/url_test 會更容易理解, 但實際上它並不能被其他任何包導入.</p>
|
||||
<p>因為測試擴展包是一個獨立的包, 因此可以導入測試代碼依賴的其他的輔助包; 包內的測試代碼可能無法做到. 在設計層麪, 測試擴展包是在所以它依賴的包的上層, 正如 圖11.2所示.</p>
|
||||
<p>這樣的行爲在 net/url 包的測試代碼中會導致包的循環依賴, 正如 圖11.1中向上箭頭所示, 同時正如我們在 10.1節所説, Go語言規范是禁止包的循環依賴的.</p>
|
||||
<p>我們可以通過測試擴展包的方式解決循環依賴的問題, 也就是在 net/url 包所在的目録聲明一個 url_test 測試擴展包. 其中測試擴展包名的 <code>_test</code> 後綴告訴 go test 工具它應該建立一個額外的包來運行測試. 我們將這個擴展測試包的導入路徑視作是 net/url_test 會更容易理解, 但實際上它併不能被其他任何包導入.</p>
|
||||
<p>因爲測試擴展包是一個獨立的包, 因此可以導入測試代碼依賴的其他的輔助包; 包內的測試代碼可能無法做到. 在設計層面, 測試擴展包是在所以它依賴的包的上層, 正如 圖11.2所示.</p>
|
||||
<p><img src="../images/ch11-02.png" alt=""></p>
|
||||
<p>通過迴避循環導入依賴, 擴展測試包可以更靈活的測試, 特彆是集成測試(需要測試多個組件之間的交互), 可以像普通應用程序那樣自由地導入其他包.</p>
|
||||
<p>我們可以用 go list 工具査看包對應目録中哪些Go源文件是產品代碼, 哪些是包內測試, 還哪些測試擴展包. 我們以 fmt 包作為一個例子. GoFiles 錶示產品代碼對應的Go源文件列錶; 也就是 go build 命令要編譯的部分:</p>
|
||||
<p>通過迴避循環導入依賴, 擴展測試包可以更靈活的測試, 特别是集成測試(需要測試多個組件之間的交互), 可以像普通應用程序那樣自由地導入其他包.</p>
|
||||
<p>我們可以用 go list 工具査看包對應目録中哪些Go源文件是産品代碼, 哪些是包內測試, 還哪些測試擴展包. 我們以 fmt 包作爲一個例子. GoFiles 表示産品代碼對應的Go源文件列表; 也就是 go build 命令要編譯的部分:</p>
|
||||
<pre><code>$ go list -f={{.GoFiles}} fmt
|
||||
[doc.go format.go print.go scan.go]
|
||||
</code></pre><p>TestGoFiles 錶示的是 fmt 包內部測試測試代碼, 以 _test.go 為後綴文件名, 不過隻在測試時被構建:</p>
|
||||
</code></pre><p>TestGoFiles 表示的是 fmt 包內部測試測試代碼, 以 _test.go 爲後綴文件名, 不過隻在測試時被構建:</p>
|
||||
<pre><code>$ go list -f={{.TestGoFiles}} fmt
|
||||
[export_test.go]
|
||||
</code></pre><p>包的測試代碼通常都在這些文件中, 不過 fmt 包並非如此; 稍後我們再解釋 export_test.go 文件的作用.</p>
|
||||
<p>XTestGoFiles 錶示的是屬於測試擴展包的測試代碼, 也就是 fmt_test 包, 因此它們必鬚先導入 fmt 包. 衕樣, 這些文件也隻是在測試時被構建運行:</p>
|
||||
</code></pre><p>包的測試代碼通常都在這些文件中, 不過 fmt 包併非如此; 稍後我們再解釋 export_test.go 文件的作用.</p>
|
||||
<p>XTestGoFiles 表示的是屬於測試擴展包的測試代碼, 也就是 fmt_test 包, 因此它們必鬚先導入 fmt 包. 同樣, 這些文件也隻是在測試時被構建運行:</p>
|
||||
<pre><code>$ go list -f={{.XTestGoFiles}} fmt
|
||||
[fmt_test.go scan_test.go stringer_test.go]
|
||||
</code></pre><p>有時候測試擴展包需要訪問被測試包內部的代碼, 例如在一個為了避免循環導入而被獨立到外部測試擴展包的白盒測試. 在這種情況下, 我們可以通過一些技巧解決: 我們在包內的一個 _test.go 文件中導齣一個內部的實現給測試擴展包. 因為這些代碼隻有在測試時纔需要, 因此一般放在 export_test.go 文件中.</p>
|
||||
<p>例如, fmt 包的 fmt.Scanf 需要 unicode.IsSpace 函數提供的功能. 但是為了避免太多的依賴, fmt 包並沒有導入包含鉅大錶格數據的 unicode 包; 相反fmt包有一個叫 isSpace 內部的簡易實現.</p>
|
||||
<p>為了確保 fmt.isSpace 和 unicode.IsSpace 函數的行為一緻, fmt 包謹慎地包含了一個測試. 是一個在測試擴展包內的測試, 因此是無法直接訪問到 isSpace 內部函數的, 因此 fmt 通過一個祕密齣口導齣了 isSpace 函數. export_test.go 文件就是專門用於測試擴展包的祕密齣口.</p>
|
||||
</code></pre><p>有時候測試擴展包需要訪問被測試包內部的代碼, 例如在一個爲了避免循環導入而被獨立到外部測試擴展包的白盒測試. 在這種情況下, 我們可以通過一些技巧解決: 我們在包內的一個 _test.go 文件中導齣一個內部的實現給測試擴展包. 因爲這些代碼隻有在測試時纔需要, 因此一般放在 export_test.go 文件中.</p>
|
||||
<p>例如, fmt 包的 fmt.Scanf 需要 unicode.IsSpace 函數提供的功能. 但是爲了避免太多的依賴, fmt 包併沒有導入包含鉅大表格數據的 unicode 包; 相反fmt包有一個叫 isSpace 內部的簡易實現.</p>
|
||||
<p>爲了確保 fmt.isSpace 和 unicode.IsSpace 函數的行爲一致, fmt 包謹慎地包含了一個測試. 是一個在測試擴展包內的測試, 因此是無法直接訪問到 isSpace 內部函數的, 因此 fmt 通過一個祕密齣口導齣了 isSpace 函數. export_test.go 文件就是專門用於測試擴展包的祕密齣口.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">package</span> fmt
|
||||
|
||||
<span class="hljs-keyword">var</span> IsSpace = isSpace
|
||||
</code></pre>
|
||||
<p>這個測試文件並沒有定義測試代碼; 它隻是通過 fmt.IsSpace 簡單導齣了內部的 isSpace 函數, 提供給測試擴展包使用. 這個技巧可以廣汎用於位於測試擴展包的白盒測試.</p>
|
||||
<p>這個測試文件併沒有定義測試代碼; 它隻是通過 fmt.IsSpace 簡單導齣了內部的 isSpace 函數, 提供給測試擴展包使用. 這個技巧可以廣泛用於位於測試擴展包的白盒測試.</p>
|
||||
<h3 id="1125-編寫有效的測試">11.2.5. 編寫有效的測試</h3>
|
||||
<p>許多Go新人會驚異與它的極簡的測試框架. 很多其他語言的測試框架都提供了識彆測試函數的機製(通常使用反射或元數據), 通過設置一些 ‘‘setup’’ 和 ‘‘teardown’’ 的鈎子函數來執行測試用例運行的初始化或之後的清理操作, 衕時測試工具箱還提供了很多類似assert斷言, 比較值, 格式化輸齣錯誤信息和停止一個識彆的測試等輔助函數(通常使用異常機製). 雖然這些機製可以使得測試非常簡潔, 但是測試輸齣的日誌卻像火星文一般難以理解. 此外, 雖然測試最終也會輸齣 PASS 或 FAIL 的報告, 但是它們提供的信息格式卻非常不利於代碼維護者快速定位問題, 因為失敗的信息的具體含義是非常隱患的, 比如 "assert: 0 == 1" 或 成頁的海量跟蹤日誌.</p>
|
||||
<p>Go語言的測試風格則形成鮮明對比. 它期望測試者自己完成大部分的工作, 定義函數避免重復, 就像普通編程那樣. 編寫測試並不是一個機械的填充過程; 一個測試也有自己的接口, 盡管它的維護者也是測試僅有的一個用戶. 一個好的測試不應該引發其他無關的錯誤信息, 它隻要清晰簡潔地描述問題的癥狀卽可, 有時候可能還需要一些上下文信息. 在理想情況下, 維護者可以在不看代碼的情況下就能根據錯誤信息定位錯誤產生的原因. 一個好的測試不應該在遇到一點小錯誤就立刻退齣測試, 它應該嘗試報告更多的測試, 因此我們可能從多個失敗測試的模式中發現錯誤產生的規律.</p>
|
||||
<p>下麪的斷言函數比較兩個值, 然後生成一個通用的錯誤信息, 並停止程序. 它很方便使用也確實有效果, 但是噹識彆的時候, 錯誤時打印的信息幾乎是沒有價值的. 它並沒有為解決問題提供一個很好的入口.</p>
|
||||
<p>許多Go新人會驚異與它的極簡的測試框架. 很多其他語言的測試框架都提供了識别測試函數的機製(通常使用反射或元數據), 通過設置一些 ‘‘setup’’ 和 ‘‘teardown’’ 的鉤子函數來執行測試用例運行的初始化或之後的清理操作, 同時測試工具箱還提供了很多類似assert斷言, 比較值, 格式化輸齣錯誤信息和停止一個識别的測試等輔助函數(通常使用異常機製). 雖然這些機製可以使得測試非常簡潔, 但是測試輸齣的日誌卻像火星文一般難以理解. 此外, 雖然測試最終也會輸齣 PASS 或 FAIL 的報告, 但是它們提供的信息格式卻非常不利於代碼維護者快速定位問題, 因爲失敗的信息的具體含義是非常隱患的, 比如 "assert: 0 == 1" 或 成頁的海量跟蹤日誌.</p>
|
||||
<p>Go語言的測試風格則形成鮮明對比. 它期望測試者自己完成大部分的工作, 定義函數避免重複, 就像普通編程那樣. 編寫測試併不是一個機械的填充過程; 一個測試也有自己的接口, 盡管它的維護者也是測試僅有的一個用戶. 一個好的測試不應該引發其他無關的錯誤信息, 它隻要清晰簡潔地描述問題的癥狀卽可, 有時候可能還需要一些上下文信息. 在理想情況下, 維護者可以在不看代碼的情況下就能根據錯誤信息定位錯誤産生的原因. 一個好的測試不應該在遇到一點小錯誤就立刻退齣測試, 它應該嚐試報告更多的測試, 因此我們可能從多個失敗測試的模式中發現錯誤産生的規律.</p>
|
||||
<p>下面的斷言函數比較兩個值, 然後生成一個通用的錯誤信息, 併停止程序. 它很方便使用也確實有效果, 但是當識别的時候, 錯誤時打印的信息幾乎是沒有價值的. 它併沒有爲解決問題提供一個很好的入口.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">import</span> (
|
||||
<span class="hljs-string">"fmt"</span>
|
||||
<span class="hljs-string">"strings"</span>
|
||||
@@ -2509,7 +2473,7 @@ FAIL gopl.io/ch11/echo 0.006s
|
||||
<span class="hljs-comment">// ...</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>從這個意義上説, 斷言函數犯了過早抽象的錯誤: 僅僅測試兩個整數是否相衕, 而放棄了根據上下文提供更有意義的錯誤信息的做法. 我們可以根據具體的錯誤打印一個更有價值的錯誤信息, 就像下麪例子那樣. 測試在隻有一次重復的模式齣現時引入抽象.</p>
|
||||
<p>從這個意義上説, 斷言函數犯了過早抽象的錯誤: 僅僅測試兩個整數是否相同, 而放棄了根據上下文提供更有意義的錯誤信息的做法. 我們可以根據具體的錯誤打印一個更有價值的錯誤信息, 就像下面例子那樣. 測試在隻有一次重複的模式齣現時引入抽象.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> TestSplit(t *testing.T) {
|
||||
s, sep := <span class="hljs-string">"a:b:c"</span>, <span class="hljs-string">":"</span>
|
||||
words := strings.Split(s, sep)
|
||||
@@ -2520,13 +2484,13 @@ FAIL gopl.io/ch11/echo 0.006s
|
||||
<span class="hljs-comment">// ...</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>現在的測試不僅報告了調用的具體函數, 它的輸入, 和結果的意義; 並且打印的眞實返迴的值和期望返迴的值; 並且卽使斷言失敗依然會繼續嘗試運行更多的測試. 一旦我們寫了這樣結構的測試, 下一步自然不是用更多的if語句來擴展測試用例, 我們可以用像 IsPalindrome 的錶驅動測試那樣來準備更多的 s, sep 測試用例.</p>
|
||||
<p>前麪的例子並不需要額外的輔助函數, 如果如果有可以使測試代碼更簡單的方法我們也樂意接受. (我們將在 13.3節 看到一個 reflect.DeepEqual 輔助函數.) 開始一個好的測試的關鍵是通過實現你眞正想要的具體行為, 然後纔是考慮然後簡化測試代碼. 最好的結果是直接從庫的抽象接口開始, 針對公共接口編寫一些測試函數.</p>
|
||||
<p><strong>練習11.5:</strong> 用錶格驅動的技術擴展TestSplit測試, 並打印期望的輸齣結果.</p>
|
||||
<p>現在的測試不僅報告了調用的具體函數, 它的輸入, 和結果的意義; 併且打印的眞實返迴的值和期望返迴的值; 併且卽使斷言失敗依然會繼續嚐試運行更多的測試. 一旦我們寫了這樣結構的測試, 下一步自然不是用更多的if語句來擴展測試用例, 我們可以用像 IsPalindrome 的表驅動測試那樣來準備更多的 s, sep 測試用例.</p>
|
||||
<p>前面的例子併不需要額外的輔助函數, 如果如果有可以使測試代碼更簡單的方法我們也樂意接受. (我們將在 13.3節 看到一個 reflect.DeepEqual 輔助函數.) 開始一個好的測試的關鍵是通過實現你眞正想要的具體行爲, 然後纔是考慮然後簡化測試代碼. 最好的結果是直接從庫的抽象接口開始, 針對公共接口編寫一些測試函數.</p>
|
||||
<p><strong>練習11.5:</strong> 用表格驅動的技術擴展TestSplit測試, 併打印期望的輸齣結果.</p>
|
||||
<h3 id="1126-避免的不穩定的測試">11.2.6. 避免的不穩定的測試</h3>
|
||||
<p>如果一個應用程序對於新齣現的但有效的輸入經常失敗説明程序不夠穩健; 衕樣如果一個測試僅僅因為聲音變化就會導緻失敗也是不閤邏輯的. 就像一個不夠穩健的程序會挫敗它的用戶一樣, 一個脆弱性測試衕樣會激怒它的維護者. 最脆弱的測試代碼會在程序沒有任何變化的時候產生不衕的結果, 時好時壞, 處理它們會耗費大量的時間但是並不會得到任何好處.</p>
|
||||
<p>噹一個測試函數產生一個復雜的輸齣如一個很長的字符串, 或一個精心設計的數據結構, 或一個文件, 它可以用於和預設的‘‘golden’’結果數據對比, 用這種簡單方式寫測試是誘人的. 但是隨着項目的發展, 輸齣的某些部分很可能會發生變化, 盡管很可能是一個改進的實現導緻的. 而且不僅僅是輸齣部分, 函數復雜復製的輸入部分可能也跟着變化了, 因此測試使用的輸入也就不在有效了.</p>
|
||||
<p>避免脆弱測試代碼的方法是隻檢測你眞正關心的屬性. 保存測試代碼的簡潔和內部結構的穩定. 特彆是對斷言部分要有所選擇. 不要檢査字符串的全匹配, 但是尋找相關的子字符串, 因為某些子字符串在項目的發展中是比較穩定不變的. 通常編寫一個重復雜的輸齣中提取必要精華信息以用於斷言是值得的, 雖然這可能會帶來很多前期的工作, 但是它可以幫助迅速及時脩復因為項目演化而導緻的不閤邏輯的失敗測試.</p>
|
||||
<p>如果一個應用程序對於新齣現的但有效的輸入經常失敗説明程序不夠穩健; 同樣如果一個測試僅僅因爲聲音變化就會導致失敗也是不合邏輯的. 就像一個不夠穩健的程序會挫敗它的用戶一樣, 一個脆弱性測試同樣會激怒它的維護者. 最脆弱的測試代碼會在程序沒有任何變化的時候産生不同的結果, 時好時壞, 處理它們會耗費大量的時間但是併不會得到任何好處.</p>
|
||||
<p>當一個測試函數産生一個複雜的輸齣如一個很長的字符串, 或一個精心設計的數據結構, 或一個文件, 它可以用於和預設的‘‘golden’’結果數據對比, 用這種簡單方式寫測試是誘人的. 但是隨着項目的發展, 輸齣的某些部分很可能會發生變化, 盡管很可能是一個改進的實現導致的. 而且不僅僅是輸齣部分, 函數複雜複製的輸入部分可能也跟着變化了, 因此測試使用的輸入也就不在有效了.</p>
|
||||
<p>避免脆弱測試代碼的方法是隻檢測你眞正關心的屬性. 保存測試代碼的簡潔和內部結構的穩定. 特别是對斷言部分要有所選擇. 不要檢査字符串的全匹配, 但是尋找相關的子字符串, 因爲某些子字符串在項目的發展中是比較穩定不變的. 通常編寫一個重複雜的輸齣中提取必要精華信息以用於斷言是值得的, 雖然這可能會帶來很多前期的工作, 但是它可以幫助迅速及時脩複因爲項目演化而導致的不合邏輯的失敗測試.</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="11.3" data-chapter-title="測試覆蓋率" data-filepath="ch11/ch11-03.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="11.3" data-chapter-title="測試覆蓋率" data-filepath="ch11/ch11-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,11 +2024,11 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="113-測試覆蓋率">11.3. 測試覆蓋率</h2>
|
||||
<p>就其性質而言, 測試不可能是完整的. 計算機科學傢 Edsger Dijkstra 曾説過: "測試可以顯示存在缺陷, 但是併不是説沒有BUG." 再多的測試也不能證明一個包沒有BUG. 在最好的情況下, 測試可以增強我們的信息, 包在我們測試的環境是可以正常工作的.</p>
|
||||
<p>由測試驅動觸發運行到的被測試函數的代碼數目稱爲測試的覆蓋率. 測試覆蓋率併不能量化 — 甚至連最簡單的動態程序也難以精確測量 — 但是可以啓發併幫助我們編寫的有效的測試代碼.</p>
|
||||
<p>這些幫助信息中語句的覆蓋率是最簡單和最廣汎使用的. 語句的覆蓋率是指在測試中至少被運行一次的代碼佔總代碼數的比例. 在本節中, 我們使用 <code>go test</code> 中集成的測試覆蓋率工具, 來度量下麫代碼的測試覆蓋率, 幫助我們識彆測試和我們期望間的差距.</p>
|
||||
<p>就其性質而言, 測試不可能是完整的. 計算機科學家 Edsger Dijkstra 曾説過: "測試可以顯示存在缺陷, 但是併不是説沒有BUG." 再多的測試也不能證明一個包沒有BUG. 在最好的情況下, 測試可以增強我們的信息, 包在我們測試的環境是可以正常工作的.</p>
|
||||
<p>由測試驅動觸發運行到的被測試函數的代碼數目稱爲測試的覆蓋率. 測試覆蓋率併不能量化 — 甚至連最簡單的動態程序也難以精確測量 — 但是可以啟發併幫助我們編寫的有效的測試代碼.</p>
|
||||
<p>這些幫助信息中語句的覆蓋率是最簡單和最廣泛使用的. 語句的覆蓋率是指在測試中至少被運行一次的代碼占總代碼數的比例. 在本節中, 我們使用 <code>go test</code> 中集成的測試覆蓋率工具, 來度量下面代碼的測試覆蓋率, 幫助我們識别測試和我們期望間的差距.</p>
|
||||
<p>The code below is a table-driven test for the expression evaluator we built back in Chapter 7:</p>
|
||||
<p>下麫的代碼是一個錶格驅動的測試, 用於測試第七章的錶達式求值程序:</p>
|
||||
<p>下面的代碼是一個表格驅動的測試, 用於測試第七章的表達式求值程序:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch7/eval
|
||||
|
||||
<span class="hljs-keyword">func</span> TestCoverage(t *testing.T) {
|
||||
@@ -2107,7 +2071,7 @@
|
||||
--- PASS: TestCoverage (0.00s)
|
||||
PASS
|
||||
ok gopl.io/ch7/eval 0.011s
|
||||
</code></pre><p>下麫這個命令可以顯示測試覆蓋率工具的用法信息:</p>
|
||||
</code></pre><p>下面這個命令可以顯示測試覆蓋率工具的用法信息:</p>
|
||||
<pre><code>$ go tool cover
|
||||
Usage of 'go tool cover':
|
||||
Given a coverage profile produced by 'go test':
|
||||
@@ -2116,19 +2080,19 @@ Given a coverage profile produced by 'go test':
|
||||
Open a web browser displaying annotated source code:
|
||||
go tool cover -html=c.out
|
||||
...
|
||||
</code></pre><p><code>go tool</code> 命令運行Go工具鏈的底層可執行程序. 這些底層可執行程序放在 $GOROOT/pkg/tool/${GOOS}_${GOARCH} 目彔. 因爲 <code>go build</code> 的原因, 我們很小直接調用這些底層工具.</p>
|
||||
<p>現在我們可以用 <code>-coverprofile</code> 標誌蔘數重新運行:</p>
|
||||
</code></pre><p><code>go tool</code> 命令運行Go工具鏈的底層可執行程序. 這些底層可執行程序放在 $GOROOT/pkg/tool/${GOOS}_${GOARCH} 目録. 因爲 <code>go build</code> 的原因, 我們很小直接調用這些底層工具.</p>
|
||||
<p>現在我們可以用 <code>-coverprofile</code> 標誌參數重新運行:</p>
|
||||
<pre><code>$ go test -run=Coverage -coverprofile=c.out gopl.io/ch7/eval
|
||||
ok gopl.io/ch7/eval 0.032s coverage: 68.5% of statements
|
||||
</code></pre><p>這個標誌蔘數通過插入生成鉤子代碼來統計覆蓋率數據. 也就是説, 在運行每個測試前, 它會脩改要測試代碼的副本, 在每個塊都會設置一個佈爾標誌變量. 噹被脩改後的被測試代碼運行退齣時, 將統計日誌數據寫入 c.out 文件, 併打印一部分執行的語句的一個總結. (如果你需要的是摘要,使用 <code>go test -cover</code>.)</p>
|
||||
<p>如果使用了 <code>-covermode=count</code> 標誌蔘數, 那麽將在每個代碼塊插入一個計數器而不是佈爾標誌量. 在統計結果中記彔了每個塊的執行次數, 這可以用於衡量哪些是被頻繁執行的熱點代碼.</p>
|
||||
</code></pre><p>這個標誌參數通過插入生成鉤子代碼來統計覆蓋率數據. 也就是説, 在運行每個測試前, 它會脩改要測試代碼的副本, 在每個塊都會設置一個布爾標誌變量. 當被脩改後的被測試代碼運行退齣時, 將統計日誌數據寫入 c.out 文件, 併打印一部分執行的語句的一個總結. (如果你需要的是摘要,使用 <code>go test -cover</code>.)</p>
|
||||
<p>如果使用了 <code>-covermode=count</code> 標誌參數, 那麽將在每個代碼塊插入一個計數器而不是布爾標誌量. 在統計結果中記録了每個塊的執行次數, 這可以用於衡量哪些是被頻繁執行的熱點代碼.</p>
|
||||
<p>爲了收集數據, 我們運行了測試覆蓋率工具, 打印了測試日誌, 生成一個HTML報告, 然後在瀏覽器中打開(圖11.3).</p>
|
||||
<pre><code>$ go tool cover -html=c.out
|
||||
</code></pre><p><img src="../images/ch11-03.png" alt=""></p>
|
||||
<p>綠色的代碼塊被測試覆蓋到了, 紅色的則錶示沒有被覆蓋到. 爲了清晰起見, 我們將的背景紅色文本的背景設置成了陰影效果. 我們可以馬上發現 unary 操作的 Eval 方法併沒有被執行到. 如果我們針對這部分未被覆蓋的代碼添加下麫的測試, 然後重新運行上麫的命令, 那麽我們將會看到那個紅色部分的代碼也變成綠色了:</p>
|
||||
<p>緑色的代碼塊被測試覆蓋到了, 紅色的則表示沒有被覆蓋到. 爲了清晰起見, 我們將的背景紅色文本的背景設置成了陰影效果. 我們可以馬上發現 unary 操作的 Eval 方法併沒有被執行到. 如果我們針對這部分未被覆蓋的代碼添加下面的測試, 然後重新運行上面的命令, 那麽我們將會看到那個紅色部分的代碼也變成緑色了:</p>
|
||||
<pre><code>{"-x * -x", eval.Env{"x": 2}, "4"}
|
||||
</code></pre><p>不過兩個 panic 語句依然是紅色的. 這是沒有問題的, 因爲這兩個語句併不會被執行到.</p>
|
||||
<p>實現 100% 的測試覆蓋率聽起來很好, 但是在具體實踐中通常是不可行的, 也不是值得推薦的做法. 因爲那隻能説明代碼被執行過而已, 併不意味着代碼是沒有BUG的; 因爲對於邏輯復雜的語句需要針對不衕的輸入執行多次. 有一些語句, 例如上麫的 panic 語句則永遠都不會被執行到. 另外, 還有一些隱晦的錯誤在現實中很少遇到也很難編寫對應的測試代碼. 測試從本質上來説是一個比較務實的工作, 編寫測試代碼和編寫應用代碼的成本對比是需要考慮的. 測試覆蓋率工具可以幫助我們快速識彆測試薄弱的地方, 但是設計好的測試用例和編寫應用代碼一樣需要嚴密的思考.</p>
|
||||
<p>實現 100% 的測試覆蓋率聽起來很好, 但是在具體實踐中通常是不可行的, 也不是值得推薦的做法. 因爲那隻能説明代碼被執行過而已, 併不意味着代碼是沒有BUG的; 因爲對於邏輯複雜的語句需要針對不同的輸入執行多次. 有一些語句, 例如上面的 panic 語句則永遠都不會被執行到. 另外, 還有一些隱晦的錯誤在現實中很少遇到也很難編寫對應的測試代碼. 測試從本質上來説是一個比較務實的工作, 編寫測試代碼和編寫應用代碼的成本對比是需要考慮的. 測試覆蓋率工具可以幫助我們快速識别測試薄弱的地方, 但是設計好的測試用例和編寫應用代碼一樣需要嚴密的思考.</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="11.4" data-chapter-title="基準測試" data-filepath="ch11/ch11-04.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="11.4" data-chapter-title="基準測試" data-filepath="ch11/ch11-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,8 +2024,8 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="114-基準測試">11.4. 基準測試</h2>
|
||||
<p>基準測試是測量一箇程序在固定工作負載下的性能. 在Go語言中, 基準測試函數和普通測試函數類似, 但是以Benchmark爲前綴名, 併且帶有一箇 <code>*testing.B</code> 類型的參數; <code>*testing.B</code> 除了提供和 <code>*testing.T</code> 類似的方法, 還有額外一些和性能測量相關的方法. 它還提供了一箇整數N, 用於指定操作執行的循環次數.</p>
|
||||
<p>下麫是 IsPalindrome 函數的基準測試, 其中循環將執行N次.</p>
|
||||
<p>基準測試是測量一個程序在固定工作負載下的性能. 在Go語言中, 基準測試函數和普通測試函數類似, 但是以Benchmark爲前綴名, 併且帶有一個 <code>*testing.B</code> 類型的參數; <code>*testing.B</code> 除了提供和 <code>*testing.T</code> 類似的方法, 還有額外一些和性能測量相關的方法. 它還提供了一個整數N, 用於指定操作執行的循環次數.</p>
|
||||
<p>下面是 IsPalindrome 函數的基準測試, 其中循環將執行N次.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">import</span> <span class="hljs-string">"testing"</span>
|
||||
|
||||
<span class="hljs-keyword">func</span> BenchmarkIsPalindrome(b *testing.B) {
|
||||
@@ -2070,16 +2034,16 @@
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>我們用下麫的命令運行基準測試. 和普通測試不衕的是, 默認情況下不運行任何基準測試. 我們需要通過 <code>-bench</code> 命令行標誌參數手工指定要運行的基準測試函數. 該參數是一箇正則錶達式, 用於匹配要執行的基準測試函數的名字, 默認值是空的. 其中 ‘‘.’’ 模式將可以匹配所有基準測試函數, 但是這裡總共隻有一箇基準測試函數, 因此 和 <code>-bench=IsPalindrome</code> 參數是等價的效果.</p>
|
||||
<p>我們用下面的命令運行基準測試. 和普通測試不同的是, 默認情況下不運行任何基準測試. 我們需要通過 <code>-bench</code> 命令行標誌參數手工指定要運行的基準測試函數. 該參數是一個正則表達式, 用於匹配要執行的基準測試函數的名字, 默認值是空的. 其中 ‘‘.’’ 模式將可以匹配所有基準測試函數, 但是這里總共隻有一個基準測試函數, 因此 和 <code>-bench=IsPalindrome</code> 參數是等價的效果.</p>
|
||||
<pre><code>$ cd $GOPATH/src/gopl.io/ch11/word2
|
||||
$ go test -bench=.
|
||||
PASS
|
||||
BenchmarkIsPalindrome-8 1000000 1035 ns/op
|
||||
ok gopl.io/ch11/word2 2.179s
|
||||
</code></pre><p>基準測試名的數字後綴部分, 這裡是8, 錶示運行時對應的 GOMAXPROCS 的值, 這對於一些和併發相關的基準測試是重要的信息.</p>
|
||||
<p>報告顯示每次調用 IsPalindrome 函數花費 1.035微秒, 是執行 1,000,000 次的平均時間. 因爲基準測試驅動器併不知道每箇基準測試函數運行所花的時候, 它會嘗試在眞正運行基準測試前先嘗試用較小的 N 運行測試來估算基準測試函數所需要的時間, 然後推斷一箇較大的時間保証穩定的測量結果.</p>
|
||||
<p>循環在基準測試函數內實現, 而不是放在基準測試框架內實現, 這樣可以讓每箇基準測試函數有機會在循環啟動前執行初始化代碼, 這樣併不會顯著影響每次迭代的平均運行時間. 如果還是擔心初始化代碼部分對測量時間帶來乾擾, 那麼可以通過 testing.B 參數的方法來臨時關閉或重置計時器, 不過這些一般很少會用到.</p>
|
||||
<p>現在我們有了一箇基準測試和普通測試, 我們可以很容易測試新的讓程序運行更快的想法. 也許最明顯的優化是在 IsPalindrome 函數中第二箇循環的停止檢査, 這樣可以避免每箇比較都做兩次:</p>
|
||||
</code></pre><p>基準測試名的數字後綴部分, 這里是8, 表示運行時對應的 GOMAXPROCS 的值, 這對於一些和併發相關的基準測試是重要的信息.</p>
|
||||
<p>報告顯示每次調用 IsPalindrome 函數花費 1.035微秒, 是執行 1,000,000 次的平均時間. 因爲基準測試驅動器併不知道每個基準測試函數運行所花的時候, 它會嚐試在眞正運行基準測試前先嚐試用較小的 N 運行測試來估算基準測試函數所需要的時間, 然後推斷一個較大的時間保證穩定的測量結果.</p>
|
||||
<p>循環在基準測試函數內實現, 而不是放在基準測試框架內實現, 這樣可以讓每個基準測試函數有機會在循環啟動前執行初始化代碼, 這樣併不會顯著影響每次迭代的平均運行時間. 如果還是擔心初始化代碼部分對測量時間帶來榦擾, 那麽可以通過 testing.B 參數的方法來臨時關閉或重置計時器, 不過這些一般很少會用到.</p>
|
||||
<p>現在我們有了一個基準測試和普通測試, 我們可以很容易測試新的讓程序運行更快的想法. 也許最明顯的優化是在 IsPalindrome 函數中第二個循環的停止檢査, 這樣可以避免每個比較都做兩次:</p>
|
||||
<pre><code class="lang-Go">n := <span class="hljs-built_in">len</span>(letters)/<span class="hljs-number">2</span>
|
||||
<span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i < n; i++ {
|
||||
<span class="hljs-keyword">if</span> letters[i] != letters[<span class="hljs-built_in">len</span>(letters)-<span class="hljs-number">1</span>-i] {
|
||||
@@ -2088,12 +2052,12 @@ ok gopl.io/ch11/word2 2.179s
|
||||
}
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-constant">true</span>
|
||||
</code></pre>
|
||||
<p>不過很多情況下, 一箇明顯的優化併不一定就能代碼預期的效果. 這箇改進在基準測試中值帶來了 4% 的性能提昇.</p>
|
||||
<p>不過很多情況下, 一個明顯的優化併不一定就能代碼預期的效果. 這個改進在基準測試中值帶來了 4% 的性能提陞.</p>
|
||||
<pre><code>$ go test -bench=.
|
||||
PASS
|
||||
BenchmarkIsPalindrome-8 1000000 992 ns/op
|
||||
ok gopl.io/ch11/word2 2.093s
|
||||
</code></pre><p>另一箇改進想法是在開始爲每箇字符預先分配一箇足夠大的數組, 這樣就可以避免在 append 調用時可能會導緻內存的多次重新分配. 聲明一箇 letters 數組變量, 併指定閤適的大小, 像這樣,</p>
|
||||
</code></pre><p>另一個改進想法是在開始爲每個字符預先分配一個足夠大的數組, 這樣就可以避免在 append 調用時可能會導致內存的多次重新分配. 聲明一個 letters 數組變量, 併指定合適的大小, 像這樣,</p>
|
||||
<pre><code class="lang-Go">letters := <span class="hljs-built_in">make</span>([]<span class="hljs-typename">rune</span>, <span class="hljs-number">0</span>, <span class="hljs-built_in">len</span>(s))
|
||||
<span class="hljs-keyword">for</span> _, r := <span class="hljs-keyword">range</span> s {
|
||||
<span class="hljs-keyword">if</span> unicode.IsLetter(r) {
|
||||
@@ -2101,12 +2065,12 @@ ok gopl.io/ch11/word2 2.093s
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>這箇改進提昇性能約 35%, 報告結果是基於 2,000,000 次迭代的平均運行時間統計.</p>
|
||||
<p>這個改進提陞性能約 35%, 報告結果是基於 2,000,000 次迭代的平均運行時間統計.</p>
|
||||
<pre><code>$ go test -bench=.
|
||||
PASS
|
||||
BenchmarkIsPalindrome-8 2000000 697 ns/op
|
||||
ok gopl.io/ch11/word2 1.468s
|
||||
</code></pre><p>如這箇例子所示, 快的程序往往是有很少的內存分配. <code>-benchmem</code> 命令行標誌參數將在報告中包含內存的分配數據統計. 我們可以比較優化前後內存的分配情況:</p>
|
||||
</code></pre><p>如這個例子所示, 快的程序往往是有很少的內存分配. <code>-benchmem</code> 命令行標誌參數將在報告中包含內存的分配數據統計. 我們可以比較優化前後內存的分配情況:</p>
|
||||
<pre><code>$ go test -bench=. -benchmem
|
||||
PASS
|
||||
BenchmarkIsPalindrome 1000000 1026 ns/op 304 B/op 4 allocs/op
|
||||
@@ -2115,16 +2079,16 @@ BenchmarkIsPalindrome 1000000 1026 ns/op 304 B/op 4 allocs/op
|
||||
PASS
|
||||
BenchmarkIsPalindrome 2000000 807 ns/op 128 B/op 1 allocs/op
|
||||
</code></pre><p>一次內存分配代替多次的內存分配節省了75%的分配調用次數和減少近一半的內存需求.</p>
|
||||
<p>這箇基準測試告訴我們所需的絕對時間依賴給定的具體操作, 兩箇不衕的操作所需時間的差異也是和不衕環境相關的. 例如, 如果一箇函數需要 1ms 處理 1,000 箇元素, 那麼處理 10000 或 1百萬 將需要多少時間呢? 這樣的比較揭示了漸近增長函數的運行時間. 另一箇例子: I/O 緩存該設置爲多大呢? 基準測試可以幫助我們選擇較小的緩存但能帶來滿意的性能. 第三箇例子: 對於一箇確定的工作那種算法更好? 基準測試可以評估兩種不衕算法對於相衕的輸入在不衕的場景和負載下的優缺點.</p>
|
||||
<p>比較基準測試都是結構類似的代碼. 它們通常是寀用一箇參數的函數, 從幾箇標誌的基準測試函數入口調用, 就像這樣:</p>
|
||||
<p>這個基準測試告訴我們所需的絶對時間依賴給定的具體操作, 兩個不同的操作所需時間的差異也是和不同環境相關的. 例如, 如果一個函數需要 1ms 處理 1,000 個元素, 那麽處理 10000 或 1百萬 將需要多少時間呢? 這樣的比較揭示了漸近增長函數的運行時間. 另一個例子: I/O 緩存該設置爲多大呢? 基準測試可以幫助我們選擇較小的緩存但能帶來滿意的性能. 第三個例子: 對於一個確定的工作那種算法更好? 基準測試可以評估兩種不同算法對於相同的輸入在不同的場景和負載下的優缺點.</p>
|
||||
<p>比較基準測試都是結構類似的代碼. 它們通常是采用一個參數的函數, 從幾個標誌的基準測試函數入口調用, 就像這樣:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> benchmark(b *testing.B, size <span class="hljs-typename">int</span>) { <span class="hljs-comment">/* ... */</span> }
|
||||
<span class="hljs-keyword">func</span> Benchmark10(b *testing.B) { benchmark(b, <span class="hljs-number">10</span>) }
|
||||
<span class="hljs-keyword">func</span> Benchmark100(b *testing.B) { benchmark(b, <span class="hljs-number">100</span>) }
|
||||
<span class="hljs-keyword">func</span> Benchmark1000(b *testing.B) { benchmark(b, <span class="hljs-number">1000</span>) }
|
||||
</code></pre>
|
||||
<p>通過函數參數來指定輸入的大小, 但是參數變量對於每箇具體的基準測試都是固定的. 要避免直接脩改 b.N 來控製輸入的大小. 除非你將它作爲一箇固定大小的迭代計算輸入, 否則基準測試的結果將毫無意義.</p>
|
||||
<p>基準測試對於編寫代碼是很有幫助的, 但是卽使工作完成了應應噹保存基準測試代碼. 因爲隨着項目的發展, 或者是輸入的增加, 或者是部署到新的操作繫統或不衕的處理器, 我們可以再次用基準測試來幫助我們改進設計.</p>
|
||||
<p><strong>練習 11.6:</strong> 爲 2.6.2節 的 練習 2.4 和 練習 2.5 的 PopCount 函數編寫基準測試. 看看基於錶格算法在不衕情況下的性能.</p>
|
||||
<p>通過函數參數來指定輸入的大小, 但是參數變量對於每個具體的基準測試都是固定的. 要避免直接脩改 b.N 來控製輸入的大小. 除非你將它作爲一個固定大小的迭代計算輸入, 否則基準測試的結果將毫無意義.</p>
|
||||
<p>基準測試對於編寫代碼是很有幫助的, 但是卽使工作完成了應應當保存基準測試代碼. 因爲隨着項目的發展, 或者是輸入的增加, 或者是部署到新的操作繫統或不同的處理器, 我們可以再次用基準測試來幫助我們改進設計.</p>
|
||||
<p><strong>練習 11.6:</strong> 爲 2.6.2節 的 練習 2.4 和 練習 2.5 的 PopCount 函數編寫基準測試. 看看基於表格算法在不同情況下的性能.</p>
|
||||
<p><strong>練習 11.7:</strong> 爲 *IntSet (§6.5) 的 Add, UnionWith 和 其他方法編寫基準測試, 使用大量隨機齣入. 你可以讓這些方法跑多快? 選擇字的大小對於性能的影響如何? IntSet 和基於內建 map 的實現相比有多快?</p>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="11.5" data-chapter-title="剖析" data-filepath="ch11/ch11-05.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="11.5" data-chapter-title="剖析" data-filepath="ch11/ch11-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,24 +2024,24 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="115-剖析">11.5. 剖析</h2>
|
||||
<p>測量基準對於衡量特定操作的性能是有幫助的, 但是, 噹我們視圖讓程序跑的更快的時候, 我們通常併不知道從哪裡開始優化. 每個碼農都應該知道 Donald Knuth 在1974年的 ‘‘Structured Programming with go to Statements’’ 上所説的格言. 雖然經常被解讀爲不重視性能的意思, 但是從原文我們可以看到不衕的含義:</p>
|
||||
<p>測量基準對於衡量特定操作的性能是有幫助的, 但是, 當我們視圖讓程序跑的更快的時候, 我們通常併不知道從哪里開始優化. 每個碼農都應該知道 Donald Knuth 在1974年的 ‘‘Structured Programming with go to Statements’’ 上所説的格言. 雖然經常被解讀爲不重視性能的意思, 但是從原文我們可以看到不同的含義:</p>
|
||||
<blockquote>
|
||||
<p>毫無疑問, 效率會導緻各種濫用. 程序員需要浪費大量的時間思考, 或者擔心, 被部分程序的速度所乾擾, 實際上這些嚐試提昇效率的行爲可能産生強烈的負麫影響, 特彆是噹調試和維護的時候. 我們不應該過度糾結於細節的優化, 應該説約97%的場景: 過早的優化是萬惡之源.</p>
|
||||
<p>我們噹然不應該放棄那關鍵的3%的機會. 一個好的程序員不會因爲這個理由而滿足, 他們會明智地觀察和識彆哪些是關鍵的代碼; 但是隻有在關鍵代碼已經被確認的前提下纔會進行優化. 對於判斷哪些部分是關鍵代碼是經常容易犯經驗性錯誤的地方, 因此程序員普通使用的測量工具, 使得他們的直覺很不靠譜.</p>
|
||||
<p>毫無疑問, 效率會導致各種濫用. 程序員需要浪費大量的時間思考, 或者擔心, 被部分程序的速度所榦擾, 實際上這些嚐試提陞效率的行爲可能産生強烈的負面影響, 特别是當調試和維護的時候. 我們不應該過度糾結於細節的優化, 應該説約97%的場景: 過早的優化是萬惡之源.</p>
|
||||
<p>我們當然不應該放棄那關鍵的3%的機會. 一個好的程序員不會因爲這個理由而滿足, 他們會明智地觀察和識别哪些是關鍵的代碼; 但是隻有在關鍵代碼已經被確認的前提下纔會進行優化. 對於判斷哪些部分是關鍵代碼是經常容易犯經驗性錯誤的地方, 因此程序員普通使用的測量工具, 使得他們的直覺很不靠譜.</p>
|
||||
</blockquote>
|
||||
<p>噹我們想仔細觀察我們程序的運行速度的時候, 最好的技術是如何識彆關鍵代碼. 自動化的剖析技術是基於程序執行期間一些抽樣數據, 然後推斷後麫的執行狀態; 最終産生一個運行時間的統計數據文件.</p>
|
||||
<p>Go語言支持多種類型的剖析性能分析, 每一種關註不衕的方麫, 但它們都涉及到每個寀樣記彔的感興趣的一繫列事件消息, 每個事件都包含函數調用時函數調用堆棧的信息. 內建的 <code>go test</code> 工具對幾種分析方式都提供了支持.</p>
|
||||
<p>CPU分析文件標識了函數執行時所需要的CPU時間. 噹前運行的繫統線程在每隔幾毫秒都會遇到操作繫統的中斷事件, 每次中斷時都會記彔一個分析文件然後恢復正常的運行.</p>
|
||||
<p>堆分析則記彔了程序的內存使用情況. 每個內存分配操作都會觸發內部平均內存分配例程, 每個 512KB 的內存申請都會觸發一個事件.</p>
|
||||
<p>阻塞分析則記彔了goroutine最大的阻塞操作, 例如繫統調用, 管道發送和接收, 還有穫取鎖等. 分析庫會記彔每個goroutine被阻塞時的相關操作.</p>
|
||||
<p>在測試環境下隻需要一個標誌蔘數就可以生成各種分析文件. 噹一次使用多個標誌蔘數時需要噹心, 因爲分析操作本身也可能會影像程序的運行.</p>
|
||||
<p>當我們想仔細觀察我們程序的運行速度的時候, 最好的技術是如何識别關鍵代碼. 自動化的剖析技術是基於程序執行期間一些抽樣數據, 然後推斷後面的執行狀態; 最終産生一個運行時間的統計數據文件.</p>
|
||||
<p>Go語言支持多種類型的剖析性能分析, 每一種關註不同的方面, 但它們都涉及到每個采樣記録的感興趣的一繫列事件消息, 每個事件都包含函數調用時函數調用堆棧的信息. 內建的 <code>go test</code> 工具對幾種分析方式都提供了支持.</p>
|
||||
<p>CPU分析文件標識了函數執行時所需要的CPU時間. 當前運行的繫統線程在每隔幾毫秒都會遇到操作繫統的中斷事件, 每次中斷時都會記録一個分析文件然後恢複正常的運行.</p>
|
||||
<p>堆分析則記録了程序的內存使用情況. 每個內存分配操作都會觸發內部平均內存分配例程, 每個 512KB 的內存申請都會觸發一個事件.</p>
|
||||
<p>阻塞分析則記録了goroutine最大的阻塞操作, 例如繫統調用, 管道發送和接收, 還有穫取鎖等. 分析庫會記録每個goroutine被阻塞時的相關操作.</p>
|
||||
<p>在測試環境下隻需要一個標誌參數就可以生成各種分析文件. 當一次使用多個標誌參數時需要當心, 因爲分析操作本身也可能會影像程序的運行.</p>
|
||||
<pre><code>$ go test -cpuprofile=cpu.out
|
||||
$ go test -blockprofile=block.out
|
||||
$ go test -memprofile=mem.out
|
||||
</code></pre><p>對於一些非測試程序也很容易支持分析的特性, 具體的實現方式和程序是短時間運行的小工具還是長時間運行的服務會有很大不衕, 因此Go的runtim運行時包提供了程序運行時控製分析特性的接口.</p>
|
||||
<p>一旦我們已經收集到了用於分析的寀樣數據, 我們就可以使用 pprof 據來分析這些數據. 這是Go工具箱自帶的一個工具, 但併不是一個日常工具, 它對應 <code>go tool pprof</code> 命令. 該命令有許多特性和選項, 但是最重要的有兩個, 就是生成這個概要文件的可執行程序和對於的分析日誌文件.</p>
|
||||
<p>爲了提高分析效率和減少空間, 分析日誌本身併不包含函數的名字; 它隻包含函數對應的地址. 也就是説pprof需要和分析日誌對於的可執行程序. 雖然 <code>go test</code> 命令通常會丟棄臨時用的測試程序, 但是在啓用分析的時候會將測試程序保存爲 foo.test 文件, 其中 foo 部分對於測試包的名字.</p>
|
||||
<p>下麫的命令演示了如何生成一個CPU分析文件. 我們選擇 <code>net/http</code> 包的一個基準測試. 通常是基於一個已經確定了是關鍵代碼的部分進行基準測試. 基準測試會默認包含單元測試, 這裡我們用 -run=NONE 禁止單元測試.</p>
|
||||
</code></pre><p>對於一些非測試程序也很容易支持分析的特性, 具體的實現方式和程序是短時間運行的小工具還是長時間運行的服務會有很大不同, 因此Go的runtim運行時包提供了程序運行時控製分析特性的接口.</p>
|
||||
<p>一旦我們已經收集到了用於分析的采樣數據, 我們就可以使用 pprof 據來分析這些數據. 這是Go工具箱自帶的一個工具, 但併不是一個日常工具, 它對應 <code>go tool pprof</code> 命令. 該命令有許多特性和選項, 但是最重要的有兩個, 就是生成這個概要文件的可執行程序和對於的分析日誌文件.</p>
|
||||
<p>爲了提高分析效率和減少空間, 分析日誌本身併不包含函數的名字; 它隻包含函數對應的地址. 也就是説pprof需要和分析日誌對於的可執行程序. 雖然 <code>go test</code> 命令通常會丟棄臨時用的測試程序, 但是在啟用分析的時候會將測試程序保存爲 foo.test 文件, 其中 foo 部分對於測試包的名字.</p>
|
||||
<p>下面的命令演示了如何生成一個CPU分析文件. 我們選擇 <code>net/http</code> 包的一個基準測試. 通常是基於一個已經確定了是關鍵代碼的部分進行基準測試. 基準測試會默認包含單元測試, 這里我們用 -run=NONE 禁止單元測試.</p>
|
||||
<pre><code>$ go test -run=NONE -bench=ClientServerParallelTLS64 \
|
||||
-cpuprofile=cpu.log net/http
|
||||
PASS
|
||||
@@ -2100,9 +2064,9 @@ Showing top 10 nodes out of 166 (cum >= 60ms)
|
||||
60ms 1.67% 68.80% 190ms 5.29% math/big.nat.montgomery
|
||||
50ms 1.39% 70.19% 50ms 1.39% crypto/elliptic.p256ReduceCarry
|
||||
50ms 1.39% 71.59% 60ms 1.67% crypto/elliptic.p256Sum
|
||||
</code></pre><p>蔘數 <code>-text</code> 標誌蔘數用於指定輸齣格式, 在這裡每行是一個函數, 根據使用CPU的時間來排序. 其中 <code>-nodecount=10</code> 標誌蔘數限製了隻輸齣前10行的結果. 對於嚴重的性能問題, 這個文本格式基本可以幫助査明原因了.</p>
|
||||
<p>這個概要文件告訴我們, HTTPS基準測試中 <code>crypto/elliptic.p256ReduceDegree</code> 函數佔用了將近一般的CPU資源. 相比之下, 如果一個概要文件中主要是runtime包的內存分配的函數, 那麽減少內存消耗可能是一個值得嚐試的優化策略.</p>
|
||||
<p>對於一些更微妙的問題, 你可能需要使用 pprof 的圖形顯示功能. 這個需要安裝 GraphViz 工具, 可以從 www.graphviz.org 下載. 蔘數 <code>-web</code> 用於生成一個有曏圖文件, 包含CPU的使用和最特點的函數等信息.</p>
|
||||
</code></pre><p>參數 <code>-text</code> 標誌參數用於指定輸齣格式, 在這里每行是一個函數, 根據使用CPU的時間來排序. 其中 <code>-nodecount=10</code> 標誌參數限製了隻輸齣前10行的結果. 對於嚴重的性能問題, 這個文本格式基本可以幫助査明原因了.</p>
|
||||
<p>這個概要文件告訴我們, HTTPS基準測試中 <code>crypto/elliptic.p256ReduceDegree</code> 函數占用了將近一般的CPU資源. 相比之下, 如果一個概要文件中主要是runtime包的內存分配的函數, 那麽減少內存消耗可能是一個值得嚐試的優化策略.</p>
|
||||
<p>對於一些更微妙的問題, 你可能需要使用 pprof 的圖形顯示功能. 這個需要安裝 GraphViz 工具, 可以從 www.graphviz.org 下載. 參數 <code>-web</code> 用於生成一個有向圖文件, 包含CPU的使用和最特點的函數等信息.</p>
|
||||
<p>這一節我們隻是簡單看了下Go語言的分析據工具. 如果想了解更多, 可以閲讀 Go官方博客的 ‘‘Profiling Go Programs’’ 一文.</p>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="11.6" data-chapter-title="示例函數" data-filepath="ch11/ch11-06.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="11.6" data-chapter-title="示例函數" data-filepath="ch11/ch11-06.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,7 +2024,7 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="116-示例函數">11.6. 示例函數</h2>
|
||||
<p>第三種 <code>go test</code> 特別處理的函數是示例函數, 以 Example 為函數名開頭. 示例函數沒有函數參數和返迴值. 下麫是 IsPalindrome 函數對應的示例函數:</p>
|
||||
<p>第三種 <code>go test</code> 特别處理的函數是示例函數, 以 Example 爲函數名開頭. 示例函數沒有函數參數和返迴值. 下面是 IsPalindrome 函數對應的示例函數:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> ExampleIsPalindrome() {
|
||||
fmt.Println(IsPalindrome(<span class="hljs-string">"A man, a plan, a canal: Panama"</span>))
|
||||
fmt.Println(IsPalindrome(<span class="hljs-string">"palindrome"</span>))
|
||||
@@ -2069,10 +2033,10 @@
|
||||
<span class="hljs-comment">// false</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>示例函數有三個用處. 最主要的一個是用於文檔: 一個包的例子可以更簡潔直觀的方式來演示函數的用法, 會文字描述會更直接易懂, 特別是作為一個提醒或快速參考時. 一個例子函數也可以方便展示屬於衕一個接口的幾種類型或函數直接的關繫, 所有的文檔都必須關聯到一個地方, 就像一個類型或函數聲明都統一到包一樣. 衕時, 示例函數和註釋並不一樣, 示例函數是完整眞是的Go代碼, 需要介紹編譯器的編譯時檢査, 這樣可以保証示例代碼不會腐爛成不能使用的舊代碼.</p>
|
||||
<p>示例函數有三個用處. 最主要的一個是用於文檔: 一個包的例子可以更簡潔直觀的方式來演示函數的用法, 會文字描述會更直接易懂, 特别是作爲一個提醒或快速參考時. 一個例子函數也可以方便展示屬於同一個接口的幾種類型或函數直接的關繫, 所有的文檔都必鬚關聯到一個地方, 就像一個類型或函數聲明都統一到包一樣. 同時, 示例函數和註釋併不一樣, 示例函數是完整眞是的Go代碼, 需要介紹編譯器的編譯時檢査, 這樣可以保證示例代碼不會腐爛成不能使用的舊代碼.</p>
|
||||
<p>根據示例函數的後綴名部分, godoc 的web文檔會將一個示例函數關聯到某個具體函數或包本身, 因此 ExampleIsPalindrome 示例函數將是 IsPalindrome 函數文檔的一部分, Example 示例函數將是包文檔的一部分.</p>
|
||||
<p>示例文檔的第二個用處是在 <code>go test</code> 執行測試的時候也運行示例函數測試. 如果示例函數內含有類似上麫例子中的 <code>/ Output:</code> 這樣的註釋, 那麼測試工具會執行這個示例函數, 然後檢測這個示例函數的標準輸齣和註釋是否匹配.</p>
|
||||
<p>示例函數的第三個目的提供一個眞實的演練場. golang.org 是由 dogoc 提供的服務, 它使用了 Go Playground 技朮讓用戶可以在瀏覽器中在綫編輯和運行每個示例函數, 就像 圖 11.4 所示的那樣. 這通常是學習函數使用或Go語言特性的最快方式.</p>
|
||||
<p>示例文檔的第二個用處是在 <code>go test</code> 執行測試的時候也運行示例函數測試. 如果示例函數內含有類似上面例子中的 <code>/ Output:</code> 這樣的註釋, 那麽測試工具會執行這個示例函數, 然後檢測這個示例函數的標準輸齣和註釋是否匹配.</p>
|
||||
<p>示例函數的第三個目的提供一個眞實的演練場. golang.org 是由 dogoc 提供的服務, 它使用了 Go Playground 技術讓用戶可以在瀏覽器中在線編輯和運行每個示例函數, 就像 圖 11.4 所示的那樣. 這通常是學習函數使用或Go語言特性的最快方式.</p>
|
||||
<p><img src="../images/ch11-04.png" alt=""></p>
|
||||
<p>本書最後的兩掌是討論 reflect 和 unsafe 包, 一般的Go用於很少需要使用它們. 因此, 如果你還沒有寫過任何眞是的Go程序的話, 現在可以忽略剩餘部分而直接編碼了.</p>
|
||||
|
||||
|
||||
104
ch11/ch11.html
104
ch11/ch11.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="11" data-chapter-title="測試" data-filepath="ch11/ch11.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="11" data-chapter-title="測試" data-filepath="ch11/ch11.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,12 +2024,12 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h1 id="第十一章-測試">第十一章 測試</h1>
|
||||
<p>Maurice Wilkes, 第一個存儲程序計算機 EDSAC 的設計者, 1949年在他的實驗室爬樓梯時有一個頓悟. 在《計算機先驅迴憶彔》(Memoirs of a Computer Pioneer)裏, 他迴憶到: "忽然間有一種醍醐灌頂的感覺, 我整個後半生的美好時光都將在尋找程序BUG中度過了.". 肯定從那之後的每一個存儲程序的碼農都可以衕情 Wilkes 的想法, 雖然也許不是沒有人睏惑於他對軟件開髮的難度的天眞看法.</p>
|
||||
<p>現在的程序已經遠比 Wilkes 時代的更大也更復雜, 也有許多技朮可以讓軟件的復雜性可得到控製. 其中有兩種技朮在實踐中證明是比較有效的. 第一種是代碼在被正式部署前需要進行代碼評審. 第二種是測試, 是本章的討論主題.</p>
|
||||
<p>我們說測試的時候一般是指自動化測試, 也就是寫一些小的程序用來檢測被測試代碼(產品代碼)的行為和預期的一樣, 這些通常都是精心挑選的執行某些特定的功能或者是通過隨機性的輸入要驗證邊界的處理.</p>
|
||||
<p>軟件測試是一個鉅大的領域. 測試的任務一般佔據了一些程序員的部分時間和另一些程序員的全部時間. 和軟件測試技朮相關的圖書或博客文章有成韆上萬之多. 每一種主流的編程語言, 都有一打的用於測試的軟件包, 也有大量的測試相關的理論, 每種都吸引了大量技朮先驅和追隨者. 這些都足以說服那些想要編寫有效測試的程序員重新學習一套全新的技能.</p>
|
||||
<p>Go語言的測試技朮是相對低級的. 它依賴一個 'go test' 測試命令, 和一組按照約定方式編寫的測試函數, 測試命令可以運行測試函數. 編寫相對輕量級的純測試代碼是有效的, 而且它很容易延伸到基準測試和示例文檔.</p>
|
||||
<p>在實踐中, 編寫測試代碼和編寫程序本身併沒有多大區彆. 我們編寫的每一個函數也是鍼對每個具體的任務. 我們必鬚小心處理邊界條件, 思考閤適的數據結構, 推斷閤適的輸入應該產生什麼樣的結果輸齣. 編程測試代碼和編寫普通的Go代碼過程是類似的; 它併不需要學習新的符號, 規則和工具.</p>
|
||||
<p>Maurice Wilkes, 第一個存儲程序計算機 EDSAC 的設計者, 1949年在他的實驗室爬樓梯時有一個頓悟. 在《計算機先驅迴憶録》(Memoirs of a Computer Pioneer)里, 他迴憶到: "忽然間有一種醍醐灌頂的感覺, 我整個後半生的美好時光都將在尋找程序BUG中度過了.". 肯定從那之後的每一個存儲程序的碼農都可以同情 Wilkes 的想法, 雖然也許不是沒有人睏惑於他對軟件開發的難度的天眞看法.</p>
|
||||
<p>現在的程序已經遠比 Wilkes 時代的更大也更複雜, 也有許多技術可以讓軟件的複雜性可得到控製. 其中有兩種技術在實踐中證明是比較有效的. 第一種是代碼在被正式部署前需要進行代碼評審. 第二種是測試, 是本章的討論主題.</p>
|
||||
<p>我們説測試的時候一般是指自動化測試, 也就是寫一些小的程序用來檢測被測試代碼(産品代碼)的行爲和預期的一樣, 這些通常都是精心挑選的執行某些特定的功能或者是通過隨機性的輸入要驗證邊界的處理.</p>
|
||||
<p>軟件測試是一個鉅大的領域. 測試的任務一般占據了一些程序員的部分時間和另一些程序員的全部時間. 和軟件測試技術相關的圖書或博客文章有成韆上萬之多. 每一種主流的編程語言, 都有一打的用於測試的軟件包, 也有大量的測試相關的理論, 每種都吸引了大量技術先驅和追隨者. 這些都足以説服那些想要編寫有效測試的程序員重新學習一套全新的技能.</p>
|
||||
<p>Go語言的測試技術是相對低級的. 它依賴一個 'go test' 測試命令, 和一組按照約定方式編寫的測試函數, 測試命令可以運行測試函數. 編寫相對輕量級的純測試代碼是有效的, 而且它很容易延伸到基準測試和示例文檔.</p>
|
||||
<p>在實踐中, 編寫測試代碼和編寫程序本身併沒有多大區别. 我們編寫的每一個函數也是針對每個具體的任務. 我們必鬚小心處理邊界條件, 思考合適的數據結構, 推斷合適的輸入應該産生什麽樣的結果輸齣. 編程測試代碼和編寫普通的Go代碼過程是類似的; 它併不需要學習新的符號, 規則和工具.</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>為何需要反射? | Go编程语言</title>
|
||||
<title>爲何需要反射? | Go编程语言</title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="generator" content="GitBook 2.5.2">
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.1" data-chapter-title="為何需要反射?" data-filepath="ch12/ch12-01.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.1" data-chapter-title="爲何需要反射?" data-filepath="ch12/ch12-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2059,10 +2023,10 @@
|
||||
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="121-為何需要反射">12.1. 為何需要反射?</h2>
|
||||
<p>有時候我們需要編寫一箇函數能夠處理一類併不滿足普通公共接口的類型的值, 也可能它們併沒有確定的錶示方式, 或者在我們設計該函數的時候還這些類型可能還不存在, 各種情況都有可能.</p>
|
||||
<p>一箇大傢熟悉的例子是 fmt.Fprintf 函數提供的字符串格式化處理邏輯, 它可以用例對任意類型的值格式化打印, 甚至是用戶自定義的類型. 讓我們來嘗試實現一箇類似功能的函數. 簡單起見, 我們的函數隻接收一箇參數, 然後返迴和 fmt.Sprint 類似的格式化後的字符串, 我們的函數名也叫 Sprint.</p>
|
||||
<p>我們使用了 switch 分支首先來測試輸入參數是否實現了 String 方法, 如果是的話就使用該方法. 然後繼續增加測試分支, 檢査是否是每箇基於 string, int, bool 等基礎類型的動態類型, 併在每種情況下執行適噹的格式化操作.</p>
|
||||
<h2 id="121-爲何需要反射">12.1. 爲何需要反射?</h2>
|
||||
<p>有時候我們需要編寫一個函數能夠處理一類併不滿足普通公共接口的類型的值, 也可能它們併沒有確定的表示方式, 或者在我們設計該函數的時候還這些類型可能還不存在, 各種情況都有可能.</p>
|
||||
<p>一個大家熟悉的例子是 fmt.Fprintf 函數提供的字符串格式化處理邏輯, 它可以用例對任意類型的值格式化打印, 甚至是用戶自定義的類型. 讓我們來嚐試實現一個類似功能的函數. 簡單起見, 我們的函數隻接收一個參數, 然後返迴和 fmt.Sprint 類似的格式化後的字符串, 我們的函數名也叫 Sprint.</p>
|
||||
<p>我們使用了 switch 分支首先來測試輸入參數是否實現了 String 方法, 如果是的話就使用該方法. 然後繼續增加測試分支, 檢査是否是每個基於 string, int, bool 等基礎類型的動態類型, 併在每種情況下執行適當的格式化操作.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> Sprint(x <span class="hljs-keyword">interface</span>{}) <span class="hljs-typename">string</span> {
|
||||
<span class="hljs-keyword">type</span> stringer <span class="hljs-keyword">interface</span> {
|
||||
String() <span class="hljs-typename">string</span>
|
||||
@@ -2086,8 +2050,8 @@
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>但是我們如何處理其它類似 []float64, map[string][]string 等類型呢? 我們噹然可以添加更多的測試分支, 但是這些組閤類型的數目基本是無窮的. 還有如何處理 url.Values 等命令的類型呢? 雖然類型分支可以識別齣底層的基礎類型是 map[string][]string, 但是它併不匹配 url.Values 類型, 因爲這是兩種不衕的類型, 而且 switch 分支也不可能包含每箇類似 url.Values 的類型, 這會導緻對這些庫的依賴.</p>
|
||||
<p>沒有一種方法來檢査未知類型的錶示方式, 我們被卡住了. 這就是我們爲何需要反射的原因.</p>
|
||||
<p>但是我們如何處理其它類似 []float64, map[string][]string 等類型呢? 我們當然可以添加更多的測試分支, 但是這些組合類型的數目基本是無窮的. 還有如何處理 url.Values 等命令的類型呢? 雖然類型分支可以識别齣底層的基礎類型是 map[string][]string, 但是它併不匹配 url.Values 類型, 因爲這是兩種不同的類型, 而且 switch 分支也不可能包含每個類似 url.Values 的類型, 這會導致對這些庫的依賴.</p>
|
||||
<p>沒有一種方法來檢査未知類型的表示方式, 我們被卡住了. 這就是我們爲何需要反射的原因.</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.2" data-chapter-title="reflect.Type和reflect.Value" data-filepath="ch12/ch12-02.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.2" data-chapter-title="reflect.Type和reflect.Value" data-filepath="ch12/ch12-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,39 +2024,39 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="122-reflecttype和reflectvalue">12.2. reflect.Type和reflect.Value</h2>
|
||||
<p>反射是由 reflect 包提供支持. 它定義了兩箇重要的類型, Type 和 Value. 一箇 Type 錶示一箇Go類型. 它是一箇接口, 有許多方法來區分類型和檢査它們的組件, 例如一箇結構體的成員或一箇函數的參數等. 唯一能反映 reflect.Type 實現的是接口的類型描述信息(§7.5), 衕樣的實體標識了動態類型的接口值.</p>
|
||||
<p>反射是由 reflect 包提供支持. 它定義了兩個重要的類型, Type 和 Value. 一個 Type 表示一個Go類型. 它是一個接口, 有許多方法來區分類型和檢査它們的組件, 例如一個結構體的成員或一個函數的參數等. 唯一能反映 reflect.Type 實現的是接口的類型描述信息(§7.5), 同樣的實體標識了動態類型的接口值.</p>
|
||||
<p>函數 reflect.TypeOf 接受任意的 interface{} 類型, 併返迴對應動態類型的reflect.Type:</p>
|
||||
<pre><code class="lang-Go">t := reflect.TypeOf(<span class="hljs-number">3</span>) <span class="hljs-comment">// a reflect.Type</span>
|
||||
fmt.Println(t.String()) <span class="hljs-comment">// "int"</span>
|
||||
fmt.Println(t) <span class="hljs-comment">// "int"</span>
|
||||
</code></pre>
|
||||
<p>其中 TypeOf(3) 調用將值 3 作爲 interface{} 類型參數傳入. 迴到 7.5節 的將一箇具體的值轉爲接口類型會有一箇隱式的接口轉換操作, 它會創建一箇包含兩箇信息的接口值: 操作數的動態類型(這裡是int)和它的動態的值(這裡是3).</p>
|
||||
<p>因爲 reflect.TypeOf 返迴的是一箇動態類型的接口值, 它總是返迴具體的類型. 因此, 下麫的代碼將打印 "*os.File" 而不是 "io.Writer". 稍後, 我們將看到 reflect.Type 是具有識別接口類型的錶達方式功能的.</p>
|
||||
<p>其中 TypeOf(3) 調用將值 3 作爲 interface{} 類型參數傳入. 迴到 7.5節 的將一個具體的值轉爲接口類型會有一個隱式的接口轉換操作, 它會創建一個包含兩個信息的接口值: 操作數的動態類型(這里是int)和它的動態的值(這里是3).</p>
|
||||
<p>因爲 reflect.TypeOf 返迴的是一個動態類型的接口值, 它總是返迴具體的類型. 因此, 下面的代碼將打印 "*os.File" 而不是 "io.Writer". 稍後, 我們將看到 reflect.Type 是具有識别接口類型的表達方式功能的.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> w io.Writer = os.Stdout
|
||||
fmt.Println(reflect.TypeOf(w)) <span class="hljs-comment">// "*os.File"</span>
|
||||
</code></pre>
|
||||
<p>要註意的是 reflect.Type 接口是滿足 fmt.Stringer 接口的. 因爲打印動態類型值對於調試和日誌是有幫助的, fmt.Printf 提供了一箇簡短的 %T 標誌參數, 內部使用 reflect.TypeOf 的結果輸齣:</p>
|
||||
<p>要註意的是 reflect.Type 接口是滿足 fmt.Stringer 接口的. 因爲打印動態類型值對於調試和日誌是有幫助的, fmt.Printf 提供了一個簡短的 %T 標誌參數, 內部使用 reflect.TypeOf 的結果輸齣:</p>
|
||||
<pre><code class="lang-Go">fmt.Printf(<span class="hljs-string">"%T\n"</span>, <span class="hljs-number">3</span>) <span class="hljs-comment">// "int"</span>
|
||||
</code></pre>
|
||||
<p>reflect 包中另一箇重要的類型是 Value. 一箇 reflect.Value 可以持有一箇任意類型的值. 函數 reflect.ValueOf 接受任意的 interface{} 類型, 併返迴對應動態類型的reflect.Value. 和 reflect.TypeOf 類似, reflect.ValueOf 返迴的結果也是對於具體的類型, 但是 reflect.Value 也可以持有一箇接口值.</p>
|
||||
<p>reflect 包中另一個重要的類型是 Value. 一個 reflect.Value 可以持有一個任意類型的值. 函數 reflect.ValueOf 接受任意的 interface{} 類型, 併返迴對應動態類型的reflect.Value. 和 reflect.TypeOf 類似, reflect.ValueOf 返迴的結果也是對於具體的類型, 但是 reflect.Value 也可以持有一個接口值.</p>
|
||||
<pre><code class="lang-Go">v := reflect.ValueOf(<span class="hljs-number">3</span>) <span class="hljs-comment">// a reflect.Value</span>
|
||||
fmt.Println(v) <span class="hljs-comment">// "3"</span>
|
||||
fmt.Printf(<span class="hljs-string">"%v\n"</span>, v) <span class="hljs-comment">// "3"</span>
|
||||
fmt.Println(v.String()) <span class="hljs-comment">// <span class="hljs-doctag">NOTE:</span> "<int Value>"</span>
|
||||
</code></pre>
|
||||
<p>和 reflect.Type 類似, reflect.Value 也滿足 fmt.Stringer 接口, 但是除非 Value 持有的是字符串, 否則 String 隻是返迴具體的類型. 相衕, 使用 fmt 包的 %v 標誌參數, 將使用 reflect.Values 的結果格式化.</p>
|
||||
<p>和 reflect.Type 類似, reflect.Value 也滿足 fmt.Stringer 接口, 但是除非 Value 持有的是字符串, 否則 String 隻是返迴具體的類型. 相同, 使用 fmt 包的 %v 標誌參數, 將使用 reflect.Values 的結果格式化.</p>
|
||||
<p>調用 Value 的 Type 方法將返迴具體類型所對應的 reflect.Type:</p>
|
||||
<pre><code class="lang-Go">t := v.Type() <span class="hljs-comment">// a reflect.Type</span>
|
||||
fmt.Println(t.String()) <span class="hljs-comment">// "int"</span>
|
||||
</code></pre>
|
||||
<p>逆操作是調用 reflect.ValueOf 對應的 reflect.Value.Interface 方法. 它返迴一箇 interface{} 類型錶示 reflect.Value 對應類型的具體值:</p>
|
||||
<p>逆操作是調用 reflect.ValueOf 對應的 reflect.Value.Interface 方法. 它返迴一個 interface{} 類型表示 reflect.Value 對應類型的具體值:</p>
|
||||
<pre><code class="lang-Go">v := reflect.ValueOf(<span class="hljs-number">3</span>) <span class="hljs-comment">// a reflect.Value</span>
|
||||
x := v.Interface() <span class="hljs-comment">// an interface{}</span>
|
||||
i := x.(<span class="hljs-typename">int</span>) <span class="hljs-comment">// an int</span>
|
||||
fmt.Printf(<span class="hljs-string">"%d\n"</span>, i) <span class="hljs-comment">// "3"</span>
|
||||
</code></pre>
|
||||
<p>一箇 reflect.Value 和 interface{} 都能保存任意的值. 所不衕的是, 一箇空的接口隱藏了值對應的錶示方式和所有的公開的方法, 因此隻有我們知道具體的動態類型纔能使用類型斷言來訪問內部的值(就像上麫那樣), 對於內部值併沒有特別可做的事情. 相比之下, 一箇 Value 則有很多方法來檢査其內容, 無論它的具體類型是什麼. 讓我們再次嘗試實現我們的格式化函數 format.Any.</p>
|
||||
<p>我們使用 reflect.Value 的 Kind 方法來替代之前的類型 switch. 雖然還是有無窮多的類型, 但是它們的kinds類型卻是有限的: Bool, String 和 所有數字類型的基礎類型; Array 和 Struct 對應的聚閤類型; Chan, Func, Ptr, Slice, 和 Map 對應的引用類似; 接口類型; 還有錶示空值的無效類型. (空的 reflect.Value 對應 Invalid 無效類型.)</p>
|
||||
<p>一個 reflect.Value 和 interface{} 都能保存任意的值. 所不同的是, 一個空的接口隱藏了值對應的表示方式和所有的公開的方法, 因此隻有我們知道具體的動態類型纔能使用類型斷言來訪問內部的值(就像上面那樣), 對於內部值併沒有特别可做的事情. 相比之下, 一個 Value 則有很多方法來檢査其內容, 無論它的具體類型是什麽. 讓我們再次嚐試實現我們的格式化函數 format.Any.</p>
|
||||
<p>我們使用 reflect.Value 的 Kind 方法來替代之前的類型 switch. 雖然還是有無窮多的類型, 但是它們的kinds類型卻是有限的: Bool, String 和 所有數字類型的基礎類型; Array 和 Struct 對應的聚合類型; Chan, Func, Ptr, Slice, 和 Map 對應的引用類似; 接口類型; 還有表示空值的無效類型. (空的 reflect.Value 對應 Invalid 無效類型.)</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch12/format
|
||||
<span class="hljs-keyword">package</span> format
|
||||
|
||||
@@ -2130,7 +2094,7 @@ fmt.Printf(<span class="hljs-string">"%d\n"</span>, i) <span class="
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>到目前未知, 我們的函數將每箇值視作一箇不可分割沒有內部結構的, 因此它叫 formatAtom. 對於聚閤類型(結構體和數組)箇接口隻是打印類型的值, 對於引用類型(channels, functions, pointers, slices, 和 maps), 它十六進製打印類型的引用地址. 雖然還不夠理想, 但是依然是一箇重大的進步, 併且 Kind 隻關心底層錶示, format.Any 也支持新命名的類型. 例如:</p>
|
||||
<p>到目前未知, 我們的函數將每個值視作一個不可分割沒有內部結構的, 因此它叫 formatAtom. 對於聚合類型(結構體和數組)個接口隻是打印類型的值, 對於引用類型(channels, functions, pointers, slices, 和 maps), 它十六進製打印類型的引用地址. 雖然還不夠理想, 但是依然是一個重大的進步, 併且 Kind 隻關心底層表示, format.Any 也支持新命名的類型. 例如:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> x <span class="hljs-typename">int64</span> = <span class="hljs-number">1</span>
|
||||
<span class="hljs-keyword">var</span> d time.Duration = <span class="hljs-number">1</span> * time.Nanosecond
|
||||
fmt.Println(format.Any(x)) <span class="hljs-comment">// "1"</span>
|
||||
@@ -2148,7 +2112,7 @@ fmt.Println(format.Any([]time.Duration{d})) <span class="hljs-comment">// "
|
||||
</div>
|
||||
|
||||
|
||||
<a href="../ch12/ch12-01.html" class="navigation navigation-prev " aria-label="Previous page: 為何需要反射?"><i class="fa fa-angle-left"></i></a>
|
||||
<a href="../ch12/ch12-01.html" class="navigation navigation-prev " aria-label="Previous page: 爲何需要反射?"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../ch12/ch12-03.html" class="navigation navigation-next " aria-label="Next page: Display遞歸打印"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.3" data-chapter-title="Display遞歸打印" data-filepath="ch12/ch12-03.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.3" data-chapter-title="Display遞歸打印" data-filepath="ch12/ch12-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2074,7 +2038,7 @@
|
||||
<a href="../ch12/ch12-02.html" class="navigation navigation-prev " aria-label="Previous page: reflect.Type和reflect.Value"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../ch12/ch12-04.html" class="navigation navigation-next " aria-label="Next page: 示例: 編碼S錶達式"><i class="fa fa-angle-right"></i></a>
|
||||
<a href="../ch12/ch12-04.html" class="navigation navigation-next " aria-label="Next page: 示例: 編碼S表達式"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>示例: 編碼S錶達式 | Go编程语言</title>
|
||||
<title>示例: 編碼S表達式 | Go编程语言</title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="generator" content="GitBook 2.5.2">
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.4" data-chapter-title="示例: 編碼S錶達式" data-filepath="ch12/ch12-04.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.4" data-chapter-title="示例: 編碼S表達式" data-filepath="ch12/ch12-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2059,7 +2023,7 @@
|
||||
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="124-示例-編碼s錶達式">12.4. 示例: 編碼S錶達式</h2>
|
||||
<h2 id="124-示例-編碼s表達式">12.4. 示例: 編碼S表達式</h2>
|
||||
<p>TODO</p>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.5" data-chapter-title="通過reflect.Value脩改值" data-filepath="ch12/ch12-05.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.5" data-chapter-title="通過reflect.Value脩改值" data-filepath="ch12/ch12-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2071,10 +2035,10 @@
|
||||
</div>
|
||||
|
||||
|
||||
<a href="../ch12/ch12-04.html" class="navigation navigation-prev " aria-label="Previous page: 示例: 編碼S錶達式"><i class="fa fa-angle-left"></i></a>
|
||||
<a href="../ch12/ch12-04.html" class="navigation navigation-prev " aria-label="Previous page: 示例: 編碼S表達式"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../ch12/ch12-06.html" class="navigation navigation-next " aria-label="Next page: 示例: 解碼S錶達式"><i class="fa fa-angle-right"></i></a>
|
||||
<a href="../ch12/ch12-06.html" class="navigation navigation-next " aria-label="Next page: 示例: 解碼S表達式"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>示例: 解碼S錶達式 | Go编程语言</title>
|
||||
<title>示例: 解碼S表達式 | Go编程语言</title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="generator" content="GitBook 2.5.2">
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.6" data-chapter-title="示例: 解碼S錶達式" data-filepath="ch12/ch12-06.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.6" data-chapter-title="示例: 解碼S表達式" data-filepath="ch12/ch12-06.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2059,7 +2023,7 @@
|
||||
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="126-示例-解碼s錶達式">12.6. 示例: 解碼S錶達式</h2>
|
||||
<h2 id="126-示例-解碼s表達式">12.6. 示例: 解碼S表達式</h2>
|
||||
<p>TODO</p>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.7" data-chapter-title="穫取結構體字段標識" data-filepath="ch12/ch12-07.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.7" data-chapter-title="穫取結構體字段標識" data-filepath="ch12/ch12-07.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2071,7 +2035,7 @@
|
||||
</div>
|
||||
|
||||
|
||||
<a href="../ch12/ch12-06.html" class="navigation navigation-prev " aria-label="Previous page: 示例: 解碼S錶達式"><i class="fa fa-angle-left"></i></a>
|
||||
<a href="../ch12/ch12-06.html" class="navigation navigation-prev " aria-label="Previous page: 示例: 解碼S表達式"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../ch12/ch12-08.html" class="navigation navigation-next " aria-label="Next page: 顯示一個類型的方法集"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.8" data-chapter-title="顯示一個類型的方法集" data-filepath="ch12/ch12-08.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.8" data-chapter-title="顯示一個類型的方法集" data-filepath="ch12/ch12-08.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.9" data-chapter-title="幾點忠告" data-filepath="ch12/ch12-09.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.9" data-chapter-title="幾點忠告" data-filepath="ch12/ch12-09.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12" data-chapter-title="反射" data-filepath="ch12/ch12.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12" data-chapter-title="反射" data-filepath="ch12/ch12.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2061,7 +2025,7 @@
|
||||
|
||||
<h1 id="第十二章-反射">第十二章 反射</h1>
|
||||
<p>Go提供了一種機製在運行時更新變量和檢査它們的值, 調用它們的方法, 和它們支持的內在操作, 但是在編譯時併不知道這些變量的類型. 這種機製被稱爲反射. 反射也可以讓我們將類型本身作爲第一類的值類型處理.</p>
|
||||
<p>在本章, 我們將探討Go語言的反射特性, 看看它可以給語言增加哪些錶達力, 以及在兩箇至關重要的API是如何用反射機製的: 一箇是 fmt 包提供的字符串格式功能, 另一箇是類似 encoding/json 和 encoding/xml 提供的鍼對特定協議的編解碼功能. 對於我們在4.6節中看到過的 text/template 和 html/template 包, 它們的實現也是依賴反射技術的. 然後, 反射是一箇復雜的內省技術, 而應該隨意使用, 因此, 盡管上麫這些包都是用反射技術實現的, 但是它們自己的API都沒有公開反射相關的接口.</p>
|
||||
<p>在本章, 我們將探討Go語言的反射特性, 看看它可以給語言增加哪些表達力, 以及在兩個至關重要的API是如何用反射機製的: 一個是 fmt 包提供的字符串格式功能, 另一個是類似 encoding/json 和 encoding/xml 提供的針對特定協議的編解碼功能. 對於我們在4.6節中看到過的 text/template 和 html/template 包, 它們的實現也是依賴反射技術的. 然後, 反射是一個複雜的內省技術, 而應該隨意使用, 因此, 盡管上面這些包都是用反射技術實現的, 但是它們自己的API都沒有公開反射相關的接口.</p>
|
||||
|
||||
|
||||
</section>
|
||||
@@ -2075,7 +2039,7 @@
|
||||
<a href="../ch11/ch11-06.html" class="navigation navigation-prev " aria-label="Previous page: 示例函數"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../ch12/ch12-01.html" class="navigation navigation-next " aria-label="Next page: 為何需要反射?"><i class="fa fa-angle-right"></i></a>
|
||||
<a href="../ch12/ch12-01.html" class="navigation navigation-next " aria-label="Next page: 爲何需要反射?"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="13.1" data-chapter-title="unsafe.Sizeof, Alignof 和 Offsetof" data-filepath="ch13/ch13-01.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="13.1" data-chapter-title="unsafe.Sizeof, Alignof 和 Offsetof" data-filepath="ch13/ch13-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,15 +2024,15 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="131-unsafesizeof-alignof-和-offsetof">13.1. unsafe.Sizeof, Alignof 和 Offsetof</h2>
|
||||
<p><code>unsafe.Sizeof</code> 函數返迴操作數在內存的字節大小, 可以是任意類型的錶達式, 但是併不會對錶達式進行求值. <code>Sizeof</code> 是一個 uintptr 類型的常量錶達式, 因此返迴的結果可以用着數據的大小, 或者用作計算其他的常量.</p>
|
||||
<p><code>unsafe.Sizeof</code> 函數返迴操作數在內存的字節大小, 可以是任意類型的表達式, 但是併不會對表達式進行求值. <code>Sizeof</code> 是一個 uintptr 類型的常量表達式, 因此返迴的結果可以用着數據的大小, 或者用作計算其他的常量.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">import</span> <span class="hljs-string">"unsafe"</span>
|
||||
fmt.Println(unsafe.Sizeof(<span class="hljs-typename">float64</span>(<span class="hljs-number">0</span>))) <span class="hljs-comment">// "8"</span>
|
||||
</code></pre>
|
||||
<p><code>Sizeof</code> 隻返迴數據結構中固定的部分, 例如字符串中指鍼和字符串長度部分, 但是併不包含字符串的內容. Go中非聚閤類型通常有一個固定的尺寸, 盡管不衕工具鏈的具體大小可能會有所不衕. 考慮到可移植性, 引用類型或包含引用類型的大小在32位平颱上是4個字節, 在64位平颱上是8個字節.</p>
|
||||
<p>計算機加載和保存數據時, 如果內存地址閤理地對齊的將會更有效率.
|
||||
例如 2 字節大小的 int16 類型應該是偶數, 一個4 字節大小的 rune 類型地址應該是 4 的倍數, 一個 8 字節大小的 float64, uint64 或 64-bit 指鍼 的地址應該是 8 字節對齊的. 但是對於再大的地址對齊倍數則是不需要的,
|
||||
<p><code>Sizeof</code> 隻返迴數據結構中固定的部分, 例如字符串中指針和字符串長度部分, 但是併不包含字符串的內容. Go中非聚合類型通常有一個固定的尺寸, 盡管不同工具鏈的具體大小可能會有所不同. 考慮到可移植性, 引用類型或包含引用類型的大小在32位平颱上是4個字節, 在64位平颱上是8個字節.</p>
|
||||
<p>計算機加載和保存數據時, 如果內存地址合理地對齊的將會更有效率.
|
||||
例如 2 字節大小的 int16 類型應該是偶數, 一個4 字節大小的 rune 類型地址應該是 4 的倍數, 一個 8 字節大小的 float64, uint64 或 64-bit 指針 的地址應該是 8 字節對齊的. 但是對於再大的地址對齊倍數則是不需要的,
|
||||
卽使是 complex128 等較大的數據類型.</p>
|
||||
<p>由於這個因素,一個聚閤類型(結構體或數組)的大小至少是所有字段或元素大小的總和, 或者更大因為可能存在空洞. 空洞是編譯器自動添加的沒有被使用的空間, 用於保證後麫每個字段或元素的地址相對於結構或數組的開始地址能夠閤理地對齊.</p>
|
||||
<p>由於這個因素,一個聚合類型(結構體或數組)的大小至少是所有字段或元素大小的總和, 或者更大因爲可能存在空洞. 空洞是編譯器自動添加的沒有被使用的空間, 用於保證後面每個字段或元素的地址相對於結構或數組的開始地址能夠合理地對齊.</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -2119,14 +2083,14 @@ fmt.Println(unsafe.Sizeof(<span class="hljs-typename">float64</span>(<span class
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Go的語言規範併沒有保證一個字段的聲明順序和內存中的順序是一緻的, 所以理論上一個編譯器可以隨意地重新排列每個字段的內存佈侷, 隨着在寫作本書的時候編譯器還沒有這麼做. 下麫的三個結構體有着相衕的字段, 但是第一個比另外的兩個需要多 50% 的內存.</p>
|
||||
<p>Go的語言規范併沒有保證一個字段的聲明順序和內存中的順序是一致的, 所以理論上一個編譯器可以隨意地重新排列每個字段的內存布局, 隨着在寫作本書的時候編譯器還沒有這麽做. 下面的三個結構體有着相同的字段, 但是第一個比另外的兩個需要多 50% 的內存.</p>
|
||||
<pre><code class="lang-Go"> <span class="hljs-comment">// 64-bit 32-bit</span>
|
||||
<span class="hljs-keyword">struct</span>{ <span class="hljs-typename">bool</span>; <span class="hljs-typename">float64</span>; <span class="hljs-typename">int16</span> } <span class="hljs-comment">// 3 words 4words</span>
|
||||
<span class="hljs-keyword">struct</span>{ <span class="hljs-typename">float64</span>; <span class="hljs-typename">int16</span>; <span class="hljs-typename">bool</span> } <span class="hljs-comment">// 2 words 3words</span>
|
||||
<span class="hljs-keyword">struct</span>{ <span class="hljs-typename">bool</span>; <span class="hljs-typename">int16</span>; <span class="hljs-typename">float64</span> } <span class="hljs-comment">// 2 words 3words</span>
|
||||
</code></pre>
|
||||
<p>雖然關於對齊算法的細節超齣了本書的範圍, 也不是每一個結構體都需要擔心這個問題, 不過有效的包裝可以使數據結構更加緊湊, 內存使用率和性能都可能受益.</p>
|
||||
<p><code>unsafe.Alignof</code> 函數返迴對應參數的類型需要對齊的倍數. 和 Sizeof 類似, Alignof 也是返迴一個常量錶達式, 對應一個常量. 通常情況下佈爾和數字類型需要對齊到它們本身的大小(最多8個字節), 其它的類型對齊到機器字大小.</p>
|
||||
<p>雖然關於對齊算法的細節超齣了本書的范圍, 也不是每一個結構體都需要擔心這個問題, 不過有效的包裝可以使數據結構更加緊湊, 內存使用率和性能都可能受益.</p>
|
||||
<p><code>unsafe.Alignof</code> 函數返迴對應參數的類型需要對齊的倍數. 和 Sizeof 類似, Alignof 也是返迴一個常量表達式, 對應一個常量. 通常情況下布爾和數字類型需要對齊到它們本身的大小(最多8個字節), 其它的類型對齊到機器字大小.</p>
|
||||
<p><code>unsafe.Offsetof</code> 函數的參數必鬚是一個字段 <code>x.f</code>, 然後返迴 <code>f</code> 字段相對於 <code>x</code> 起始地址的偏移量, 包括可能的空洞.</p>
|
||||
<p>圖 13.1 顯示了一個結構體變量 x 以及其在32位和64位機器上的典型的內存. 灰色區域是空洞.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> x <span class="hljs-keyword">struct</span> {
|
||||
@@ -2136,7 +2100,7 @@ fmt.Println(unsafe.Sizeof(<span class="hljs-typename">float64</span>(<span class
|
||||
}
|
||||
</code></pre>
|
||||
<p>The table below shows the results of applying the three unsafe functions to x itself and to each of its three fields:</p>
|
||||
<p>下麫顯示了應用三個函數對 x 和它的三個字段計算的結果:</p>
|
||||
<p>下面顯示了應用三個函數對 x 和它的三個字段計算的結果:</p>
|
||||
<p><img src="../images/ch13-01.png" alt=""></p>
|
||||
<p>32位繫統:</p>
|
||||
<pre><code>Sizeof(x) = 16 Alignof(x) = 4
|
||||
@@ -2149,7 +2113,7 @@ Sizeof(x.a) = 1 Alignof(x.a) = 1 Offsetof(x.a) = 0
|
||||
Sizeof(x.b) = 2 Alignof(x.b) = 2 Offsetof(x.b) = 2
|
||||
Sizeof(x.c) = 24 Alignof(x.c) = 8 Offsetof(x.c) = 8
|
||||
</code></pre><p>雖然它們在不安全的 unsafe 包, 但是這幾個函數併不是眞的不安全,
|
||||
特彆在需要優化內存空間時它們對於理解原生的內存佈侷很有幫助.</p>
|
||||
特别在需要優化內存空間時它們對於理解原生的內存布局很有幫助.</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="13.2" data-chapter-title="unsafe.Pointer" data-filepath="ch13/ch13-02.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="13.2" data-chapter-title="unsafe.Pointer" data-filepath="ch13/ch13-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,19 +2024,19 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="132-unsafepointer">13.2. unsafe.Pointer</h2>
|
||||
<p>大多數指鍼類型寫成 <em>T, 含義是 "一個指曏T類型變量的指鍼". <code>unsafe.Pointer</code> 是特彆定義的一種指鍼類型, 它可以包含任意類型變量的地址. 當然, 我們不可以直接使用 </em>p 穫取 <code>unsafe.Pointer</code> 指鍼指曏的眞實變量, 因為我們併不知道變量的類型. 和普通指鍼一樣, <code>unsafe.Pointer</code> 指鍼是可以比較的, 支持和 nil 比較判斷是否為空指鍼.</p>
|
||||
<p>一個普通的 <em>T 類型指鍼可以被轉化為 <code>unsafe.Pointer</code> 類型指鍼, 併且一個 <code>unsafe.Pointer</code> 類型指鍼也可以被轉迴普通指鍼, 也可以是和 </em>T 不衕類型的指鍼. 通過將 <code>*float64</code> 類型指鍼 轉化為 <code>*uint64</code> 類型指鍼, 我們可以檢査一個浮點數變量的位模式.</p>
|
||||
<p>大多數指針類型寫成 <em>T, 含義是 "一個指向T類型變量的指針". <code>unsafe.Pointer</code> 是特别定義的一種指針類型, 它可以包含任意類型變量的地址. 當然, 我們不可以直接使用 </em>p 穫取 <code>unsafe.Pointer</code> 指針指向的眞實變量, 因爲我們併不知道變量的類型. 和普通指針一樣, <code>unsafe.Pointer</code> 指針是可以比較的, 支持和 nil 比較判斷是否爲空指針.</p>
|
||||
<p>一個普通的 <em>T 類型指針可以被轉化爲 <code>unsafe.Pointer</code> 類型指針, 併且一個 <code>unsafe.Pointer</code> 類型指針也可以被轉迴普通指針, 也可以是和 </em>T 不同類型的指針. 通過將 <code>*float64</code> 類型指針 轉化爲 <code>*uint64</code> 類型指針, 我們可以檢査一個浮點數變量的位模式.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">package</span> math
|
||||
|
||||
<span class="hljs-keyword">func</span> Float64bits(f <span class="hljs-typename">float64</span>) <span class="hljs-typename">uint64</span> { <span class="hljs-keyword">return</span> *(*<span class="hljs-typename">uint64</span>)(unsafe.Pointer(&f)) }
|
||||
|
||||
fmt.Printf(<span class="hljs-string">"%#016x\n"</span>, Float64bits(<span class="hljs-number">1.0</span>)) <span class="hljs-comment">// "0x3ff0000000000000"</span>
|
||||
</code></pre>
|
||||
<p>通過新指鍼, 我們可以更新浮點數的位模式. 通過位模式操作浮點數是可以的, 但是更重要的意義是指鍼轉換讓我們可以在不破壞類型繫統的前提下曏內存寫入任意的值.</p>
|
||||
<p>一個 <code>unsafe.Pointer</code> 指鍼也可以被轉化為 uintptr 類似, 然後保存到指鍼型數值變量中, 用以做必要的指鍼運算.
|
||||
<p>通過新指針, 我們可以更新浮點數的位模式. 通過位模式操作浮點數是可以的, 但是更重要的意義是指針轉換讓我們可以在不破壞類型繫統的前提下向內存寫入任意的值.</p>
|
||||
<p>一個 <code>unsafe.Pointer</code> 指針也可以被轉化爲 uintptr 類似, 然後保存到指針型數值變量中, 用以做必要的指針運算.
|
||||
(第三章內容, uintptr是一個無符號的整型數, 足有保存一個地址.)
|
||||
這種轉換也是可逆的, 但是, 將 uintptr 轉為 <code>unsafe.Pointer</code> 指鍼可能破壞類型繫統, 因為併不是所有的數字都是有效的內存地址.</p>
|
||||
<p>許多將 <code>unsafe.Pointer</code> 指鍼 轉為原生數字, 然後再轉為 <code>unsafe.Pointer</code> 指鍼的操作是不安全的. 下麫的例子需要將變量 x 的地址加上 b 字段的偏移轉化為 *int16 類型指鍼, 然後通過該指鍼更新 <code>x.b</code>:</p>
|
||||
這種轉換也是可逆的, 但是, 將 uintptr 轉爲 <code>unsafe.Pointer</code> 指針可能破壞類型繫統, 因爲併不是所有的數字都是有效的內存地址.</p>
|
||||
<p>許多將 <code>unsafe.Pointer</code> 指針 轉爲原生數字, 然後再轉爲 <code>unsafe.Pointer</code> 指針的操作是不安全的. 下面的例子需要將變量 x 的地址加上 b 字段的偏移轉化爲 *int16 類型指針, 然後通過該指針更新 <code>x.b</code>:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">//gopl.io/ch13/unsafeptr</span>
|
||||
|
||||
<span class="hljs-keyword">var</span> x <span class="hljs-keyword">struct</span> {
|
||||
@@ -2087,16 +2051,16 @@ pb := (*<span class="hljs-typename">int16</span>)(unsafe.Pointer(
|
||||
*pb = <span class="hljs-number">42</span>
|
||||
fmt.Println(x.b) <span class="hljs-comment">// "42"</span>
|
||||
</code></pre>
|
||||
<p>盡管寫法很繁瑣, 但在這裏併不是一件壞事, 因為這些功能應該很謹慎地使用. 不要試圖將引入可能而破壞代碼的正確性的 uintptr 臨時變量. 下麫段代碼是不正確的:</p>
|
||||
<p>錯誤的原因很微妙. 有時候垃圾迴收器會移動一些變量以降低內存碎片的問題.這類垃圾迴收器被稱為移動GC. 當一個變量被移動, 所有的保存改變量舊地址的指鍼必鬚衕時被更新為變量移動後的新地址. 從垃圾收集器的視角來看, 一個 <code>unsafe.Pointer</code> 是一個指鍼, 因此當變量被移動是對應的指鍼必鬚被更新, 但是 <code>uintptr</code> 隻是一個普通的數字, 所以其值不應該被改變. 上麫錯誤的代碼因為一個非指鍼的臨時變量 <code>tmp</code>, 導緻垃圾收集器無法正確識彆這個是一個指曏變量 <code>x</code> 的指鍼. 第二個語句執行時, 變量 <code>x</code> 可能已經被轉移, 臨時變量 <code>tmp</code> 也就不在對應現在的 <code>&x.b</code>. 第三個賦值語句將徹底摧譭那個之前的那部分內存空間.</p>
|
||||
<p>有很多類似原因導緻的錯誤. 例如這條語句:</p>
|
||||
<p>盡管寫法很繁瑣, 但在這里併不是一件壞事, 因爲這些功能應該很謹慎地使用. 不要試圖將引入可能而破壞代碼的正確性的 uintptr 臨時變量. 下面段代碼是不正確的:</p>
|
||||
<p>錯誤的原因很微妙. 有時候垃圾迴收器會移動一些變量以降低內存碎片的問題.這類垃圾迴收器被稱爲移動GC. 當一個變量被移動, 所有的保存改變量舊地址的指針必鬚同時被更新爲變量移動後的新地址. 從垃圾收集器的視角來看, 一個 <code>unsafe.Pointer</code> 是一個指針, 因此當變量被移動是對應的指針必鬚被更新, 但是 <code>uintptr</code> 隻是一個普通的數字, 所以其值不應該被改變. 上面錯誤的代碼因爲一個非指針的臨時變量 <code>tmp</code>, 導致垃圾收集器無法正確識别這個是一個指向變量 <code>x</code> 的指針. 第二個語句執行時, 變量 <code>x</code> 可能已經被轉移, 臨時變量 <code>tmp</code> 也就不在對應現在的 <code>&x.b</code>. 第三個賦值語句將徹底摧譭那個之前的那部分內存空間.</p>
|
||||
<p>有很多類似原因導致的錯誤. 例如這條語句:</p>
|
||||
<pre><code class="lang-Go">pT := <span class="hljs-typename">uintptr</span>(unsafe.Pointer(<span class="hljs-built_in">new</span>(T))) <span class="hljs-comment">// 提示: 錯誤!</span>
|
||||
</code></pre>
|
||||
<p>這裏併沒有指鍼引用 <code>new</code> 新創建的變量, 因此語句執行完成之後, 垃圾收集器有權迴收其內存空間, 所以返迴的 <code>pT</code> 保存將是無效的地址.</p>
|
||||
<p>目前的Go語言實現還沒有使用移動GC(未來可能實現), 但這不該是僥倖的理由: 當前的Go實現已經有移動變量的場景. 在5.2節我們提到goroutine的棧是根據需要動態增長的. 當這個時候, 原來棧中的所以變量可能需要被移動到新的更大的棧中, 所以我們無法確保變量的地址在整個使用週期內保持不變.</p>
|
||||
<p>在編寫本文時, 還沒有清晰的原則就指引Go程序員, 什麼樣 <code>unsafe.Pointer</code> 和 <code>uintptr</code> 的轉換是不安全的(參考 <a href="https://github.com/golang/go/issues/7192" target="_blank">Go issue7192</a>. 譯註: 該問題已經脩復.), 因此我們強烈建議按照最壞的方式處理. 將所有包含變量 <code>y</code> 地址的 <code>uintptr</code> 類型變量當作 BUG 處理, 衕時減少不必要的 <code>unsafe.Pointer</code> 到 <code>uintptr</code> 的轉換. 在第一個例子中, 有三個到 <code>uintptr</code> 的轉換, 字段偏移量的運算, 所有的轉換全在一個錶達式完成.</p>
|
||||
<p>當調用一個庫函數, 併且返迴的是 <code>uintptr</code> 類型是, 比如下麫反射包中的相關函數,
|
||||
返迴的結果應該立卽轉換為 <code>unsafe.Pointer</code> 以確保指鍼指曏的是相衕的變量.</p>
|
||||
<p>這里併沒有指針引用 <code>new</code> 新創建的變量, 因此語句執行完成之後, 垃圾收集器有權迴收其內存空間, 所以返迴的 <code>pT</code> 保存將是無效的地址.</p>
|
||||
<p>目前的Go語言實現還沒有使用移動GC(未來可能實現), 但這不該是僥幸的理由: 當前的Go實現已經有移動變量的場景. 在5.2節我們提到goroutine的棧是根據需要動態增長的. 當這個時候, 原來棧中的所以變量可能需要被移動到新的更大的棧中, 所以我們無法確保變量的地址在整個使用週期內保持不變.</p>
|
||||
<p>在編寫本文時, 還沒有清晰的原則就指引Go程序員, 什麽樣 <code>unsafe.Pointer</code> 和 <code>uintptr</code> 的轉換是不安全的(參考 <a href="https://github.com/golang/go/issues/7192" target="_blank">Go issue7192</a>. 譯註: 該問題已經脩複.), 因此我們強烈建議按照最壞的方式處理. 將所有包含變量 <code>y</code> 地址的 <code>uintptr</code> 類型變量當作 BUG 處理, 同時減少不必要的 <code>unsafe.Pointer</code> 到 <code>uintptr</code> 的轉換. 在第一個例子中, 有三個到 <code>uintptr</code> 的轉換, 字段偏移量的運算, 所有的轉換全在一個表達式完成.</p>
|
||||
<p>當調用一個庫函數, 併且返迴的是 <code>uintptr</code> 類型是, 比如下面反射包中的相關函數,
|
||||
返迴的結果應該立卽轉換爲 <code>unsafe.Pointer</code> 以確保指針指向的是相同的變量.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">package</span> reflect
|
||||
|
||||
<span class="hljs-keyword">func</span> (Value) Pointer() <span class="hljs-typename">uintptr</span>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="13.3" data-chapter-title="示例: 深度相等判斷" data-filepath="ch13/ch13-03.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="13.3" data-chapter-title="示例: 深度相等判斷" data-filepath="ch13/ch13-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,7 +2024,7 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="133-示例-深度相等判斷">13.3. 示例: 深度相等判斷</h2>
|
||||
<p>來自 reflect 包的 DeepEqual 對兩個值進行深度相等判斷. DeepEqual 使用內建的 <code>==</code> 操作符對基礎類型進行相等判斷, 對於復閤類型則遞歸變量每個基礎類型然後做類似的比較判斷. 因為它工作在任意的類型上, 甚至對一些不支持 <code>==</code> 操作符的類型也可以工作, 因此在一些測試代碼中被廣氾地使用. 比如下麫的代碼是用 DeepEqual 比較兩個字符串數組是否等價.</p>
|
||||
<p>來自 reflect 包的 DeepEqual 對兩個值進行深度相等判斷. DeepEqual 使用內建的 <code>==</code> 操作符對基礎類型進行相等判斷, 對於複合類型則遞歸變量每個基礎類型然後做類似的比較判斷. 因爲它工作在任意的類型上, 甚至對一些不支持 <code>==</code> 操作符的類型也可以工作, 因此在一些測試代碼中被廣泛地使用. 比如下面的代碼是用 DeepEqual 比較兩個字符串數組是否等價.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> TestSplit(t *testing.T) {
|
||||
got := strings.Split(<span class="hljs-string">"a:b:c"</span>, <span class="hljs-string">":"</span>)
|
||||
want := []<span class="hljs-typename">string</span>{<span class="hljs-string">"a"</span>, <span class="hljs-string">"b"</span>, <span class="hljs-string">"c"</span>};
|
||||
@@ -2069,14 +2033,14 @@
|
||||
</code></pre>
|
||||
<p>盡管 DeepEqual 很方便, 而且可以支持任意的類型, 但是也有不足之處.
|
||||
例如, 它將一個 nil map 和 非 nil 的空的 map 視作不相等,
|
||||
衕樣 nil slice 和 非 nil 的空的 slice 也不相等.</p>
|
||||
同樣 nil slice 和 非 nil 的空的 slice 也不相等.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> a, b []<span class="hljs-typename">string</span> = <span class="hljs-constant">nil</span>, []<span class="hljs-typename">string</span>{}
|
||||
fmt.Println(reflect.DeepEqual(a, b)) <span class="hljs-comment">// "false"</span>
|
||||
|
||||
<span class="hljs-keyword">var</span> c, d <span class="hljs-keyword">map</span>[<span class="hljs-typename">string</span>]<span class="hljs-typename">int</span> = <span class="hljs-constant">nil</span>, <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[<span class="hljs-typename">string</span>]<span class="hljs-typename">int</span>)
|
||||
fmt.Println(reflect.DeepEqual(c, d)) <span class="hljs-comment">// "false"</span>
|
||||
</code></pre>
|
||||
<p>在這裏定義一個自己的 Equal 函數用於比較人員的值. 和 DeepEqual 類似的是它也是基於 slice 和 map 的元素進行遞歸比較, 不衕之處是它將 nil slice(map類似) 和非 nil 的空 slice 視作相等的值. 基礎部分的比較可以基於反射完成, 和 12.3 章的 Display 實現方法類似. 衕樣, 我們頂一個一個內部函數 equal, 用於內部的遞歸比較. 目前不用關心 seen 參數. 對於每一對需要比較的 x 和 y, equal 函數 首先檢測它們是否都有效(或都無效), 然後檢測它們是否是相衕的類型. 剩下的部分是一個大的 switch 分支, 用於擁有相衕基礎類型的比較. 因為頁麫空間的限製, 我們省略了一些類似的分支.</p>
|
||||
<p>在這里定義一個自己的 Equal 函數用於比較人員的值. 和 DeepEqual 類似的是它也是基於 slice 和 map 的元素進行遞歸比較, 不同之處是它將 nil slice(map類似) 和非 nil 的空 slice 視作相等的值. 基礎部分的比較可以基於反射完成, 和 12.3 章的 Display 實現方法類似. 同樣, 我們頂一個一個內部函數 equal, 用於內部的遞歸比較. 目前不用關心 seen 參數. 對於每一對需要比較的 x 和 y, equal 函數 首先檢測它們是否都有效(或都無效), 然後檢測它們是否是相同的類型. 剩下的部分是一個大的 switch 分支, 用於擁有相同基礎類型的比較. 因爲頁面空間的限製, 我們省略了一些類似的分支.</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch13/equal
|
||||
<span class="hljs-keyword">func</span> equal(x, y reflect.Value, seen <span class="hljs-keyword">map</span>[comparison]<span class="hljs-typename">bool</span>) <span class="hljs-typename">bool</span> {
|
||||
<span class="hljs-keyword">if</span> !x.IsValid() || !y.IsValid() {
|
||||
@@ -2116,8 +2080,8 @@ fmt.Println(reflect.DeepEqual(c, d)) <span class="hljs-comment">// "false&q
|
||||
<span class="hljs-built_in">panic</span>(<span class="hljs-string">"unreachable"</span>)
|
||||
}
|
||||
</code></pre>
|
||||
<p>和前麫的建議一樣, 我們不公開使用反射相關的接口,
|
||||
所以導齣的函數需要在內部自己將變量轉為 reflect.Value 類型.</p>
|
||||
<p>和前面的建議一樣, 我們不公開使用反射相關的接口,
|
||||
所以導齣的函數需要在內部自己將變量轉爲 reflect.Value 類型.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// Equal reports whether x and y are deeply equal.</span>
|
||||
<span class="hljs-keyword">func</span> Equal(x, y <span class="hljs-keyword">interface</span>{}) <span class="hljs-typename">bool</span> {
|
||||
seen := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[comparison]<span class="hljs-typename">bool</span>)
|
||||
@@ -2129,7 +2093,7 @@ fmt.Println(reflect.DeepEqual(c, d)) <span class="hljs-comment">// "false&q
|
||||
treflect.Type
|
||||
}
|
||||
</code></pre>
|
||||
<p>為了確保算法對於循環數據結構也能正常退齣, 我們必鬚記彔每次已經比較的變量, 從而避免進入第二次的比較. Equal 函數分配了一組用於比較的結構體, 包含每對比較對象的地址(unsafe.Pointer形式保存)和類型. 我們記彔類型的原因是, 有些不衕的變量可能對應相衕的地址. 例如, 如果 x 和 y 都是數組類型, 那麼 x 和 <code>x[0]</code> 將對應相衕的地址, y 和 <code>y[0]</code> 也是對應相衕的地址, 這可以用於判斷 對x 和 y 比較 或 x[0] 和 y[0] 的是否進行過了.</p>
|
||||
<p>爲了確保算法對於循環數據結構也能正常退齣, 我們必鬚記録每次已經比較的變量, 從而避免進入第二次的比較. Equal 函數分配了一組用於比較的結構體, 包含每對比較對象的地址(unsafe.Pointer形式保存)和類型. 我們記録類型的原因是, 有些不同的變量可能對應相同的地址. 例如, 如果 x 和 y 都是數組類型, 那麽 x 和 <code>x[0]</code> 將對應相同的地址, y 和 <code>y[0]</code> 也是對應相同的地址, 這可以用於判斷 對x 和 y 比較 或 x[0] 和 y[0] 的是否進行過了.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// cycle check</span>
|
||||
<span class="hljs-keyword">if</span> x.CanAddr() && y.CanAddr() {
|
||||
xptr := unsafe.Pointer(x.UnsafeAddr())
|
||||
@@ -2150,7 +2114,7 @@ fmt.Println(Equal([]<span class="hljs-typename">string</span>{<span class="hljs-
|
||||
fmt.Println(Equal([]<span class="hljs-typename">string</span>(<span class="hljs-constant">nil</span>), []<span class="hljs-typename">string</span>{})) <span class="hljs-comment">// "true"</span>
|
||||
fmt.Println(Equal(<span class="hljs-keyword">map</span>[<span class="hljs-typename">string</span>]<span class="hljs-typename">int</span>(<span class="hljs-constant">nil</span>), <span class="hljs-keyword">map</span>[<span class="hljs-typename">string</span>]<span class="hljs-typename">int</span>{})) <span class="hljs-comment">// "true"</span>
|
||||
</code></pre>
|
||||
<p>它甚至可以處理類似12.3章中導緻Display陷入死循環的數據.</p>
|
||||
<p>它甚至可以處理類似12.3章中導致Display陷入死循環的數據.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// Circular linked lists a -> b -> a and c -> c.</span>
|
||||
<span class="hljs-keyword">type</span> link <span class="hljs-keyword">struct</span> {
|
||||
value <span class="hljs-typename">string</span>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="13.4" data-chapter-title="通過cgo調用C代碼" data-filepath="ch13/ch13-04.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="13.4" data-chapter-title="通過cgo調用C代碼" data-filepath="ch13/ch13-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,19 +2024,19 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="134-通過cgo調用c代碼">13.4. 通過cgo調用C代碼</h2>
|
||||
<p>Go程序可能會遇到要訪問C語言的某些硬件驅動的場景, 或者是從一個C++實現的嵌入式數據庫査詢記彔的場景, 或者是使用Fortran實現的一些綫性代數庫的場景. C作為一個通用語言, 很多庫會選擇提供一個C兼容的API, 然後用其他語言實現.</p>
|
||||
<p>在本節中, 我們將構建一個簡易的數據壓縮程序, 通過使用一個Go語言自帶的叫cgo的用於支援C語言函數調用的工具. 這類工具被稱為外圍函數接口(ffi), 併且cgo也不是Go中唯一的類似工具. SWIG(swig.org) 是類似的另一個被廣氾使用的工具, 它提供了很多復雜特性以支援C++的集成, 但 SWIG 不是這裏要討論的主題.</p>
|
||||
<p>在標準庫的 <code>compress/...</code> 子目彔有很多流行的壓縮算法的編碼和解碼實現, 包括LZW壓縮算法(Unix的compress命令用的算法)和DEFLATE壓縮算法(GNU gzip命令用的算法). 這些包的API的細節有些差異, 但是它們都提供了鍼對 <code>io.Writer</code> 的壓縮接口, 和提供了鍼對 <code>io.Reader</code> 的解壓縮接口. 例如:</p>
|
||||
<p>Go程序可能會遇到要訪問C語言的某些硬件驅動的場景, 或者是從一個C++實現的嵌入式數據庫査詢記録的場景, 或者是使用Fortran實現的一些線性代數庫的場景. C作爲一個通用語言, 很多庫會選擇提供一個C兼容的API, 然後用其他語言實現.</p>
|
||||
<p>在本節中, 我們將構建一個簡易的數據壓縮程序, 通過使用一個Go語言自帶的叫cgo的用於支援C語言函數調用的工具. 這類工具被稱爲外圍函數接口(ffi), 併且cgo也不是Go中唯一的類似工具. SWIG(swig.org) 是類似的另一個被廣泛使用的工具, 它提供了很多複雜特性以支援C++的集成, 但 SWIG 不是這里要討論的主題.</p>
|
||||
<p>在標準庫的 <code>compress/...</code> 子目録有很多流行的壓縮算法的編碼和解碼實現, 包括LZW壓縮算法(Unix的compress命令用的算法)和DEFLATE壓縮算法(GNU gzip命令用的算法). 這些包的API的細節有些差異, 但是它們都提供了針對 <code>io.Writer</code> 的壓縮接口, 和提供了針對 <code>io.Reader</code> 的解壓縮接口. 例如:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">package</span> gzip <span class="hljs-comment">// compress/gzip</span>
|
||||
<span class="hljs-keyword">func</span> NewWriter(w io.Writer) io.WriteCloser
|
||||
<span class="hljs-keyword">func</span> NewReader(r io.Reader) (io.ReadCloser, error)
|
||||
</code></pre>
|
||||
<p>bzip2壓縮算法, 是基於優雅的 Burrows-Wheeler 變換, 運行速度比 gzip 要慢, 但是可以提供更高的壓縮比. 標準庫的 <code>compress/bzip2</code> 包目前還沒有提供 bzip2 算法的壓縮實現. 完全從頭實現是一個繁瑣的工作, 而且 bzip.org 有現成的 libbzip2 開源實現, 文檔齊全而且性能較好,</p>
|
||||
<p>如果C庫比較小, 我們可以用純Go重新實現一遍. 如果我們對性能沒有特殊要求, 我們可以用 <code>os/exec</code> 包的方法將C編寫的應用程序作為一個子進行運行. 隻有當你需要使用復雜但是性能更高的底層C接口時, 就是使用cgo的場景了. 下麫我們將通過一個例子講述cgo的用法.</p>
|
||||
<p>如果C庫比較小, 我們可以用純Go重新實現一遍. 如果我們對性能沒有特殊要求, 我們可以用 <code>os/exec</code> 包的方法將C編寫的應用程序作爲一個子進行運行. 隻有當你需要使用複雜但是性能更高的底層C接口時, 就是使用cgo的場景了. 下面我們將通過一個例子講述cgo的用法.</p>
|
||||
<p>要使用 libbzip2, 我們需要一個 <code>bz_stream</code> 結構體, 用於保持輸入和輸齣緩存.
|
||||
然後有三個函數: BZ2_bzCompressInit 用於初始化緩存, BZ2_bzCompress 用於將輸入緩存的數據壓縮到輸齣緩存, BZ2_bzCompressEnd 用於釋放不需要的緩存.
|
||||
(目前不要擔心包的具體結構, 這個例子的目的就是演示各個部分如何組閤在一起的)</p>
|
||||
<p>我們可以在Go代碼中直接調用 BZ2_bzCompressInit 和 BZ2_bzCompressEnd, 但是對於 BZ2_bzCompress, 我們將定義一個C語言的包裝函數, 為了顯示他是如何完成的. 下麫是C代碼, 對應一個獨立的文件.</p>
|
||||
(目前不要擔心包的具體結構, 這個例子的目的就是演示各個部分如何組合在一起的)</p>
|
||||
<p>我們可以在Go代碼中直接調用 BZ2_bzCompressInit 和 BZ2_bzCompressEnd, 但是對於 BZ2_bzCompress, 我們將定義一個C語言的包裝函數, 爲了顯示他是如何完成的. 下面是C代碼, 對應一個獨立的文件.</p>
|
||||
<pre><code class="lang-C">gopl.io/ch13/bzip
|
||||
|
||||
<span class="hljs-comment">/* This file is gopl.io/ch13/bzip/bzip2.c, */</span>
|
||||
@@ -2091,7 +2055,7 @@
|
||||
<span class="hljs-keyword">return</span> r;
|
||||
}
|
||||
</code></pre>
|
||||
<p>現在讓我們轉到Go部分, 第一部分如下所示. 其中 <code>import "C"</code> 的語句是比較特彆的. 其實併沒有一個叫 <code>C</code> 的包, 但是這行語句會讓Go構建在編譯之前先運行cgo工具.</p>
|
||||
<p>現在讓我們轉到Go部分, 第一部分如下所示. 其中 <code>import "C"</code> 的語句是比較特别的. 其實併沒有一個叫 <code>C</code> 的包, 但是這行語句會讓Go構建在編譯之前先運行cgo工具.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// Package bzip provides a writer that uses bzip2 compression (bzip.org).</span>
|
||||
<span class="hljs-keyword">package</span> bzip
|
||||
|
||||
@@ -2127,7 +2091,7 @@ int bz2compress(bz_stream *s, int action,
|
||||
<span class="hljs-keyword">return</span> w
|
||||
}
|
||||
</code></pre>
|
||||
<p>在循環的每次迭代中, 曏bz2compress傳入數據的地址和剩餘部分的長度, 還有輸齣緩存 w.outbuf 的地址和容量. 這兩個長度信息通過它們的地址傳入而不是值傳入, 因為bz2compress函數可能會根據已經壓縮的數據和壓縮後數據的大小來更新這兩個值(譯註: 這裏的用法有問題, 勘誤已經提到. 具體脩復的方法稍後再補充). 每個塊壓縮後的數據被寫入到底層的 io.Writer.</p>
|
||||
<p>在循環的每次迭代中, 向bz2compress傳入數據的地址和剩餘部分的長度, 還有輸齣緩存 w.outbuf 的地址和容量. 這兩個長度信息通過它們的地址傳入而不是值傳入, 因爲bz2compress函數可能會根據已經壓縮的數據和壓縮後數據的大小來更新這兩個值(譯註: 這里的用法有問題, 勘誤已經提到. 具體脩複的方法稍後再補充). 每個塊壓縮後的數據被寫入到底層的 io.Writer.</p>
|
||||
<p>Close 方法和 Write 方法有着類似的結構, 通過一個循環將剩餘的壓縮數據刷新到輸齣緩存.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// Close flushes the compressed data and closes the stream.</span>
|
||||
<span class="hljs-comment">// It does not close the underlying io.Writer.</span>
|
||||
@@ -2152,9 +2116,9 @@ int bz2compress(bz_stream *s, int action,
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>壓縮完成後, Close 用了 defer 確保函數退齣前調用 C.BZ2_bzCompressEnd 釋放輸入和輸齣流的緩存. 此刻 <code>w.stream</code> 指鍼將不在有效, 我們將它設置為 nil 以保證安全, 然後在每個方法中增加 nil 檢測, 以防止用戶在關閉後依然錯誤使用相關方法.</p>
|
||||
<p>不僅僅寫是非併髮安全的, 甚至併髮調用 Close 和 Write 也可能導緻C代碼的崩潰. 脩復這個問題是 練習13.3 的內容.</p>
|
||||
<p>下麫的bzipper程序是使用我們自己包實現的bzip2壓縮命令. 它的行為和許多Unix繫統的 bzip2 命令類似.</p>
|
||||
<p>壓縮完成後, Close 用了 defer 確保函數退齣前調用 C.BZ2_bzCompressEnd 釋放輸入和輸齣流的緩存. 此刻 <code>w.stream</code> 指針將不在有效, 我們將它設置爲 nil 以保證安全, 然後在每個方法中增加 nil 檢測, 以防止用戶在關閉後依然錯誤使用相關方法.</p>
|
||||
<p>不僅僅寫是非併發安全的, 甚至併發調用 Close 和 Write 也可能導致C代碼的崩潰. 脩複這個問題是 練習13.3 的內容.</p>
|
||||
<p>下面的bzipper程序是使用我們自己包實現的bzip2壓縮命令. 它的行爲和許多Unix繫統的 bzip2 命令類似.</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch13/bzipper
|
||||
|
||||
<span class="hljs-comment">// Bzipper reads input, bzip2-compresses it, and writes it out.</span>
|
||||
@@ -2177,7 +2141,7 @@ int bz2compress(bz_stream *s, int action,
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>在上麫的場景中, 我們使用 bzipper 壓縮了 /usr/share/dict/words 繫統自帶的詞典, 從 938,848 字節壓縮到 335,405 字節, 大於是原始大小的三分之一. 然後使用繫統自帶的bunzip2命令進行解壓. 壓縮前後文件的SHA256哈希碼是相衕了, 這也說明了我們的壓縮工具是可用的. (如果你的繫統沒有sha256sum命令, 那麼請先按照 練習4.2 實現一個類似的工具)</p>
|
||||
<p>在上面的場景中, 我們使用 bzipper 壓縮了 /usr/share/dict/words 繫統自帶的詞典, 從 938,848 字節壓縮到 335,405 字節, 大於是原始大小的三分之一. 然後使用繫統自帶的bunzip2命令進行解壓. 壓縮前後文件的SHA256哈希碼是相同了, 這也説明了我們的壓縮工具是可用的. (如果你的繫統沒有sha256sum命令, 那麽請先按照 練習4.2 實現一個類似的工具)</p>
|
||||
<pre><code>$ go build gopl.io/ch13/bzipper
|
||||
$ wc -c < /usr/share/dict/words
|
||||
938848
|
||||
@@ -2187,9 +2151,9 @@ $ ./bzipper < /usr/share/dict/words | wc -c
|
||||
335405
|
||||
$ ./bzipper < /usr/share/dict/words | bunzip2 | sha256sum
|
||||
126a4ef38493313edc50b86f90dfdaf7c59ec6c948451eac228f2f3a8ab1a6ed -
|
||||
</code></pre><p>我們演示了將一個C庫鏈接到Go程序. 相反, 將Go編譯為靜態庫然後鏈接到C程序, 或者將Go編譯為動態庫然後在C程序中動態加載也都是可行的. 這裏我們隻展示的cgo很小的一些方麫, 更多的關於內存管理, 指鍼, 迴調函數, 信號處理, 字符串, errno處理, 終結器, 以及 goroutines 和繫統綫程的關繫等, 有很多細節可以討論. 特彆是如何將Go的指鍼傳入C函數的規則也是異常復雜的, 部分的原因在 13.2節 有討論到, 但是在Go1.5中還沒有被明確. 如果要進一步閱讀, 可以從 <a href="https://golang.org/cmd/cgo" target="_blank">https://golang.org/cmd/cgo</a> 開始.</p>
|
||||
<p><strong>練習13.3:</strong> 使用 sync.Mutex 以保證 bzip2.writer 在多個 goroutines 中被併髮調用是安全的.</p>
|
||||
<p><strong>練習13.4:</strong> 因為C庫依賴的限製. 使用 <code>os/exec</code> 包啓動 <code>/bin/bzip2</code> 命令作為一個子進程, 提供一個純Go的 bzip.NewWriter 的替代實現.</p>
|
||||
</code></pre><p>我們演示了將一個C庫鏈接到Go程序. 相反, 將Go編譯爲靜態庫然後鏈接到C程序, 或者將Go編譯爲動態庫然後在C程序中動態加載也都是可行的. 這里我們隻展示的cgo很小的一些方面, 更多的關於內存管理, 指針, 迴調函數, 信號處理, 字符串, errno處理, 終結器, 以及 goroutines 和繫統線程的關繫等, 有很多細節可以討論. 特别是如何將Go的指針傳入C函數的規則也是異常複雜的, 部分的原因在 13.2節 有討論到, 但是在Go1.5中還沒有被明確. 如果要進一步閲讀, 可以從 <a href="https://golang.org/cmd/cgo" target="_blank">https://golang.org/cmd/cgo</a> 開始.</p>
|
||||
<p><strong>練習13.3:</strong> 使用 sync.Mutex 以保證 bzip2.writer 在多個 goroutines 中被併發調用是安全的.</p>
|
||||
<p><strong>練習13.4:</strong> 因爲C庫依賴的限製. 使用 <code>os/exec</code> 包啟動 <code>/bin/bzip2</code> 命令作爲一個子進程, 提供一個純Go的 bzip.NewWriter 的替代實現.</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
|
||||
|
||||
<link rel="next" href="../exercise/ex.html" />
|
||||
<link rel="next" href="../errata.html" />
|
||||
|
||||
|
||||
<link rel="prev" href="../ch13/ch13-04.html" />
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="13.5" data-chapter-title="幾點忠告" data-filepath="ch13/ch13-05.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="13.5" data-chapter-title="幾點忠告" data-filepath="ch13/ch13-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,9 +2024,9 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="135-幾點忠告">13.5. 幾點忠告</h2>
|
||||
<p>我們在前一章結尾的時候, 我們警告要謹慎使用反射. 那些警告衕樣適用於本章的 unsafe 包.</p>
|
||||
<p>高級語言使得程序員不用在關繫眞正運行程序的指令細節, 衕時也不再需要關註許多如內部佈侷之類的無關實現細節. 因為這個絶緣的抽象層, 我們可以編寫安全健壯的, 併且可以運行在不衕操作繫統上的具有高度可移植性的程序.</p>
|
||||
<p>但是 unsafe 包, 讓程序員可以透過這個絶緣的抽象層使用使用一些必要的功能, 或者是為了更高的性能. 代價就是犧牲了可移植性和程序安全, 因此使用 unsafe 是一個危險的行為. 我們對何時以及如何使用unsafe包的建議和我們在11.5節提到的Knuth對過早優化的建議類似. 大多數Go程序員可能永遠不會需要直接使用unsafe包. 當然, 永遠都會有一些用 unsafe 包實現會更簡單的場景. 如果確實認為使用 unsafe 包是最理想的方式, 那麼應該盡可能將它限製較小的範圍, 那樣其他代碼忽略unsafe的影響.</p>
|
||||
<p>我們在前一章結尾的時候, 我們警告要謹慎使用反射. 那些警告同樣適用於本章的 unsafe 包.</p>
|
||||
<p>高級語言使得程序員不用在關繫眞正運行程序的指令細節, 同時也不再需要關註許多如內部布局之類的無關實現細節. 因爲這個絶緣的抽象層, 我們可以編寫安全健壯的, 併且可以運行在不同操作繫統上的具有高度可移植性的程序.</p>
|
||||
<p>但是 unsafe 包, 讓程序員可以透過這個絶緣的抽象層使用使用一些必要的功能, 或者是爲了更高的性能. 代價就是犧牲了可移植性和程序安全, 因此使用 unsafe 是一個危險的行爲. 我們對何時以及如何使用unsafe包的建議和我們在11.5節提到的Knuth對過早優化的建議類似. 大多數Go程序員可能永遠不會需要直接使用unsafe包. 當然, 永遠都會有一些用 unsafe 包實現會更簡單的場景. 如果確實認爲使用 unsafe 包是最理想的方式, 那麽應該盡可能將它限製較小的范圍, 那樣其他代碼忽略unsafe的影響.</p>
|
||||
<p>現在, 把最後兩章拋入腦後吧. 編寫一些實在的應用. 遠離reflect的unsafe包, 除非你確實需要它們.</p>
|
||||
<p>用Go快樂地編程. 我們希望你能像我們一樣喜歡Go語言.</p>
|
||||
|
||||
@@ -2078,7 +2042,7 @@
|
||||
<a href="../ch13/ch13-04.html" class="navigation navigation-prev " aria-label="Previous page: 通過cgo調用C代碼"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../exercise/ex.html" class="navigation navigation-next " aria-label="Next page: 習題解答"><i class="fa fa-angle-right"></i></a>
|
||||
<a href="../errata.html" class="navigation navigation-next " aria-label="Next page: 勘誤"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
108
ch13/ch13.html
108
ch13/ch13.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="13" data-chapter-title="底層編程" data-filepath="ch13/ch13.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="13" data-chapter-title="底層編程" data-filepath="ch13/ch13.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,16 +2024,16 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h1 id="第13章-底層編程">第13章 底層編程</h1>
|
||||
<p>Go的設計包含了諸多安全策略, 限製了可能導緻程序錯誤的用法. 編譯時類型檢査檢測可以髮現大多數類型不匹配的變量操作, 例如兩個字符串做減法的錯誤. 字符串, 字典, 切片 和管道等所有的內置類型, 都有嚴格的類型轉換規則.</p>
|
||||
<p>對於無法靜態檢測到的錯誤, 例如數組訪問越界或使用空指鍼, 動態檢測可以保證程序在遇到問題的時候立卽終止併打印相關的錯誤信息. 自動內存管理(垃圾迴收)消除了大部分野指鍼和內存洩漏的問題.</p>
|
||||
<p>Go的實現刻意隱藏了很多底層細節. 我們無法知道一個結構體的內存佈侷, 也無法穫取一個運行函數的機器碼, 也無法知道當前的 goroutine 是運行在哪個操作繫統綫程上. 事實上, Go的調度器會自己決定是否需要將 goroutine 從一個操作繫統綫程轉移到另一個操作繫統綫程. 一個指曏變量的指鍼也併沒有展示變量眞實的地址. 因為垃圾迴收器會根據需要移動變量的位置, 當然對應的也會被自動更新.</p>
|
||||
<p>總的來說, Go語言的這些特殊使得Go程序相比較低級的C語言來說, 更容易預測, 更容易理解, 也不容易崩潰. 通過隱藏底層的細節, 也使得Go程序具有高度的可移植性, 因為語言的語義在很大程度上是獨立於任何編譯器, 操作繫統和CPU繫統結構的(當然也不完全絶對獨立: 例如CPU字的大小, 某些錶達式求值的順序, 還有編譯器實現的一些限製).</p>
|
||||
<p>Go的設計包含了諸多安全策略, 限製了可能導致程序錯誤的用法. 編譯時類型檢査檢測可以發現大多數類型不匹配的變量操作, 例如兩個字符串做減法的錯誤. 字符串, 字典, 切片 和管道等所有的內置類型, 都有嚴格的類型轉換規則.</p>
|
||||
<p>對於無法靜態檢測到的錯誤, 例如數組訪問越界或使用空指針, 動態檢測可以保證程序在遇到問題的時候立卽終止併打印相關的錯誤信息. 自動內存管理(垃圾迴收)消除了大部分野指針和內存洩漏的問題.</p>
|
||||
<p>Go的實現刻意隱藏了很多底層細節. 我們無法知道一個結構體的內存布局, 也無法穫取一個運行函數的機器碼, 也無法知道當前的 goroutine 是運行在哪個操作繫統線程上. 事實上, Go的調度器會自己決定是否需要將 goroutine 從一個操作繫統線程轉移到另一個操作繫統線程. 一個指向變量的指針也併沒有展示變量眞實的地址. 因爲垃圾迴收器會根據需要移動變量的位置, 當然對應的也會被自動更新.</p>
|
||||
<p>總的來説, Go語言的這些特殊使得Go程序相比較低級的C語言來説, 更容易預測, 更容易理解, 也不容易崩潰. 通過隱藏底層的細節, 也使得Go程序具有高度的可移植性, 因爲語言的語義在很大程度上是獨立於任何編譯器, 操作繫統和CPU繫統結構的(當然也不完全絶對獨立: 例如CPU字的大小, 某些表達式求值的順序, 還有編譯器實現的一些限製).</p>
|
||||
<p>有時候我們可能會放棄部分語言特性而優先選擇更好的性能優化, 與其他語言編寫的庫互操作, 或者不用純Go語言來實現某些函數.</p>
|
||||
<p>在本章, 我們將展示如何使用 unsafe 包來襬脫通常的規則限製, 如何創建C函數庫的綁定, 以及如何進行繫統調用.</p>
|
||||
<p>本章描述的方法不應該輕易使用. 如果沒有處理好細節, 它們可能導緻各種不可預測的隱晦的錯誤, 甚至連本地的C程序員也無法理解. 使用 unsafe 包衕時也無法保證與未來版本的兼容性, 因為在有意無意中會使用很多實現的細節, 而這些實現的細節在未來很可能會改變.</p>
|
||||
<p>unsafe 包的實現比較特殊. 雖然它可以和普通包一樣的導入和使用, 但它實際上是由編譯器實現的. 它提供了一些訪問語言內部特性的方法, 特彆是內存佈侷相關的細節.
|
||||
將這些特彆封裝到一個獨立的包中, 是為在極少數情況下需要使用的時候, 引起人們的註意(它們是不安全的). 此外, 有一些環境因為安全的因素可能限製這個包的使用.</p>
|
||||
<p>unsafe 包被廣氾地用於比較低級的包, 例如 runtime, os, syscall 還有 net 等, 因為它們需要和操作繫統密切配閤的, 但是普通的程序一般是不需要的.</p>
|
||||
<p>本章描述的方法不應該輕易使用. 如果沒有處理好細節, 它們可能導致各種不可預測的隱晦的錯誤, 甚至連本地的C程序員也無法理解. 使用 unsafe 包同時也無法保證與未來版本的兼容性, 因爲在有意無意中會使用很多實現的細節, 而這些實現的細節在未來很可能會改變.</p>
|
||||
<p>unsafe 包的實現比較特殊. 雖然它可以和普通包一樣的導入和使用, 但它實際上是由編譯器實現的. 它提供了一些訪問語言內部特性的方法, 特别是內存布局相關的細節.
|
||||
將這些特别封裝到一個獨立的包中, 是爲在極少數情況下需要使用的時候, 引起人們的註意(它們是不安全的). 此外, 有一些環境因爲安全的因素可能限製這個包的使用.</p>
|
||||
<p>unsafe 包被廣泛地用於比較低級的包, 例如 runtime, os, syscall 還有 net 等, 因爲它們需要和操作繫統密切配合的, 但是普通的程序一般是不需要的.</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
100
ch2/ch2-01.html
100
ch2/ch2-01.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="2.1" data-chapter-title="命名" data-filepath="ch2/ch2-01.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="2.1" data-chapter-title="命名" data-filepath="ch2/ch2-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,7 +2024,7 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="21-命名">2.1. 命名</h2>
|
||||
<p>Go語言中的的函數名, 變量名, 常量名, 類型名, 語句段標簽名, 和 包名 等所有的命名, 都遵循一個命名規則: 一個名字必鬚以一個字母(Unicode字母)或下劃綫開頭, 後麫可以跟任意數量的字母,數字或下劃綫. 不衕大小寫字母是不衕的: <code>heapSort</code> 和 <code>Heapsort</code> 是兩個不衕的名字.</p>
|
||||
<p>Go語言中的的函數名, 變量名, 常量名, 類型名, 語句段標籤名, 和 包名 等所有的命名, 都遵循一個命名規則: 一個名字必鬚以一個字母(Unicode字母)或下劃線開頭, 後面可以跟任意數量的字母,數字或下劃線. 不同大小寫字母是不同的: <code>heapSort</code> 和 <code>Heapsort</code> 是兩個不同的名字.</p>
|
||||
<p>Go語言類似 <code>if</code> 和 <code>switch</code> 的關鍵字有25個; 關鍵字不能用於自定義名字, 隻能在特定語法中使用.</p>
|
||||
<pre><code>break default func interface select
|
||||
case defer go map struct
|
||||
@@ -2079,9 +2043,9 @@ Functions: make len cap new append copy close delete
|
||||
complex real imag
|
||||
panic recover
|
||||
</code></pre><p>這些內部預先定義的名字不是關鍵字, 你可以在定義中重現使用它們. 在一些特殊的場景重新定義是有意義的, 但是也要註意避免引起混亂.</p>
|
||||
<p>如果一個實體是在函數內部定義, 那麼它的就隻在函數內部有效. 如果是在函數外部定義, 那麼將在當前包的所有文件中都可以訪問. 名字的開頭字母的大小寫決定了名字在包外的可見性. 如果一個名字是大寫字母開頭的, 那麼它將是導齣的, 也就是可以被外部的包訪問, 例如 <code>fmt</code> 包的 <code>Printf</code> 函數就是導齣的, 可以在 <code>fmt</code> 包外部訪問. 包本身的名字一般總是用小寫字母.</p>
|
||||
<p>名字的長度沒有限製, 但是Go的風格是盡量使用短小的名字, 對於侷部變量尤其是這樣; 你會經常看到 <code>i</code> 之類的名字, 而是冗長的 <code>theLoopIndex</code>. 通常來說, 如果一個名字的作用域比較大, 生命週期較長, 那麼用長的名字將更有意義.</p>
|
||||
<p>在習慣上, Go程序員推薦使用<code>駝峯式</code>命名, 當名字有幾個單詞的時優先使用大小寫分隔, 而不是優先用下劃綫分隔. 因此, 標準庫有 <code>QuoteRuneToASCII</code> 和 <code>parseRequestLine</code> 這樣的函數命名, 但是不會用 <code>quote_rune_to_ASCII</code> 和 <code>parse_request_line</code> 這樣的命名. 像 <code>ASCII</code> 和 <code>HTML</code> 這樣的縮略詞避免使用大小寫混閤, 它們可能被稱為 <code>htmlEscape</code>, <code>HTMLEscape</code> 或 <code>escapeHTML</code>, 但不會是 <code>escapeHtml</code>.</p>
|
||||
<p>如果一個實體是在函數內部定義, 那麽它的就隻在函數內部有效. 如果是在函數外部定義, 那麽將在當前包的所有文件中都可以訪問. 名字的開頭字母的大小寫決定了名字在包外的可見性. 如果一個名字是大寫字母開頭的, 那麽它將是導齣的, 也就是可以被外部的包訪問, 例如 <code>fmt</code> 包的 <code>Printf</code> 函數就是導齣的, 可以在 <code>fmt</code> 包外部訪問. 包本身的名字一般總是用小寫字母.</p>
|
||||
<p>名字的長度沒有限製, 但是Go的風格是盡量使用短小的名字, 對於局部變量尤其是這樣; 你會經常看到 <code>i</code> 之類的名字, 而是冗長的 <code>theLoopIndex</code>. 通常來説, 如果一個名字的作用域比較大, 生命週期較長, 那麽用長的名字將更有意義.</p>
|
||||
<p>在習慣上, Go程序員推薦使用<code>駝峯式</code>命名, 當名字有幾個單詞的時優先使用大小寫分隔, 而不是優先用下劃線分隔. 因此, 標準庫有 <code>QuoteRuneToASCII</code> 和 <code>parseRequestLine</code> 這樣的函數命名, 但是不會用 <code>quote_rune_to_ASCII</code> 和 <code>parse_request_line</code> 這樣的命名. 像 <code>ASCII</code> 和 <code>HTML</code> 這樣的縮略詞避免使用大小寫混合, 它們可能被稱爲 <code>htmlEscape</code>, <code>HTMLEscape</code> 或 <code>escapeHTML</code>, 但不會是 <code>escapeHtml</code>.</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
104
ch2/ch2-02.html
104
ch2/ch2-02.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="2.2" data-chapter-title="聲明" data-filepath="ch2/ch2-02.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="2.2" data-chapter-title="聲明" data-filepath="ch2/ch2-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,9 +2024,9 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="22-聲明">2.2. 聲明</h2>
|
||||
<p>聲明定義了程序的入口以及部分或全部的屬性. Go主要有四種聲明類型: var, const, type, 和 func, 分彆對應 變量, 常量, 類型, 和 函數的 聲明. 這一章我們重點討論變量和類型的聲明, 第三章將討論常量的聲明, 第五章將討論函數的聲明.</p>
|
||||
<p>一個Go程序存儲在一個或多個以<code>.go</code>為後綴名的文件中. 每個文件以個包的聲明開始, 以說明文件是屬於包的一部分.
|
||||
包聲明之後是 import 導入聲明, 然後是包一級的類型/變量/常量/函數的聲明, 聲明的順序無關緊要. 例如, 下麫的例子聲明了一個常量, 一個函數和兩個變量:</p>
|
||||
<p>聲明定義了程序的入口以及部分或全部的屬性. Go主要有四種聲明類型: var, const, type, 和 func, 分别對應 變量, 常量, 類型, 和 函數的 聲明. 這一章我們重點討論變量和類型的聲明, 第三章將討論常量的聲明, 第五章將討論函數的聲明.</p>
|
||||
<p>一個Go程序存儲在一個或多個以<code>.go</code>爲後綴名的文件中. 每個文件以個包的聲明開始, 以説明文件是屬於包的一部分.
|
||||
包聲明之後是 import 導入聲明, 然後是包一級的類型/變量/常量/函數的聲明, 聲明的順序無關緊要. 例如, 下面的例子聲明了一個常量, 一個函數和兩個變量:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch2/boiling
|
||||
<span class="hljs-comment">// Boiling prints the boiling point of water.</span>
|
||||
<span class="hljs-keyword">package</span> main
|
||||
@@ -2079,9 +2043,9 @@
|
||||
<span class="hljs-comment">// boiling point = 212°F or 100°C</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>其中 常量 <code>boilingF</code> 是在包一級聲明的, 然後 <code>f</code> 和 <code>c</code> 是在 main 函數內部聲明的. 在包一級聲明的名字可在整個包訪問, 而不僅僅在其聲明的文件中訪問. 相比之下, 侷部聲明的名字就隻能在函數內部很小的部分可訪問.</p>
|
||||
<p>一個函數的聲明有一個函數名字, 參數列錶(由函數的調用者提供參數變量的具體值), 一個可選的返迴值列錶, 和包含函數語句定義的函數體. 如果函數沒有返迴值, 那麼返迴值列錶是省略的. 執行函數從函數的第一個語句開始, 但是順序執行直到遇到 renturn 返迴語言, 如果沒有返迴語句則是到函數末尾, 然後返迴到調用者.</p>
|
||||
<p>我們已經看到過很多函數的例子了, 在第五章將深入討論函數的細節, 這裏隻粗略說下. 下麫的 <code>fToC</code> 函數封裝了溫度轉換的邏輯, 這樣它隻需要定義一次, 就可以在多個地方多次使用. 這個例子中, main 函數就調用了兩次 <code>fToC</code> 函數, 分彆是使用侷部定義的兩個常量作為函數參數.</p>
|
||||
<p>其中 常量 <code>boilingF</code> 是在包一級聲明的, 然後 <code>f</code> 和 <code>c</code> 是在 main 函數內部聲明的. 在包一級聲明的名字可在整個包訪問, 而不僅僅在其聲明的文件中訪問. 相比之下, 局部聲明的名字就隻能在函數內部很小的部分可訪問.</p>
|
||||
<p>一個函數的聲明有一個函數名字, 參數列表(由函數的調用者提供參數變量的具體值), 一個可選的返迴值列表, 和包含函數語句定義的函數體. 如果函數沒有返迴值, 那麽返迴值列表是省略的. 執行函數從函數的第一個語句開始, 但是順序執行直到遇到 renturn 返迴語言, 如果沒有返迴語句則是到函數末尾, 然後返迴到調用者.</p>
|
||||
<p>我們已經看到過很多函數的例子了, 在第五章將深入討論函數的細節, 這里隻粗略説下. 下面的 <code>fToC</code> 函數封裝了溫度轉換的邏輯, 這樣它隻需要定義一次, 就可以在多個地方多次使用. 這個例子中, main 函數就調用了兩次 <code>fToC</code> 函數, 分别是使用局部定義的兩個常量作爲函數參數.</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch2/ftoc
|
||||
<span class="hljs-comment">// Ftoc prints two Fahrenheit-to-Celsius conversions.</span>
|
||||
<span class="hljs-keyword">package</span> main
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
### 2.3.1. 簡短變量聲明
|
||||
|
||||
在函數內部, 有一種稱為簡短變量聲明的形式可用於聲明和初始化侷部變量. 以 `名字 := 錶達式` 方式聲明變量, 變量的類型根據錶達式來推導. 這裏函數中是三個簡短變量聲明語句(§1.4):
|
||||
在函數內部, 有一種稱爲簡短變量聲明的形式可用於聲明和初始化局部變量. 以 `名字 := 表達式` 方式聲明變量, 變量的類型根據表達式來推導. 這里函數中是三個簡短變量聲明語句(§1.4):
|
||||
|
||||
```Go
|
||||
anim := gif.GIF{LoopCount: nframes}
|
||||
@@ -8,7 +8,7 @@ freq := rand.Float64() * 3.0
|
||||
t := 0.0
|
||||
```
|
||||
|
||||
因為簡潔和靈活性, 簡短變量聲明用於大部分的侷部變量的聲明和初始化. var 方式的聲明往往是用於需要顯示指定類型的侷部變量, 或者因為稍後會被賦值而初始值無關緊要的變量.
|
||||
因爲簡潔和靈活性, 簡短變量聲明用於大部分的局部變量的聲明和初始化. var 方式的聲明往往是用於需要顯示指定類型的局部變量, 或者因爲稍後會被賦值而初始值無關緊要的變量.
|
||||
|
||||
|
||||
```Go
|
||||
@@ -27,7 +27,7 @@ i, j := 0, 1
|
||||
|
||||
但是這種聲明多個變量的方式隻簡易在可以提高代碼可讀性的地方使用, 比如 for 循環的初始化部分.
|
||||
|
||||
請記住 `:=` 是一個變量聲明, 而 `=` 是一個賦值操作. 不要混淆多個變量的聲明和元組的多重(§2.4.1), 後者是將右邊的錶達式值賦給左邊對應位置的變量:
|
||||
請記住 `:=` 是一個變量聲明, 而 `=` 是一個賦值操作. 不要混淆多個變量的聲明和元組的多重(§2.4.1), 後者是將右邊的表達式值賦給左邊對應位置的變量:
|
||||
|
||||
```Go
|
||||
i, j = j, i // 交換 i 和 j 的值
|
||||
@@ -44,9 +44,9 @@ if err != nil {
|
||||
f.Close()
|
||||
```
|
||||
|
||||
這裏有一個比較微妙的地方: 簡短變量聲明左邊的全部變量可能併不是全部都是剛剛聲明的. 如果有一些已經在相衕的詞法塊聲明過了(§2.7), 那麼簡短變量聲明對這些已經聲明過的變量就隻有賦值行為了.
|
||||
這里有一個比較微妙的地方: 簡短變量聲明左邊的全部變量可能併不是全部都是剛剛聲明的. 如果有一些已經在相同的詞法塊聲明過了(§2.7), 那麽簡短變量聲明對這些已經聲明過的變量就隻有賦值行爲了.
|
||||
|
||||
在下麫的代碼中, 第一個語句聲明了 in 和 err 變量. 第二個語句隻聲明了 out, 然後對已經聲明的 err 進行賦值.
|
||||
在下面的代碼中, 第一個語句聲明了 in 和 err 變量. 第二個語句隻聲明了 out, 然後對已經聲明的 err 進行賦值.
|
||||
|
||||
```Go
|
||||
in, err := os.Open(infile)
|
||||
@@ -64,6 +64,6 @@ f, err := os.Create(outfile) // compile error: no new variables
|
||||
|
||||
解決的方法是第二個語句改用普通的賦值語言.
|
||||
|
||||
簡短變量聲明隻有對在變量已經在衕級詞法域聲明過的變量纔和賦值操作等衕, 如果變量是在外部詞法域聲明了, 那麼將會聲明一個新變量. 我們在本章後麫將會看到類似的例子.
|
||||
簡短變量聲明隻有對在變量已經在同級詞法域聲明過的變量纔和賦值操作等同, 如果變量是在外部詞法域聲明了, 那麽將會聲明一個新變量. 我們在本章後面將會看到類似的例子.
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
### 2.3.2 指鍼
|
||||
### 2.3.2 指針
|
||||
|
||||
一個變量對應一個保存了一個值的內存空間. 變量在聲明語句創建時綁定一個名字, 比如 x, 但是還有很多變量始終以錶達式方式引入, 例如 x[i] 或 x.f. 所有這些錶達式都讀取一個變量的值, 除非它們是齣現在賦值語句的左邊, 這種時候是給變量賦予一個新值.
|
||||
一個變量對應一個保存了一個值的內存空間. 變量在聲明語句創建時綁定一個名字, 比如 x, 但是還有很多變量始終以表達式方式引入, 例如 x[i] 或 x.f. 所有這些表達式都讀取一個變量的值, 除非它們是齣現在賦值語句的左邊, 這種時候是給變量賦予一個新值.
|
||||
|
||||
一個指鍼的值是一個變量的地址. 一個指鍼對應變量在內存中的存儲位置. 併不是每一個值都會有一個地址, 但是對於每一個變量必然有對應的地址. 通過指鍼, 我們可以直接讀或更新變量的值, 而不需要知道變量的名字(卽使變量有名字的話).
|
||||
一個指針的值是一個變量的地址. 一個指針對應變量在內存中的存儲位置. 併不是每一個值都會有一個地址, 但是對於每一個變量必然有對應的地址. 通過指針, 我們可以直接讀或更新變量的值, 而不需要知道變量的名字(卽使變量有名字的話).
|
||||
|
||||
如果這樣聲明一個變量 `var x int`, 那麼 `&x` 錶達式(x的地址)將產生一個指曏整數變量的指鍼, 對應的數據類型是 `*int`, 稱之為 "指曏 int 的指鍼". 如果指鍼名字為 p, 那麼可以說 "p 指鍼指曏 x", 或者說 "p 指鍼保存了 x 變量的地址". `*p` 對應 p 指鍼指曏的變量的值. `*p` 錶達式讀取變量的值, 為 int 類型, 衕時因為 `*p` 對應一個變量, 所以可以齣現在賦值語句的左邊, 用於更新所指曏的變量的值.
|
||||
如果這樣聲明一個變量 `var x int`, 那麽 `&x` 表達式(x的地址)將産生一個指向整數變量的指針, 對應的數據類型是 `*int`, 稱之爲 "指向 int 的指針". 如果指針名字爲 p, 那麽可以説 "p 指針指向 x", 或者説 "p 指針保存了 x 變量的地址". `*p` 對應 p 指針指向的變量的值. `*p` 表達式讀取變量的值, 爲 int 類型, 同時因爲 `*p` 對應一個變量, 所以可以齣現在賦值語句的左邊, 用於更新所指向的變量的值.
|
||||
|
||||
```Go
|
||||
x := 1
|
||||
@@ -14,18 +14,18 @@ fmt.Println(*p) // "1"
|
||||
fmt.Println(x) // "2"
|
||||
```
|
||||
|
||||
對於聚閤類型, 比如結構體的每個字段, 或者是數組的每個元素, 也都是對應一個變量, 併且可以被穫取地址.
|
||||
對於聚合類型, 比如結構體的每個字段, 或者是數組的每個元素, 也都是對應一個變量, 併且可以被穫取地址.
|
||||
|
||||
變量有時候被稱為可尋址的值. 如果變量由錶達式臨時生成, 那麼錶達式必鬚能接受 `&` 取地址操作.
|
||||
變量有時候被稱爲可尋址的值. 如果變量由表達式臨時生成, 那麽表達式必鬚能接受 `&` 取地址操作.
|
||||
|
||||
任何類型的指鍼的零值都是 nil. 如果 `p != nil` 測試為眞, 那麼 p 是指曏變量. 指鍼直接也是可以進行相等測試的, 隻有當它們指曏衕一個變量或全部是 nil 時纔相等.
|
||||
任何類型的指針的零值都是 nil. 如果 `p != nil` 測試爲眞, 那麽 p 是指向變量. 指針直接也是可以進行相等測試的, 隻有當它們指向同一個變量或全部是 nil 時纔相等.
|
||||
|
||||
```Go
|
||||
var x, y int
|
||||
fmt.Println(&x == &x, &x == &y, &x == nil) // "true false false"
|
||||
```
|
||||
|
||||
在Go語言中, 返迴函數中侷部變量的地址是安全的. 例如下麫的代碼, 調用 f 函數時創建 v 侷部變量, 在地址被返迴之後依然有效, 因為指鍼 p 依然引用這個變量.
|
||||
在Go語言中, 返迴函數中局部變量的地址是安全的. 例如下面的代碼, 調用 f 函數時創建 v 局部變量, 在地址被返迴之後依然有效, 因爲指針 p 依然引用這個變量.
|
||||
|
||||
```Go
|
||||
var p = f()
|
||||
@@ -36,13 +36,13 @@ func f() *int {
|
||||
}
|
||||
```
|
||||
|
||||
每次調用 f 函數都將返迴不衕的結果:
|
||||
每次調用 f 函數都將返迴不同的結果:
|
||||
|
||||
```Go
|
||||
fmt.Println(f() == f()) // "false"
|
||||
```
|
||||
|
||||
因為指鍼包含了一個變量的地址, 因此將指鍼作為參數調用函數, 將可以在函數中通過指鍼更新變量的值. 例如這個通過指鍼來更新變量的值, 然後返迴更新後的值, 可用在一個錶達式中:
|
||||
因爲指針包含了一個變量的地址, 因此將指針作爲參數調用函數, 將可以在函數中通過指針更新變量的值. 例如這個通過指針來更新變量的值, 然後返迴更新後的值, 可用在一個表達式中:
|
||||
|
||||
```Go
|
||||
func incr(p *int) int {
|
||||
@@ -55,9 +55,9 @@ incr(&v) // side effect: v is now 2
|
||||
fmt.Println(incr(&v)) // "3" (and v is 3)
|
||||
```
|
||||
|
||||
每次我們對變量取地址, 或者復製指鍼, 我們都創建了變量的新的彆名. 例如, *p 是 變量 v 的彆名. 指鍼特彆有加載的地方在於我們可以不用名字而訪問一個變量, 但是這是一把雙刃劍: 要找到一個變量的所有訪問者, 我們必鬚知道變量全部的彆名. 不僅僅是指鍼創建彆名, 很多其他引用類型也會創建彆名, 例如 切片, 字典和管道, 甚至結構體, 數組和接口都會創建所引用變量的彆名.
|
||||
每次我們對變量取地址, 或者複製指針, 我們都創建了變量的新的别名. 例如, *p 是 變量 v 的别名. 指針特别有加載的地方在於我們可以不用名字而訪問一個變量, 但是這是一把雙刃劍: 要找到一個變量的所有訪問者, 我們必鬚知道變量全部的别名. 不僅僅是指針創建别名, 很多其他引用類型也會創建别名, 例如 切片, 字典和管道, 甚至結構體, 數組和接口都會創建所引用變量的别名.
|
||||
|
||||
指鍼是 flag 包的關鍵, 它使用命令行參數來設置對應的變量, 而這些分佈在整個程序中. 為了說明這一點, 在早些的echo版本中, 包含了兩個可選的命令行參數: `-n` 用於忽略行尾的換行符, `-s sep` 用於指定分隔字符(默認是空格). 這是第四個版本, 對應包 gopl.io/ch2/echo4.
|
||||
指針是 flag 包的關鍵, 它使用命令行參數來設置對應的變量, 而這些分布在整個程序中. 爲了説明這一點, 在早些的echo版本中, 包含了兩個可選的命令行參數: `-n` 用於忽略行尾的換行符, `-s sep` 用於指定分隔字符(默認是空格). 這是第四個版本, 對應包 gopl.io/ch2/echo4.
|
||||
|
||||
```Go
|
||||
gopl.io/ch2/echo4
|
||||
@@ -82,7 +82,7 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
`flag.Bool` 函數調用創建了一個新的佈爾型標誌參數變量. 它有三個屬性: 第一個是的名字"n", 然後是標誌的默認值(這裏是false), 最後是對應的描述信息. 如果用戶輸入了無效的標誌參數, 或者輸入 `-h` 或 `-help` 標誌參數, 將打印標誌參數的名字, 默認值和描述信息. 類似的, flag.String 用於創建一個字符串類型的標誌參數變量, 衕樣包含參數名, 默認值, 和描述信息. 變量 `sep` 和 `n` 是一個指曏標誌參數變量的指鍼, 因此必鬚用 *sep 和 *n 的方式間接引用.
|
||||
`flag.Bool` 函數調用創建了一個新的布爾型標誌參數變量. 它有三個屬性: 第一個是的名字"n", 然後是標誌的默認值(這里是false), 最後是對應的描述信息. 如果用戶輸入了無效的標誌參數, 或者輸入 `-h` 或 `-help` 標誌參數, 將打印標誌參數的名字, 默認值和描述信息. 類似的, flag.String 用於創建一個字符串類型的標誌參數變量, 同樣包含參數名, 默認值, 和描述信息. 變量 `sep` 和 `n` 是一個指向標誌參數變量的指針, 因此必鬚用 *sep 和 *n 的方式間接引用.
|
||||
|
||||
|
||||
當程序運行時, 必鬚在標誌參數變量使用之前調用 flag.Parse 函數更新標誌參數變量的值(之前是默認值). 非標誌參數的普通類型參數可以用 flag.Args() 訪問, 對應一個 字符串切片. 如果 flag.Parse 解析遇到錯誤, 將打印提示信息, 然後調用 os.Exit(2) 終止程序.
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
### 2.3.3 new 函數
|
||||
|
||||
|
||||
另一個創建變量的方法是用內建的 new 函數. 錶達式 `new(T)` 創建一個T類型的匿名變量, 初始化為T類型的零值, 返迴返迴變量地址, 返迴指鍼類型為 `*T`.
|
||||
另一個創建變量的方法是用內建的 new 函數. 表達式 `new(T)` 創建一個T類型的匿名變量, 初始化爲T類型的零值, 返迴返迴變量地址, 返迴指針類型爲 `*T`.
|
||||
|
||||
```Go
|
||||
p := new(int) // p, *int 類型, 指曏匿名的 int 變量
|
||||
p := new(int) // p, *int 類型, 指向匿名的 int 變量
|
||||
fmt.Println(*p) // "0"
|
||||
*p = 2 // 設置 int 匿名變量的值為 2
|
||||
*p = 2 // 設置 int 匿名變量的值爲 2
|
||||
fmt.Println(*p) // "2"
|
||||
```
|
||||
|
||||
|
||||
從 new 創建變量和普通聲明方式創建變量沒有什麼區彆, 除了不需要聲明一個臨時變量的名字外, 我們還可以在錶達式中使用 `new(T)`. 換言之, new 類似是一種語法醣, 而不是一個新的基礎概唸.
|
||||
從 new 創建變量和普通聲明方式創建變量沒有什麽區别, 除了不需要聲明一個臨時變量的名字外, 我們還可以在表達式中使用 `new(T)`. 換言之, new 類似是一種語法醣, 而不是一個新的基礎概念.
|
||||
|
||||
下麫的兩個 newInt 函數有着相衕的行為:
|
||||
下面的兩個 newInt 函數有着相同的行爲:
|
||||
|
||||
```Go
|
||||
func newInt() *int { func newInt() *int {
|
||||
@@ -22,7 +22,7 @@ func newInt() *int { func newInt() *int {
|
||||
}
|
||||
```
|
||||
|
||||
每次調用 new 都是返迴一個新的變量的地址, 因此下麫兩個地址是不衕的:
|
||||
每次調用 new 都是返迴一個新的變量的地址, 因此下面兩個地址是不同的:
|
||||
|
||||
```Go
|
||||
p := new(int)
|
||||
@@ -30,15 +30,15 @@ q := new(int)
|
||||
fmt.Println(p == q) // "false"
|
||||
```
|
||||
|
||||
當然也有特殊情況: 如果兩個類型都是空的, 也就是說類型的大小是0, 例如 `struct{}` 和 `[0]int`, 有可能有相衕的地址(依賴具體的語言實現).
|
||||
當然也有特殊情況: 如果兩個類型都是空的, 也就是説類型的大小是0, 例如 `struct{}` 和 `[0]int`, 有可能有相同的地址(依賴具體的語言實現).
|
||||
|
||||
new 函數使用相對比較少, 因為對應結構體來說, 可以直接用字麫量語法創建新變量的方法更靈活 (§4.4.1).
|
||||
new 函數使用相對比較少, 因爲對應結構體來説, 可以直接用字面量語法創建新變量的方法更靈活 (§4.4.1).
|
||||
|
||||
由於 new 隻是一個預定義的函數, 它併不是一個關鍵字, 因此我們可以將 new 重新定義為彆的類型. 例如:
|
||||
由於 new 隻是一個預定義的函數, 它併不是一個關鍵字, 因此我們可以將 new 重新定義爲别的類型. 例如:
|
||||
|
||||
```Go
|
||||
func delta(old, new int) int { return new - old }
|
||||
```
|
||||
|
||||
因為 new 被定義為 int 類型的變量, 因此 delta 函數內部就無法在使用內置的 new 函數了.
|
||||
因爲 new 被定義爲 int 類型的變量, 因此 delta 函數內部就無法在使用內置的 new 函數了.
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
### 2.3.4. 變量的生命週期
|
||||
|
||||
變量的生命週期指的是程序運行期間變量存在的有效時間間隔. 包級聲明的變量的生命週期和程序的生命週期是一緻的. 相比之下, 侷部變量的聲明週期是動態的: 從每次創建一個新變量的聲明語句被執行開始, 直到變量不在被引用為止, 然後變量的存儲空間可能被迴收. 函數的參數變量和返迴值變量都是侷部變量. 它們在函數每次被調用的時候創建.
|
||||
變量的生命週期指的是程序運行期間變量存在的有效時間間隔. 包級聲明的變量的生命週期和程序的生命週期是一致的. 相比之下, 局部變量的聲明週期是動態的: 從每次創建一個新變量的聲明語句被執行開始, 直到變量不在被引用爲止, 然後變量的存儲空間可能被迴收. 函數的參數變量和返迴值變量都是局部變量. 它們在函數每次被調用的時候創建.
|
||||
|
||||
例如, 下麫是從 1.4 節的 Lissajous 程序摘彔的代碼片段:
|
||||
例如, 下面是從 1.4 節的 Lissajous 程序摘録的代碼片段:
|
||||
|
||||
```Go
|
||||
for t := 0.0; t < cycles*2*math.Pi; t += res {
|
||||
@@ -15,11 +15,11 @@ for t := 0.0; t < cycles*2*math.Pi; t += res {
|
||||
|
||||
在每次循環的開始創建變量 t, 然後在每次循環迭代中創建 x 和 y.
|
||||
|
||||
那麼垃圾收集器是如何知道一個變量是何時可以被迴收的呢? 這裏我們先避開完整的技朮細節, 但是基本的思路是, 從每個包級的變量和每個當前運行函數的每一個侷部變量開始, 通過指鍼或引用的路徑, 是否可以找到該變量. 如果不存在這樣的路徑, 那麼說明該變量是不可達的, 也就是說它併不會影響其餘的計算.
|
||||
那麽垃圾收集器是如何知道一個變量是何時可以被迴收的呢? 這里我們先避開完整的技術細節, 但是基本的思路是, 從每個包級的變量和每個當前運行函數的每一個局部變量開始, 通過指針或引用的路徑, 是否可以找到該變量. 如果不存在這樣的路徑, 那麽説明該變量是不可達的, 也就是説它併不會影響其餘的計算.
|
||||
|
||||
因為一個變量的聲明週期隻取決於是否可達, 因此一個循環迭代內部的侷部變量的生命週期可能超齣其侷部作用域. 它可能在函數返迴之後依然存在.
|
||||
因爲一個變量的聲明週期隻取決於是否可達, 因此一個循環迭代內部的局部變量的生命週期可能超齣其局部作用域. 它可能在函數返迴之後依然存在.
|
||||
|
||||
編譯器會選擇在棧上還是在堆上分配侷部變量的存儲空間, 但可能令人驚訝的是, 這個選擇併不是由 var 或 new 來決定的.
|
||||
編譯器會選擇在棧上還是在堆上分配局部變量的存儲空間, 但可能令人驚訝的是, 這個選擇併不是由 var 或 new 來決定的.
|
||||
|
||||
```Go
|
||||
var global *int
|
||||
@@ -31,10 +31,10 @@ func f() { func g() {
|
||||
}
|
||||
```
|
||||
|
||||
這裏的 x 必鬚在堆上分配, 因為它在函數退齣後依然可以通過包的 global 變量找到, 雖然它是在函數內部定義的; 我們說這個 x 侷部變量從 函數 f 中逃逸了. 相反, 當 g 函數返迴時, 變量 `*y` 將是不可達的, 也就是可以被迴收的. 因此, `*y` 併沒有從 函數 g 逃逸, 編譯器可以選擇在棧上分配 `*y` 的存儲空間, 雖然這裏用的是 new 方式.
|
||||
在任何時候, 你併不需為了編寫正確的代碼而要考慮變量的逃逸行為, 要記住的是, 逃逸的變量需要額外分配內存, 衕時對性能的優化會產生一定的影響.
|
||||
這里的 x 必鬚在堆上分配, 因爲它在函數退齣後依然可以通過包的 global 變量找到, 雖然它是在函數內部定義的; 我們説這個 x 局部變量從 函數 f 中逃逸了. 相反, 當 g 函數返迴時, 變量 `*y` 將是不可達的, 也就是可以被迴收的. 因此, `*y` 併沒有從 函數 g 逃逸, 編譯器可以選擇在棧上分配 `*y` 的存儲空間, 雖然這里用的是 new 方式.
|
||||
在任何時候, 你併不需爲了編寫正確的代碼而要考慮變量的逃逸行爲, 要記住的是, 逃逸的變量需要額外分配內存, 同時對性能的優化會産生一定的影響.
|
||||
|
||||
垃圾收集器對編寫正確的代碼是一個鉅大的幫助, 但併不是說你完全不用考慮內存了. 你雖然不需要顯式地分配和釋放內存, 但是要編寫高效的程序你還是需要知道變量的生命週期. 例如, 將指曏短生命週期對象的指鍼保存到具有長生命週期的對象中, 特彆是全侷變量時, 會阻止對短生命週期對象的垃圾迴收.
|
||||
垃圾收集器對編寫正確的代碼是一個鉅大的幫助, 但併不是説你完全不用考慮內存了. 你雖然不需要顯式地分配和釋放內存, 但是要編寫高效的程序你還是需要知道變量的生命週期. 例如, 將指向短生命週期對象的指針保存到具有長生命週期的對象中, 特别是全局變量時, 會阻止對短生命週期對象的垃圾迴收.
|
||||
|
||||
|
||||
|
||||
|
||||
180
ch2/ch2-03.html
180
ch2/ch2-03.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="2.3" data-chapter-title="變量" data-filepath="ch2/ch2-03.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="2.3" data-chapter-title="變量" data-filepath="ch2/ch2-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2061,30 +2025,30 @@
|
||||
|
||||
<h2 id="23-變量">2.3. 變量</h2>
|
||||
<p>var 聲明可以創建一個特定類型的變量, 然後給變量附加一個名字, 併且設置變量的初始值. 變量聲明的一般語法:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> name <span class="hljs-keyword">type</span> = 錶達式
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> name <span class="hljs-keyword">type</span> = 表達式
|
||||
</code></pre>
|
||||
<p>其中類型或 <code>= 錶達式</code> 可以省略其中的一個. 如果省略的是類型信息, 那麼將根據初始化錶達式類推導類型信息. 如果初始化錶達式被省略, 那麼將用零值初始化變量. 數值類型變量的零值是0, 佈爾類型變量的零值是 false, 字符串的零值是空字符串, 接口或引用類型(包括 切片, 字典, 通道 和 函數)的變量的零值是 nil. 數組或結構體等聚閤類型的零值是每個元素或字段都是零值.</p>
|
||||
<p>零值機製可以確保每個聲明的變量總是有一個良好定義的值, 在 Go 中不存在未初始化的變量. 這個可以簡化很多代碼, 在沒有增加額外工作的前提下確保邊界條件下的閤理行為. 例如:</p>
|
||||
<p>其中類型或 <code>= 表達式</code> 可以省略其中的一個. 如果省略的是類型信息, 那麽將根據初始化表達式類推導類型信息. 如果初始化表達式被省略, 那麽將用零值初始化變量. 數值類型變量的零值是0, 布爾類型變量的零值是 false, 字符串的零值是空字符串, 接口或引用類型(包括 切片, 字典, 通道 和 函數)的變量的零值是 nil. 數組或結構體等聚合類型的零值是每個元素或字段都是零值.</p>
|
||||
<p>零值機製可以確保每個聲明的變量總是有一個良好定義的值, 在 Go 中不存在未初始化的變量. 這個可以簡化很多代碼, 在沒有增加額外工作的前提下確保邊界條件下的合理行爲. 例如:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> s <span class="hljs-typename">string</span>
|
||||
fmt.Println(s) <span class="hljs-comment">// ""</span>
|
||||
</code></pre>
|
||||
<p>這段代碼將打印一個空字符串, 而不是導緻錯誤或產生不可預知的行為. Go 程序員經常讓一些聚閤類型的零值也有意義, 這樣不管任何類型的變量總是有一個閤理的零值狀態.</p>
|
||||
<p>可以在一個聲明語句中衕時聲明一組變量, 或用一組初始化錶達式聲明併初始化一組變量.
|
||||
如果省略每個變量的類型, 將可以聲明多個不衕類型的變量(類型由初始化錶達式推導):</p>
|
||||
<p>這段代碼將打印一個空字符串, 而不是導致錯誤或産生不可預知的行爲. Go 程序員經常讓一些聚合類型的零值也有意義, 這樣不管任何類型的變量總是有一個合理的零值狀態.</p>
|
||||
<p>可以在一個聲明語句中同時聲明一組變量, 或用一組初始化表達式聲明併初始化一組變量.
|
||||
如果省略每個變量的類型, 將可以聲明多個不同類型的變量(類型由初始化表達式推導):</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> i, j, k <span class="hljs-typename">int</span> <span class="hljs-comment">// int, int, int</span>
|
||||
<span class="hljs-keyword">var</span> b, f, s = <span class="hljs-constant">true</span>, <span class="hljs-number">2.3</span>, <span class="hljs-string">"four"</span> <span class="hljs-comment">// bool, float64, string</span>
|
||||
</code></pre>
|
||||
<p>初始化可以是字麫量或任意的錶達式. 包級彆聲明的變量會在 main 函數執行前完成初始化 (§2.6.2), 侷部變量將在聲明語句被執行到的時候初始化.</p>
|
||||
<p>初始化可以是字面量或任意的表達式. 包級别聲明的變量會在 main 函數執行前完成初始化 (§2.6.2), 局部變量將在聲明語句被執行到的時候初始化.</p>
|
||||
<p>一組變量的初始化也可以通過調用一個函數, 由函數返迴的多個返迴值初始化:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> f, err = os.Open(name) <span class="hljs-comment">// os.Open returns a file and an error</span>
|
||||
</code></pre>
|
||||
<h3 id="231-簡短變量聲明">2.3.1. 簡短變量聲明</h3>
|
||||
<p>在函數內部, 有一種稱為簡短變量聲明的形式可用於聲明和初始化侷部變量. 以 <code>名字 := 錶達式</code> 方式聲明變量, 變量的類型根據錶達式來推導. 這裏函數中是三個簡短變量聲明語句(§1.4):</p>
|
||||
<p>在函數內部, 有一種稱爲簡短變量聲明的形式可用於聲明和初始化局部變量. 以 <code>名字 := 表達式</code> 方式聲明變量, 變量的類型根據表達式來推導. 這里函數中是三個簡短變量聲明語句(§1.4):</p>
|
||||
<pre><code class="lang-Go">anim := gif.GIF{LoopCount: nframes}
|
||||
freq := rand.Float64() * <span class="hljs-number">3.0</span>
|
||||
t := <span class="hljs-number">0.0</span>
|
||||
</code></pre>
|
||||
<p>因為簡潔和靈活性, 簡短變量聲明用於大部分的侷部變量的聲明和初始化. var 方式的聲明往往是用於需要顯示指定類型的侷部變量, 或者因為稍後會被賦值而初始值無關緊要的變量.</p>
|
||||
<p>因爲簡潔和靈活性, 簡短變量聲明用於大部分的局部變量的聲明和初始化. var 方式的聲明往往是用於需要顯示指定類型的局部變量, 或者因爲稍後會被賦值而初始值無關緊要的變量.</p>
|
||||
<pre><code class="lang-Go">i := <span class="hljs-number">100</span> <span class="hljs-comment">// an int</span>
|
||||
<span class="hljs-keyword">var</span> boiling <span class="hljs-typename">float64</span> = <span class="hljs-number">100</span> <span class="hljs-comment">// a float64</span>
|
||||
<span class="hljs-keyword">var</span> names []<span class="hljs-typename">string</span>
|
||||
@@ -2095,7 +2059,7 @@ t := <span class="hljs-number">0.0</span>
|
||||
<pre><code class="lang-Go">i, j := <span class="hljs-number">0</span>, <span class="hljs-number">1</span>
|
||||
</code></pre>
|
||||
<p>但是這種聲明多個變量的方式隻簡易在可以提高代碼可讀性的地方使用, 比如 for 循環的初始化部分.</p>
|
||||
<p>請記住 <code>:=</code> 是一個變量聲明, 而 <code>=</code> 是一個賦值操作. 不要混淆多個變量的聲明和元組的多重(§2.4.1), 後者是將右邊的錶達式值賦給左邊對應位置的變量:</p>
|
||||
<p>請記住 <code>:=</code> 是一個變量聲明, 而 <code>=</code> 是一個賦值操作. 不要混淆多個變量的聲明和元組的多重(§2.4.1), 後者是將右邊的表達式值賦給左邊對應位置的變量:</p>
|
||||
<pre><code class="lang-Go">i, j = j, i <span class="hljs-comment">// 交換 i 和 j 的值</span>
|
||||
</code></pre>
|
||||
<p>和普通 var 變量聲明一樣, 簡短變量聲明也可以用調用函數的返迴值來聲明, 像 os.Open 函數返迴兩個值:</p>
|
||||
@@ -2106,8 +2070,8 @@ t := <span class="hljs-number">0.0</span>
|
||||
<span class="hljs-comment">// ...use f...</span>
|
||||
f.Close()
|
||||
</code></pre>
|
||||
<p>這裏有一個比較微妙的地方: 簡短變量聲明左邊的全部變量可能併不是全部都是剛剛聲明的. 如果有一些已經在相衕的詞法塊聲明過了(§2.7), 那麼簡短變量聲明對這些已經聲明過的變量就隻有賦值行為了.</p>
|
||||
<p>在下麫的代碼中, 第一個語句聲明了 in 和 err 變量. 第二個語句隻聲明了 out, 然後對已經聲明的 err 進行賦值.</p>
|
||||
<p>這里有一個比較微妙的地方: 簡短變量聲明左邊的全部變量可能併不是全部都是剛剛聲明的. 如果有一些已經在相同的詞法塊聲明過了(§2.7), 那麽簡短變量聲明對這些已經聲明過的變量就隻有賦值行爲了.</p>
|
||||
<p>在下面的代碼中, 第一個語句聲明了 in 和 err 變量. 第二個語句隻聲明了 out, 然後對已經聲明的 err 進行賦值.</p>
|
||||
<pre><code class="lang-Go">in, err := os.Open(infile)
|
||||
<span class="hljs-comment">// ...</span>
|
||||
out, err := os.Create(outfile)
|
||||
@@ -2118,24 +2082,24 @@ out, err := os.Create(outfile)
|
||||
f, err := os.Create(outfile) <span class="hljs-comment">// compile error: no new variables</span>
|
||||
</code></pre>
|
||||
<p>解決的方法是第二個語句改用普通的賦值語言.</p>
|
||||
<p>簡短變量聲明隻有對在變量已經在衕級詞法域聲明過的變量纔和賦值操作等衕, 如果變量是在外部詞法域聲明了, 那麼將會聲明一個新變量. 我們在本章後麫將會看到類似的例子.</p>
|
||||
<h3 id="232-指鍼">2.3.2 指鍼</h3>
|
||||
<p>一個變量對應一個保存了一個值的內存空間. 變量在聲明語句創建時綁定一個名字, 比如 x, 但是還有很多變量始終以錶達式方式引入, 例如 x[i] 或 x.f. 所有這些錶達式都讀取一個變量的值, 除非它們是齣現在賦值語句的左邊, 這種時候是給變量賦予一個新值.</p>
|
||||
<p>一個指鍼的值是一個變量的地址. 一個指鍼對應變量在內存中的存儲位置. 併不是每一個值都會有一個地址, 但是對於每一個變量必然有對應的地址. 通過指鍼, 我們可以直接讀或更新變量的值, 而不需要知道變量的名字(卽使變量有名字的話).</p>
|
||||
<p>如果這樣聲明一個變量 <code>var x int</code>, 那麼 <code>&x</code> 錶達式(x的地址)將產生一個指曏整數變量的指鍼, 對應的數據類型是 <code>*int</code>, 稱之為 "指曏 int 的指鍼". 如果指鍼名字為 p, 那麼可以說 "p 指鍼指曏 x", 或者說 "p 指鍼保存了 x 變量的地址". <code>*p</code> 對應 p 指鍼指曏的變量的值. <code>*p</code> 錶達式讀取變量的值, 為 int 類型, 衕時因為 <code>*p</code> 對應一個變量, 所以可以齣現在賦值語句的左邊, 用於更新所指曏的變量的值.</p>
|
||||
<p>簡短變量聲明隻有對在變量已經在同級詞法域聲明過的變量纔和賦值操作等同, 如果變量是在外部詞法域聲明了, 那麽將會聲明一個新變量. 我們在本章後面將會看到類似的例子.</p>
|
||||
<h3 id="232-指針">2.3.2 指針</h3>
|
||||
<p>一個變量對應一個保存了一個值的內存空間. 變量在聲明語句創建時綁定一個名字, 比如 x, 但是還有很多變量始終以表達式方式引入, 例如 x[i] 或 x.f. 所有這些表達式都讀取一個變量的值, 除非它們是齣現在賦值語句的左邊, 這種時候是給變量賦予一個新值.</p>
|
||||
<p>一個指針的值是一個變量的地址. 一個指針對應變量在內存中的存儲位置. 併不是每一個值都會有一個地址, 但是對於每一個變量必然有對應的地址. 通過指針, 我們可以直接讀或更新變量的值, 而不需要知道變量的名字(卽使變量有名字的話).</p>
|
||||
<p>如果這樣聲明一個變量 <code>var x int</code>, 那麽 <code>&x</code> 表達式(x的地址)將産生一個指向整數變量的指針, 對應的數據類型是 <code>*int</code>, 稱之爲 "指向 int 的指針". 如果指針名字爲 p, 那麽可以説 "p 指針指向 x", 或者説 "p 指針保存了 x 變量的地址". <code>*p</code> 對應 p 指針指向的變量的值. <code>*p</code> 表達式讀取變量的值, 爲 int 類型, 同時因爲 <code>*p</code> 對應一個變量, 所以可以齣現在賦值語句的左邊, 用於更新所指向的變量的值.</p>
|
||||
<pre><code class="lang-Go">x := <span class="hljs-number">1</span>
|
||||
p := &x <span class="hljs-comment">// p, of type *int, points to x</span>
|
||||
fmt.Println(*p) <span class="hljs-comment">// "1"</span>
|
||||
*p = <span class="hljs-number">2</span> <span class="hljs-comment">// equivalent to x = 2</span>
|
||||
fmt.Println(x) <span class="hljs-comment">// "2"</span>
|
||||
</code></pre>
|
||||
<p>對於聚閤類型, 比如結構體的每個字段, 或者是數組的每個元素, 也都是對應一個變量, 併且可以被穫取地址.</p>
|
||||
<p>變量有時候被稱為可尋址的值. 如果變量由錶達式臨時生成, 那麼錶達式必鬚能接受 <code>&</code> 取地址操作.</p>
|
||||
<p>任何類型的指鍼的零值都是 nil. 如果 <code>p != nil</code> 測試為眞, 那麼 p 是指曏變量. 指鍼直接也是可以進行相等測試的, 隻有當它們指曏衕一個變量或全部是 nil 時纔相等.</p>
|
||||
<p>對於聚合類型, 比如結構體的每個字段, 或者是數組的每個元素, 也都是對應一個變量, 併且可以被穫取地址.</p>
|
||||
<p>變量有時候被稱爲可尋址的值. 如果變量由表達式臨時生成, 那麽表達式必鬚能接受 <code>&</code> 取地址操作.</p>
|
||||
<p>任何類型的指針的零值都是 nil. 如果 <code>p != nil</code> 測試爲眞, 那麽 p 是指向變量. 指針直接也是可以進行相等測試的, 隻有當它們指向同一個變量或全部是 nil 時纔相等.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> x, y <span class="hljs-typename">int</span>
|
||||
fmt.Println(&x == &x, &x == &y, &x == <span class="hljs-constant">nil</span>) <span class="hljs-comment">// "true false false"</span>
|
||||
</code></pre>
|
||||
<p>在Go語言中, 返迴函數中侷部變量的地址是安全的. 例如下麫的代碼, 調用 f 函數時創建 v 侷部變量, 在地址被返迴之後依然有效, 因為指鍼 p 依然引用這個變量.</p>
|
||||
<p>在Go語言中, 返迴函數中局部變量的地址是安全的. 例如下面的代碼, 調用 f 函數時創建 v 局部變量, 在地址被返迴之後依然有效, 因爲指針 p 依然引用這個變量.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> p = f()
|
||||
|
||||
<span class="hljs-keyword">func</span> f() *<span class="hljs-typename">int</span> {
|
||||
@@ -2143,10 +2107,10 @@ fmt.Println(&x == &x, &x == &y, &x == <span class="hljs-cons
|
||||
<span class="hljs-keyword">return</span> &v
|
||||
}
|
||||
</code></pre>
|
||||
<p>每次調用 f 函數都將返迴不衕的結果:</p>
|
||||
<p>每次調用 f 函數都將返迴不同的結果:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(f() == f()) <span class="hljs-comment">// "false"</span>
|
||||
</code></pre>
|
||||
<p>因為指鍼包含了一個變量的地址, 因此將指鍼作為參數調用函數, 將可以在函數中通過指鍼更新變量的值. 例如這個通過指鍼來更新變量的值, 然後返迴更新後的值, 可用在一個錶達式中:</p>
|
||||
<p>因爲指針包含了一個變量的地址, 因此將指針作爲參數調用函數, 將可以在函數中通過指針更新變量的值. 例如這個通過指針來更新變量的值, 然後返迴更新後的值, 可用在一個表達式中:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> incr(p *<span class="hljs-typename">int</span>) <span class="hljs-typename">int</span> {
|
||||
*p++ <span class="hljs-comment">// increments what p points to; does not change p</span>
|
||||
<span class="hljs-keyword">return</span> *p
|
||||
@@ -2156,8 +2120,8 @@ v := <span class="hljs-number">1</span>
|
||||
incr(&v) <span class="hljs-comment">// side effect: v is now 2</span>
|
||||
fmt.Println(incr(&v)) <span class="hljs-comment">// "3" (and v is 3)</span>
|
||||
</code></pre>
|
||||
<p>每次我們對變量取地址, 或者復製指鍼, 我們都創建了變量的新的彆名. 例如, *p 是 變量 v 的彆名. 指鍼特彆有加載的地方在於我們可以不用名字而訪問一個變量, 但是這是一把雙刃劍: 要找到一個變量的所有訪問者, 我們必鬚知道變量全部的彆名. 不僅僅是指鍼創建彆名, 很多其他引用類型也會創建彆名, 例如 切片, 字典和管道, 甚至結構體, 數組和接口都會創建所引用變量的彆名.</p>
|
||||
<p>指鍼是 flag 包的關鍵, 它使用命令行參數來設置對應的變量, 而這些分佈在整個程序中. 為了說明這一點, 在早些的echo版本中, 包含了兩個可選的命令行參數: <code>-n</code> 用於忽略行尾的換行符, <code>-s sep</code> 用於指定分隔字符(默認是空格). 這是第四個版本, 對應包 gopl.io/ch2/echo4.</p>
|
||||
<p>每次我們對變量取地址, 或者複製指針, 我們都創建了變量的新的别名. 例如, *p 是 變量 v 的别名. 指針特别有加載的地方在於我們可以不用名字而訪問一個變量, 但是這是一把雙刃劍: 要找到一個變量的所有訪問者, 我們必鬚知道變量全部的别名. 不僅僅是指針創建别名, 很多其他引用類型也會創建别名, 例如 切片, 字典和管道, 甚至結構體, 數組和接口都會創建所引用變量的别名.</p>
|
||||
<p>指針是 flag 包的關鍵, 它使用命令行參數來設置對應的變量, 而這些分布在整個程序中. 爲了説明這一點, 在早些的echo版本中, 包含了兩個可選的命令行參數: <code>-n</code> 用於忽略行尾的換行符, <code>-s sep</code> 用於指定分隔字符(默認是空格). 這是第四個版本, 對應包 gopl.io/ch2/echo4.</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch2/echo4
|
||||
<span class="hljs-comment">// Echo4 prints its command-line arguments.</span>
|
||||
<span class="hljs-keyword">package</span> main
|
||||
@@ -2179,7 +2143,7 @@ fmt.Println(incr(&v)) <span class="hljs-comment">// "3" (and v is
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><code>flag.Bool</code> 函數調用創建了一個新的佈爾型標誌參數變量. 它有三個屬性: 第一個是的名字"n", 然後是標誌的默認值(這裏是false), 最後是對應的描述信息. 如果用戶輸入了無效的標誌參數, 或者輸入 <code>-h</code> 或 <code>-help</code> 標誌參數, 將打印標誌參數的名字, 默認值和描述信息. 類似的, flag.String 用於創建一個字符串類型的標誌參數變量, 衕樣包含參數名, 默認值, 和描述信息. 變量 <code>sep</code> 和 <code>n</code> 是一個指曏標誌參數變量的指鍼, 因此必鬚用 <em>sep 和 </em>n 的方式間接引用.</p>
|
||||
<p><code>flag.Bool</code> 函數調用創建了一個新的布爾型標誌參數變量. 它有三個屬性: 第一個是的名字"n", 然後是標誌的默認值(這里是false), 最後是對應的描述信息. 如果用戶輸入了無效的標誌參數, 或者輸入 <code>-h</code> 或 <code>-help</code> 標誌參數, 將打印標誌參數的名字, 默認值和描述信息. 類似的, flag.String 用於創建一個字符串類型的標誌參數變量, 同樣包含參數名, 默認值, 和描述信息. 變量 <code>sep</code> 和 <code>n</code> 是一個指向標誌參數變量的指針, 因此必鬚用 <em>sep 和 </em>n 的方式間接引用.</p>
|
||||
<p>當程序運行時, 必鬚在標誌參數變量使用之前調用 flag.Parse 函數更新標誌參數變量的值(之前是默認值). 非標誌參數的普通類型參數可以用 flag.Args() 訪問, 對應一個 字符串切片. 如果 flag.Parse 解析遇到錯誤, 將打印提示信息, 然後調用 os.Exit(2) 終止程序.</p>
|
||||
<p>讓我們運行一些 echo 測試用例:</p>
|
||||
<pre><code>$ go build gopl.io/ch2/echo4
|
||||
@@ -2195,33 +2159,33 @@ Usage of ./echo4:
|
||||
-s string
|
||||
separator (default " ")
|
||||
</code></pre><h3 id="233-new-函數">2.3.3 new 函數</h3>
|
||||
<p>另一個創建變量的方法是用內建的 new 函數. 錶達式 <code>new(T)</code> 創建一個T類型的匿名變量, 初始化為T類型的零值, 返迴返迴變量地址, 返迴指鍼類型為 <code>*T</code>.</p>
|
||||
<pre><code class="lang-Go">p := <span class="hljs-built_in">new</span>(<span class="hljs-typename">int</span>) <span class="hljs-comment">// p, *int 類型, 指曏匿名的 int 變量</span>
|
||||
<p>另一個創建變量的方法是用內建的 new 函數. 表達式 <code>new(T)</code> 創建一個T類型的匿名變量, 初始化爲T類型的零值, 返迴返迴變量地址, 返迴指針類型爲 <code>*T</code>.</p>
|
||||
<pre><code class="lang-Go">p := <span class="hljs-built_in">new</span>(<span class="hljs-typename">int</span>) <span class="hljs-comment">// p, *int 類型, 指向匿名的 int 變量</span>
|
||||
fmt.Println(*p) <span class="hljs-comment">// "0"</span>
|
||||
*p = <span class="hljs-number">2</span> <span class="hljs-comment">// 設置 int 匿名變量的值為 2</span>
|
||||
*p = <span class="hljs-number">2</span> <span class="hljs-comment">// 設置 int 匿名變量的值爲 2</span>
|
||||
fmt.Println(*p) <span class="hljs-comment">// "2"</span>
|
||||
</code></pre>
|
||||
<p>從 new 創建變量和普通聲明方式創建變量沒有什麼區彆, 除了不需要聲明一個臨時變量的名字外, 我們還可以在錶達式中使用 <code>new(T)</code>. 換言之, new 類似是一種語法醣, 而不是一個新的基礎概唸.</p>
|
||||
<p>下麫的兩個 newInt 函數有着相衕的行為:</p>
|
||||
<p>從 new 創建變量和普通聲明方式創建變量沒有什麽區别, 除了不需要聲明一個臨時變量的名字外, 我們還可以在表達式中使用 <code>new(T)</code>. 換言之, new 類似是一種語法醣, 而不是一個新的基礎概念.</p>
|
||||
<p>下面的兩個 newInt 函數有着相同的行爲:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> newInt() *<span class="hljs-typename">int</span> { <span class="hljs-keyword">func</span> newInt() *<span class="hljs-typename">int</span> {
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-built_in">new</span>(<span class="hljs-typename">int</span>) <span class="hljs-keyword">var</span> dummy <span class="hljs-typename">int</span>
|
||||
} <span class="hljs-keyword">return</span> &dummy
|
||||
}
|
||||
</code></pre>
|
||||
<p>每次調用 new 都是返迴一個新的變量的地址, 因此下麫兩個地址是不衕的:</p>
|
||||
<p>每次調用 new 都是返迴一個新的變量的地址, 因此下面兩個地址是不同的:</p>
|
||||
<pre><code class="lang-Go">p := <span class="hljs-built_in">new</span>(<span class="hljs-typename">int</span>)
|
||||
q := <span class="hljs-built_in">new</span>(<span class="hljs-typename">int</span>)
|
||||
fmt.Println(p == q) <span class="hljs-comment">// "false"</span>
|
||||
</code></pre>
|
||||
<p>當然也有特殊情況: 如果兩個類型都是空的, 也就是說類型的大小是0, 例如 <code>struct{}</code> 和 <code>[0]int</code>, 有可能有相衕的地址(依賴具體的語言實現).</p>
|
||||
<p>new 函數使用相對比較少, 因為對應結構體來說, 可以直接用字麫量語法創建新變量的方法更靈活 (§4.4.1).</p>
|
||||
<p>由於 new 隻是一個預定義的函數, 它併不是一個關鍵字, 因此我們可以將 new 重新定義為彆的類型. 例如:</p>
|
||||
<p>當然也有特殊情況: 如果兩個類型都是空的, 也就是説類型的大小是0, 例如 <code>struct{}</code> 和 <code>[0]int</code>, 有可能有相同的地址(依賴具體的語言實現).</p>
|
||||
<p>new 函數使用相對比較少, 因爲對應結構體來説, 可以直接用字面量語法創建新變量的方法更靈活 (§4.4.1).</p>
|
||||
<p>由於 new 隻是一個預定義的函數, 它併不是一個關鍵字, 因此我們可以將 new 重新定義爲别的類型. 例如:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> delta(old, <span class="hljs-built_in">new</span> <span class="hljs-typename">int</span>) <span class="hljs-typename">int</span> { <span class="hljs-keyword">return</span> <span class="hljs-built_in">new</span> - old }
|
||||
</code></pre>
|
||||
<p>因為 new 被定義為 int 類型的變量, 因此 delta 函數內部就無法在使用內置的 new 函數了.</p>
|
||||
<p>因爲 new 被定義爲 int 類型的變量, 因此 delta 函數內部就無法在使用內置的 new 函數了.</p>
|
||||
<h3 id="234-變量的生命週期">2.3.4. 變量的生命週期</h3>
|
||||
<p>變量的生命週期指的是程序運行期間變量存在的有效時間間隔. 包級聲明的變量的生命週期和程序的生命週期是一緻的. 相比之下, 侷部變量的聲明週期是動態的: 從每次創建一個新變量的聲明語句被執行開始, 直到變量不在被引用為止, 然後變量的存儲空間可能被迴收. 函數的參數變量和返迴值變量都是侷部變量. 它們在函數每次被調用的時候創建.</p>
|
||||
<p>例如, 下麫是從 1.4 節的 Lissajous 程序摘彔的代碼片段:</p>
|
||||
<p>變量的生命週期指的是程序運行期間變量存在的有效時間間隔. 包級聲明的變量的生命週期和程序的生命週期是一致的. 相比之下, 局部變量的聲明週期是動態的: 從每次創建一個新變量的聲明語句被執行開始, 直到變量不在被引用爲止, 然後變量的存儲空間可能被迴收. 函數的參數變量和返迴值變量都是局部變量. 它們在函數每次被調用的時候創建.</p>
|
||||
<p>例如, 下面是從 1.4 節的 Lissajous 程序摘録的代碼片段:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">for</span> t := <span class="hljs-number">0.0</span>; t < cycles*<span class="hljs-number">2</span>*math.Pi; t += res {
|
||||
x := math.Sin(t)
|
||||
y := math.Sin(t*freq + phase)
|
||||
@@ -2230,9 +2194,9 @@ fmt.Println(p == q) <span class="hljs-comment">// "false"</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>在每次循環的開始創建變量 t, 然後在每次循環迭代中創建 x 和 y.</p>
|
||||
<p>那麼垃圾收集器是如何知道一個變量是何時可以被迴收的呢? 這裏我們先避開完整的技朮細節, 但是基本的思路是, 從每個包級的變量和每個當前運行函數的每一個侷部變量開始, 通過指鍼或引用的路徑, 是否可以找到該變量. 如果不存在這樣的路徑, 那麼說明該變量是不可達的, 也就是說它併不會影響其餘的計算.</p>
|
||||
<p>因為一個變量的聲明週期隻取決於是否可達, 因此一個循環迭代內部的侷部變量的生命週期可能超齣其侷部作用域. 它可能在函數返迴之後依然存在.</p>
|
||||
<p>編譯器會選擇在棧上還是在堆上分配侷部變量的存儲空間, 但可能令人驚訝的是, 這個選擇併不是由 var 或 new 來決定的.</p>
|
||||
<p>那麽垃圾收集器是如何知道一個變量是何時可以被迴收的呢? 這里我們先避開完整的技術細節, 但是基本的思路是, 從每個包級的變量和每個當前運行函數的每一個局部變量開始, 通過指針或引用的路徑, 是否可以找到該變量. 如果不存在這樣的路徑, 那麽説明該變量是不可達的, 也就是説它併不會影響其餘的計算.</p>
|
||||
<p>因爲一個變量的聲明週期隻取決於是否可達, 因此一個循環迭代內部的局部變量的生命週期可能超齣其局部作用域. 它可能在函數返迴之後依然存在.</p>
|
||||
<p>編譯器會選擇在棧上還是在堆上分配局部變量的存儲空間, 但可能令人驚訝的是, 這個選擇併不是由 var 或 new 來決定的.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> global *<span class="hljs-typename">int</span>
|
||||
|
||||
<span class="hljs-keyword">func</span> f() { <span class="hljs-keyword">func</span> g() {
|
||||
@@ -2241,9 +2205,9 @@ fmt.Println(p == q) <span class="hljs-comment">// "false"</span>
|
||||
global = &x }
|
||||
}
|
||||
</code></pre>
|
||||
<p>這裏的 x 必鬚在堆上分配, 因為它在函數退齣後依然可以通過包的 global 變量找到, 雖然它是在函數內部定義的; 我們說這個 x 侷部變量從 函數 f 中逃逸了. 相反, 當 g 函數返迴時, 變量 <code>*y</code> 將是不可達的, 也就是可以被迴收的. 因此, <code>*y</code> 併沒有從 函數 g 逃逸, 編譯器可以選擇在棧上分配 <code>*y</code> 的存儲空間, 雖然這裏用的是 new 方式.
|
||||
在任何時候, 你併不需為了編寫正確的代碼而要考慮變量的逃逸行為, 要記住的是, 逃逸的變量需要額外分配內存, 衕時對性能的優化會產生一定的影響.</p>
|
||||
<p>垃圾收集器對編寫正確的代碼是一個鉅大的幫助, 但併不是說你完全不用考慮內存了. 你雖然不需要顯式地分配和釋放內存, 但是要編寫高效的程序你還是需要知道變量的生命週期. 例如, 將指曏短生命週期對象的指鍼保存到具有長生命週期的對象中, 特彆是全侷變量時, 會阻止對短生命週期對象的垃圾迴收.</p>
|
||||
<p>這里的 x 必鬚在堆上分配, 因爲它在函數退齣後依然可以通過包的 global 變量找到, 雖然它是在函數內部定義的; 我們説這個 x 局部變量從 函數 f 中逃逸了. 相反, 當 g 函數返迴時, 變量 <code>*y</code> 將是不可達的, 也就是可以被迴收的. 因此, <code>*y</code> 併沒有從 函數 g 逃逸, 編譯器可以選擇在棧上分配 <code>*y</code> 的存儲空間, 雖然這里用的是 new 方式.
|
||||
在任何時候, 你併不需爲了編寫正確的代碼而要考慮變量的逃逸行爲, 要記住的是, 逃逸的變量需要額外分配內存, 同時對性能的優化會産生一定的影響.</p>
|
||||
<p>垃圾收集器對編寫正確的代碼是一個鉅大的幫助, 但併不是説你完全不用考慮內存了. 你雖然不需要顯式地分配和釋放內存, 但是要編寫高效的程序你還是需要知道變量的生命週期. 例如, 將指向短生命週期對象的指針保存到具有長生命週期的對象中, 特别是全局變量時, 會阻止對短生命週期對象的垃圾迴收.</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
### 2.4.1. 元組賦值
|
||||
|
||||
元組賦值是另一種形式的賦值語句, 允許衕時更新多個變量的值. 在賦值之前, 賦值語句右邊的所有錶達式將會先進行求值, 然後再統一更新左邊變量的值. 這對於處理有些衕時齣現在元組賦值語句左右兩邊的變量很有幫助, 例如我們可以這樣交換兩個變量的值:
|
||||
元組賦值是另一種形式的賦值語句, 允許同時更新多個變量的值. 在賦值之前, 賦值語句右邊的所有表達式將會先進行求值, 然後再統一更新左邊變量的值. 這對於處理有些同時齣現在元組賦值語句左右兩邊的變量很有幫助, 例如我們可以這樣交換兩個變量的值:
|
||||
|
||||
```Go
|
||||
x, y = y, x
|
||||
@@ -31,22 +31,22 @@ func fib(n int) int {
|
||||
}
|
||||
```
|
||||
|
||||
元組賦值也可以使一繫列瑣碎賦值更緊湊(譯註: 特彆是在for循環的初始化部分),
|
||||
元組賦值也可以使一繫列瑣碎賦值更緊湊(譯註: 特别是在for循環的初始化部分),
|
||||
|
||||
```Go
|
||||
i, j, k = 2, 3, 5
|
||||
```
|
||||
|
||||
但如果錶達式太復雜的話, 應該盡量避免元組賦值; 因為一個個單獨的賦值語句的可讀性會更好.
|
||||
但如果表達式太複雜的話, 應該盡量避免元組賦值; 因爲一個個單獨的賦值語句的可讀性會更好.
|
||||
|
||||
某些錶達式會產生多個值, 比如調用一個有多個返迴值的函數.
|
||||
當這樣一個函數調用齣現在元組賦值右邊的錶達式中時(譯註: 右邊不能再有其他錶達式), 左邊變量的數目必鬚和右邊一緻.
|
||||
某些表達式會産生多個值, 比如調用一個有多個返迴值的函數.
|
||||
當這樣一個函數調用齣現在元組賦值右邊的表達式中時(譯註: 右邊不能再有其他表達式), 左邊變量的數目必鬚和右邊一致.
|
||||
|
||||
```Go
|
||||
f, err = os.Open("foo.txt") // function call returns two values
|
||||
```
|
||||
|
||||
通常, 這類函數會用額外的返迴值錶達某種錯誤類型, 例如 os.Open 是返迴一個 error 類型的錯誤, 還有一些是返迴佈爾值, 通常被稱為ok. 在稍後我們看到的三個操作都是類似的行為. 如果 字典査找(§4.3), 類型斷言(§7.10), 或 通道接收(§8.4.2) 齣現在賦值語句的右邊, 它們都將產生兩個結果, 有一個額外的佈爾結果錶示操作是否成功:
|
||||
通常, 這類函數會用額外的返迴值表達某種錯誤類型, 例如 os.Open 是返迴一個 error 類型的錯誤, 還有一些是返迴布爾值, 通常被稱爲ok. 在稍後我們看到的三個操作都是類似的行爲. 如果 字典査找(§4.3), 類型斷言(§7.10), 或 通道接收(§8.4.2) 齣現在賦值語句的右邊, 它們都將産生兩個結果, 有一個額外的布爾結果表示操作是否成功:
|
||||
|
||||
```Go
|
||||
v, ok = m[key] // map lookup
|
||||
@@ -54,7 +54,7 @@ v, ok = x.(T) // type assertion
|
||||
v, ok = <-ch // channel receive
|
||||
```
|
||||
|
||||
和變量的聲明一樣, 我們可以用下劃綫空白標識符 `_` 來丟棄不需要的值.
|
||||
和變量的聲明一樣, 我們可以用下劃線空白標識符 `_` 來丟棄不需要的值.
|
||||
|
||||
```Go
|
||||
_, err = io.Copy(dst, src) // 丟棄字節數
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
### 2.4.2. 可賦值性
|
||||
|
||||
賦值語句是顯示的賦值形式, 但是程序中還有很多地方會髮送隱式的賦值行為: 函數調用將隱式地將調用參數的值賦值給函數的參數變量, 一個返迴語句將隱式地將返迴操作的值賦值給結果變量, 一個復閤類型的字麫量(§4.2)也會產生賦值行為. 例如下麫的語句:
|
||||
賦值語句是顯示的賦值形式, 但是程序中還有很多地方會發送隱式的賦值行爲: 函數調用將隱式地將調用參數的值賦值給函數的參數變量, 一個返迴語句將隱式地將返迴操作的值賦值給結果變量, 一個複合類型的字面量(§4.2)也會産生賦值行爲. 例如下面的語句:
|
||||
|
||||
```Go
|
||||
medals := []string{"gold", "silver", "bronze"}
|
||||
```
|
||||
|
||||
隱式地對切片的每個元素進行賦值操作, 類似這樣寫的行為:
|
||||
隱式地對切片的每個元素進行賦值操作, 類似這樣寫的行爲:
|
||||
|
||||
```Go
|
||||
medals[0] = "gold"
|
||||
@@ -14,15 +14,15 @@ medals[1] = "silver"
|
||||
medals[2] = "bronze"
|
||||
```
|
||||
|
||||
字典和管道的元素, 雖然不是普通的變量, 但是也有類似的隱式賦值行為.
|
||||
字典和管道的元素, 雖然不是普通的變量, 但是也有類似的隱式賦值行爲.
|
||||
|
||||
不管是隱式還是顯示地賦值, 在賦值語句坐標的變量和右邊最終的求到的值必鬚有相衕的數據類型. 更直白地說, 隻有右邊的值對於左邊的變量是可賦值的, 賦值語句纔是允許的.
|
||||
不管是隱式還是顯示地賦值, 在賦值語句坐標的變量和右邊最終的求到的值必鬚有相同的數據類型. 更直白地説, 隻有右邊的值對於左邊的變量是可賦值的, 賦值語句纔是允許的.
|
||||
|
||||
可賦值性的規則對於不衕類型有不衕要求, 對每個新類型有關的地方我們會專門解釋.
|
||||
對於目前我們已經討論過的類型, 它的規則是簡單的: 類型必鬚完全匹配, nil 可以賦值給任何指鍼或引用類型的變量. 常量(§3.6)有更靈活的規則, 這樣可以避免不必要的顯示類型轉換.
|
||||
可賦值性的規則對於不同類型有不同要求, 對每個新類型有關的地方我們會專門解釋.
|
||||
對於目前我們已經討論過的類型, 它的規則是簡單的: 類型必鬚完全匹配, nil 可以賦值給任何指針或引用類型的變量. 常量(§3.6)有更靈活的規則, 這樣可以避免不必要的顯示類型轉換.
|
||||
|
||||
對於兩個值是否可以用 `==` 或 `!=` 進行相等比較的能力也和可賦值能力有關繫:
|
||||
對於任何的比較, 第一個操作必鬚是可用於第二個操作類型的變量的賦值的, 反之依然.
|
||||
和前麫一樣, 我們會對每個新類型比較有關的地方會做專門解釋.
|
||||
和前面一樣, 我們會對每個新類型比較有關的地方會做專門解釋.
|
||||
|
||||
|
||||
|
||||
128
ch2/ch2-04.html
128
ch2/ch2-04.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="2.4" data-chapter-title="賦值" data-filepath="ch2/ch2-04.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="2.4" data-chapter-title="賦值" data-filepath="ch2/ch2-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,23 +2024,23 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="24-賦值">2.4. 賦值</h2>
|
||||
<p>使用賦值語句可以更新一個變量的值, 最簡單的賦值語句是將要被賦值的變量放在 <code>=</code> 的左邊, 新值的錶達式放在 <code>=</code> 右邊.</p>
|
||||
<p>使用賦值語句可以更新一個變量的值, 最簡單的賦值語句是將要被賦值的變量放在 <code>=</code> 的左邊, 新值的表達式放在 <code>=</code> 右邊.</p>
|
||||
<pre><code class="lang-Go">x = <span class="hljs-number">1</span> <span class="hljs-comment">// 命令變量的賦值</span>
|
||||
*p = <span class="hljs-constant">true</span> <span class="hljs-comment">// 通過指鍼間接賦值</span>
|
||||
*p = <span class="hljs-constant">true</span> <span class="hljs-comment">// 通過指針間接賦值</span>
|
||||
person.name = <span class="hljs-string">"bob"</span> <span class="hljs-comment">// 結構體字段賦值</span>
|
||||
count[x] = count[x] * scale <span class="hljs-comment">// 數組, 切片 或 字典的 元素賦值</span>
|
||||
</code></pre>
|
||||
<p>特定的賦值語句和二元算朮復閤操作有一個簡潔形式, 例如上麫最後的語句可以重寫為:</p>
|
||||
<p>特定的賦值語句和二元算術複合操作有一個簡潔形式, 例如上面最後的語句可以重寫爲:</p>
|
||||
<pre><code class="lang-Go">count[x] *= scale
|
||||
</code></pre>
|
||||
<p>這樣可以省去對變量錶達式的重復計算.</p>
|
||||
<p>這樣可以省去對變量表達式的重複計算.</p>
|
||||
<p>數值變量也可以支持 <code>++</code> 遞增和 <code>--</code> 遞減語句:</p>
|
||||
<pre><code class="lang-Go">v := <span class="hljs-number">1</span>
|
||||
v++ <span class="hljs-comment">// 等價方式 v = v + 1; v 變成 2 </span>
|
||||
v-- <span class="hljs-comment">// 等價方式 v = v - 1; v 變成 1</span>
|
||||
</code></pre>
|
||||
<h3 id="241-元組賦值">2.4.1. 元組賦值</h3>
|
||||
<p>元組賦值是另一種形式的賦值語句, 允許衕時更新多個變量的值. 在賦值之前, 賦值語句右邊的所有錶達式將會先進行求值, 然後再統一更新左邊變量的值. 這對於處理有些衕時齣現在元組賦值語句左右兩邊的變量很有幫助, 例如我們可以這樣交換兩個變量的值:</p>
|
||||
<p>元組賦值是另一種形式的賦值語句, 允許同時更新多個變量的值. 在賦值之前, 賦值語句右邊的所有表達式將會先進行求值, 然後再統一更新左邊變量的值. 這對於處理有些同時齣現在元組賦值語句左右兩邊的變量很有幫助, 例如我們可以這樣交換兩個變量的值:</p>
|
||||
<pre><code class="lang-Go">x, y = y, x
|
||||
|
||||
a[i], a[j] = a[j], a[i]
|
||||
@@ -2098,39 +2062,39 @@ a[i], a[j] = a[j], a[i]
|
||||
<span class="hljs-keyword">return</span> x
|
||||
}
|
||||
</code></pre>
|
||||
<p>元組賦值也可以使一繫列瑣碎賦值更緊湊(譯註: 特彆是在for循環的初始化部分),</p>
|
||||
<p>元組賦值也可以使一繫列瑣碎賦值更緊湊(譯註: 特别是在for循環的初始化部分),</p>
|
||||
<pre><code class="lang-Go">i, j, k = <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>
|
||||
</code></pre>
|
||||
<p>但如果錶達式太復雜的話, 應該盡量避免元組賦值; 因為一個個單獨的賦值語句的可讀性會更好.</p>
|
||||
<p>某些錶達式會產生多個值, 比如調用一個有多個返迴值的函數.
|
||||
當這樣一個函數調用齣現在元組賦值右邊的錶達式中時(譯註: 右邊不能再有其他錶達式), 左邊變量的數目必鬚和右邊一緻.</p>
|
||||
<p>但如果表達式太複雜的話, 應該盡量避免元組賦值; 因爲一個個單獨的賦值語句的可讀性會更好.</p>
|
||||
<p>某些表達式會産生多個值, 比如調用一個有多個返迴值的函數.
|
||||
當這樣一個函數調用齣現在元組賦值右邊的表達式中時(譯註: 右邊不能再有其他表達式), 左邊變量的數目必鬚和右邊一致.</p>
|
||||
<pre><code class="lang-Go">f, err = os.Open(<span class="hljs-string">"foo.txt"</span>) <span class="hljs-comment">// function call returns two values</span>
|
||||
</code></pre>
|
||||
<p>通常, 這類函數會用額外的返迴值錶達某種錯誤類型, 例如 os.Open 是返迴一個 error 類型的錯誤, 還有一些是返迴佈爾值, 通常被稱為ok. 在稍後我們看到的三個操作都是類似的行為. 如果 字典査找(§4.3), 類型斷言(§7.10), 或 通道接收(§8.4.2) 齣現在賦值語句的右邊, 它們都將產生兩個結果, 有一個額外的佈爾結果錶示操作是否成功:</p>
|
||||
<p>通常, 這類函數會用額外的返迴值表達某種錯誤類型, 例如 os.Open 是返迴一個 error 類型的錯誤, 還有一些是返迴布爾值, 通常被稱爲ok. 在稍後我們看到的三個操作都是類似的行爲. 如果 字典査找(§4.3), 類型斷言(§7.10), 或 通道接收(§8.4.2) 齣現在賦值語句的右邊, 它們都將産生兩個結果, 有一個額外的布爾結果表示操作是否成功:</p>
|
||||
<pre><code class="lang-Go">v, ok = m[key] <span class="hljs-comment">// map lookup</span>
|
||||
v, ok = x.(T) <span class="hljs-comment">// type assertion</span>
|
||||
v, ok = <-ch <span class="hljs-comment">// channel receive</span>
|
||||
</code></pre>
|
||||
<p>和變量的聲明一樣, 我們可以用下劃綫空白標識符 <code>_</code> 來丟棄不需要的值.</p>
|
||||
<p>和變量的聲明一樣, 我們可以用下劃線空白標識符 <code>_</code> 來丟棄不需要的值.</p>
|
||||
<pre><code class="lang-Go">_, err = io.Copy(dst, src) <span class="hljs-comment">// 丟棄字節數</span>
|
||||
_, ok = x.(T) <span class="hljs-comment">// 隻檢測類型, 忽略具體值</span>
|
||||
</code></pre>
|
||||
<h3 id="242-可賦值性">2.4.2. 可賦值性</h3>
|
||||
<p>賦值語句是顯示的賦值形式, 但是程序中還有很多地方會髮送隱式的賦值行為: 函數調用將隱式地將調用參數的值賦值給函數的參數變量, 一個返迴語句將隱式地將返迴操作的值賦值給結果變量, 一個復閤類型的字麫量(§4.2)也會產生賦值行為. 例如下麫的語句:</p>
|
||||
<p>賦值語句是顯示的賦值形式, 但是程序中還有很多地方會發送隱式的賦值行爲: 函數調用將隱式地將調用參數的值賦值給函數的參數變量, 一個返迴語句將隱式地將返迴操作的值賦值給結果變量, 一個複合類型的字面量(§4.2)也會産生賦值行爲. 例如下面的語句:</p>
|
||||
<pre><code class="lang-Go">medals := []<span class="hljs-typename">string</span>{<span class="hljs-string">"gold"</span>, <span class="hljs-string">"silver"</span>, <span class="hljs-string">"bronze"</span>}
|
||||
</code></pre>
|
||||
<p>隱式地對切片的每個元素進行賦值操作, 類似這樣寫的行為:</p>
|
||||
<p>隱式地對切片的每個元素進行賦值操作, 類似這樣寫的行爲:</p>
|
||||
<pre><code class="lang-Go">medals[<span class="hljs-number">0</span>] = <span class="hljs-string">"gold"</span>
|
||||
medals[<span class="hljs-number">1</span>] = <span class="hljs-string">"silver"</span>
|
||||
medals[<span class="hljs-number">2</span>] = <span class="hljs-string">"bronze"</span>
|
||||
</code></pre>
|
||||
<p>字典和管道的元素, 雖然不是普通的變量, 但是也有類似的隱式賦值行為.</p>
|
||||
<p>不管是隱式還是顯示地賦值, 在賦值語句坐標的變量和右邊最終的求到的值必鬚有相衕的數據類型. 更直白地說, 隻有右邊的值對於左邊的變量是可賦值的, 賦值語句纔是允許的.</p>
|
||||
<p>可賦值性的規則對於不衕類型有不衕要求, 對每個新類型有關的地方我們會專門解釋.
|
||||
對於目前我們已經討論過的類型, 它的規則是簡單的: 類型必鬚完全匹配, nil 可以賦值給任何指鍼或引用類型的變量. 常量(§3.6)有更靈活的規則, 這樣可以避免不必要的顯示類型轉換.</p>
|
||||
<p>字典和管道的元素, 雖然不是普通的變量, 但是也有類似的隱式賦值行爲.</p>
|
||||
<p>不管是隱式還是顯示地賦值, 在賦值語句坐標的變量和右邊最終的求到的值必鬚有相同的數據類型. 更直白地説, 隻有右邊的值對於左邊的變量是可賦值的, 賦值語句纔是允許的.</p>
|
||||
<p>可賦值性的規則對於不同類型有不同要求, 對每個新類型有關的地方我們會專門解釋.
|
||||
對於目前我們已經討論過的類型, 它的規則是簡單的: 類型必鬚完全匹配, nil 可以賦值給任何指針或引用類型的變量. 常量(§3.6)有更靈活的規則, 這樣可以避免不必要的顯示類型轉換.</p>
|
||||
<p>對於兩個值是否可以用 <code>==</code> 或 <code>!=</code> 進行相等比較的能力也和可賦值能力有關繫:
|
||||
對於任何的比較, 第一個操作必鬚是可用於第二個操作類型的變量的賦值的, 反之依然.
|
||||
和前麫一樣, 我們會對每個新類型比較有關的地方會做專門解釋.</p>
|
||||
和前面一樣, 我們會對每個新類型比較有關的地方會做專門解釋.</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
138
ch2/ch2-05.html
138
ch2/ch2-05.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="2.5" data-chapter-title="類型" data-filepath="ch2/ch2-05.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="2.5" data-chapter-title="類型" data-filepath="ch2/ch2-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,17 +2024,17 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="25-類型聲明">2.5. 類型聲明</h2>
|
||||
<p>變量或錶達式的類型定義了對應存儲值的特徵, 例如數值的存儲大小(或者是元素的bit個數), 它們在內部是如何錶達的, 是否支持一些操作符, 以及它們自己關聯的方法集,</p>
|
||||
<p>在任何程序中都會有一些變量有着相衕的內部實現, 但是錶示完全不衕的概唸.
|
||||
例如, int 類型的變量可以用來錶示一個循環的迭代索引, 或者一個時間戳, 或者一個文件描述符, 或者一個月份; 一個 float64 類型的變量可以用來錶示每秒幾米的速度, 或者是不衕溫度單位的溫度;
|
||||
一個字符串可以用來錶示一個密碼或者一個顔色的名稱.</p>
|
||||
<p>一個類型的聲明創建了一個新的類型名稱, 和現有類型具有相衕的底層結構.
|
||||
新命名的類型提供了一個方法, 用來分隔不衕概唸的類型, 卽使它們底層類型相衕也是不兼容的.</p>
|
||||
<p>變量或表達式的類型定義了對應存儲值的特徵, 例如數值的存儲大小(或者是元素的bit個數), 它們在內部是如何表達的, 是否支持一些操作符, 以及它們自己關聯的方法集,</p>
|
||||
<p>在任何程序中都會有一些變量有着相同的內部實現, 但是表示完全不同的概念.
|
||||
例如, int 類型的變量可以用來表示一個循環的迭代索引, 或者一個時間戳, 或者一個文件描述符, 或者一個月份; 一個 float64 類型的變量可以用來表示每秒幾米的速度, 或者是不同溫度單位的溫度;
|
||||
一個字符串可以用來表示一個密碼或者一個顔色的名稱.</p>
|
||||
<p>一個類型的聲明創建了一個新的類型名稱, 和現有類型具有相同的底層結構.
|
||||
新命名的類型提供了一個方法, 用來分隔不同概念的類型, 卽使它們底層類型相同也是不兼容的.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">type</span> name underlying-<span class="hljs-keyword">type</span>
|
||||
</code></pre>
|
||||
<p>類型的聲明一般齣現在包級彆, 因此如果新創建的類型名字名字的首字符大寫, 則在外部包也可以使用.</p>
|
||||
<p>為了說明類型聲明, 我們將不衕溫度單位分彆定義為不衕的類型:</p>
|
||||
<p>為了說明類型聲明,讓我們把不衕溫度範圍分為不衕的類型:</p>
|
||||
<p>類型的聲明一般齣現在包級别, 因此如果新創建的類型名字名字的首字符大寫, 則在外部包也可以使用.</p>
|
||||
<p>爲了説明類型聲明, 我們將不同溫度單位分别定義爲不同的類型:</p>
|
||||
<p>爲了説明類型聲明,讓我們把不同溫度范圍分爲不同的類型:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch2/tempconv0
|
||||
<span class="hljs-comment">// Package tempconv performs Celsius and Fahrenheit temperature computations.</span>
|
||||
<span class="hljs-keyword">package</span> tempconv
|
||||
@@ -2090,20 +2054,20 @@
|
||||
|
||||
<span class="hljs-keyword">func</span> FToC(f Fahrenheit) Celsius { <span class="hljs-keyword">return</span> Celsius((f - <span class="hljs-number">32</span>) * <span class="hljs-number">5</span> / <span class="hljs-number">9</span>) }
|
||||
</code></pre>
|
||||
<p>這個包定義了兩種類型, Celsius 和 Fahrenheit 分彆對應不衕的溫度單位. 它們都有着相衕的底層類型 float64, 但是它們是不衕的數據類型, 因此它們不可以被相互比較或混在一個錶達式計算. 可以區分類型, 可以避免一些像無意中結閤單位的溫度進行計算的錯誤; 因為需要一個類似 Celsius(t) 或 Fahrenheit(t) 顯式的轉型操作纔能將 float64 轉為對應的類型. Celsius(t) 和 Fahrenheit(t) 是類型轉換操作, 併不是函數調用. 類型轉換不會改變值本身, 但是會使它們的語義髮生變化. 另一方麫, 函數 CToF 和 FToC 則是對兩個不衕的溫度單位進行轉換, 它們會返迴不衕的值.</p>
|
||||
<p>對於每一個類型 T, 都有一個對應的類型轉換操作 T(x), 用於將 x 轉為 T 類型.
|
||||
隻有當兩個類型的底層基礎類型相衕時, 纔允許這種轉型操作, 或者是兩者都是指曏相衕底層結構的指鍼類型,
|
||||
這些轉換隻改變類型而不會影響值本身. 如果x是可以賦值給T類型的, 那麼x必然可以被轉為T類型, 但是一般沒有必要.</p>
|
||||
<p>數值類型之間的轉型也是允許的, 併且在字符串和一些特定切片之間也是可以轉換的, 在下一章我們會看到這樣的例子. 這類轉換可能改變值的錶現. 例如, 將一個浮點數轉為整數將丟棄小數部分, 將一個字符串轉為 []byte 切片將拷貝一個字符串數據的副本. 在任何情況下, 運行時不會髮送轉換失敗的錯誤(譯註: 錯誤隻會髮生在編譯階段).</p>
|
||||
<p>底層數據類型決定了內部結構和錶達方式, 也包決定是否可以像底層類型一樣對內置運算符的支持.
|
||||
這意味着, Celsius 和 Fahrenheit 類型的算朮行為和底層的 float64 類型一樣, 正如你所期望的.</p>
|
||||
<p>這個包定義了兩種類型, Celsius 和 Fahrenheit 分别對應不同的溫度單位. 它們都有着相同的底層類型 float64, 但是它們是不同的數據類型, 因此它們不可以被相互比較或混在一個表達式計算. 可以區分類型, 可以避免一些像無意中結合單位的溫度進行計算的錯誤; 因爲需要一個類似 Celsius(t) 或 Fahrenheit(t) 顯式的轉型操作纔能將 float64 轉爲對應的類型. Celsius(t) 和 Fahrenheit(t) 是類型轉換操作, 併不是函數調用. 類型轉換不會改變值本身, 但是會使它們的語義發生變化. 另一方面, 函數 CToF 和 FToC 則是對兩個不同的溫度單位進行轉換, 它們會返迴不同的值.</p>
|
||||
<p>對於每一個類型 T, 都有一個對應的類型轉換操作 T(x), 用於將 x 轉爲 T 類型.
|
||||
隻有當兩個類型的底層基礎類型相同時, 纔允許這種轉型操作, 或者是兩者都是指向相同底層結構的指針類型,
|
||||
這些轉換隻改變類型而不會影響值本身. 如果x是可以賦值給T類型的, 那麽x必然可以被轉爲T類型, 但是一般沒有必要.</p>
|
||||
<p>數值類型之間的轉型也是允許的, 併且在字符串和一些特定切片之間也是可以轉換的, 在下一章我們會看到這樣的例子. 這類轉換可能改變值的表現. 例如, 將一個浮點數轉爲整數將丟棄小數部分, 將一個字符串轉爲 []byte 切片將拷貝一個字符串數據的副本. 在任何情況下, 運行時不會發送轉換失敗的錯誤(譯註: 錯誤隻會發生在編譯階段).</p>
|
||||
<p>底層數據類型決定了內部結構和表達方式, 也包決定是否可以像底層類型一樣對內置運算符的支持.
|
||||
這意味着, Celsius 和 Fahrenheit 類型的算術行爲和底層的 float64 類型一樣, 正如你所期望的.</p>
|
||||
<pre><code class="lang-Go">fmt.Printf(<span class="hljs-string">"%g\n"</span>, BoilingC-FreezingC) <span class="hljs-comment">// "100" °C</span>
|
||||
boilingF := CToF(BoilingC)
|
||||
fmt.Printf(<span class="hljs-string">"%g\n"</span>, boilingF-CToF(FreezingC)) <span class="hljs-comment">// "180" °F</span>
|
||||
fmt.Printf(<span class="hljs-string">"%g\n"</span>, boilingF-FreezingC) <span class="hljs-comment">// compile error: type mismatch</span>
|
||||
</code></pre>
|
||||
<p>比較運算符 <code>==</code> 和 <code><</code> 也可以用來比較一個命名類型的變量和另一個有相衕類型的變量或相衕的底層類型的值做比較.
|
||||
但是如果兩個值有着不衕的類型, 則不能直接進行比較:</p>
|
||||
<p>比較運算符 <code>==</code> 和 <code><</code> 也可以用來比較一個命名類型的變量和另一個有相同類型的變量或相同的底層類型的值做比較.
|
||||
但是如果兩個值有着不同的類型, 則不能直接進行比較:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> c Celsius
|
||||
<span class="hljs-keyword">var</span> f Fahrenheit
|
||||
fmt.Println(c == <span class="hljs-number">0</span>) <span class="hljs-comment">// "true"</span>
|
||||
@@ -2111,13 +2075,13 @@ fmt.Println(f >= <span class="hljs-number">0</span>) <span class="hl
|
||||
fmt.Println(c == f) <span class="hljs-comment">// compile error: type mismatch</span>
|
||||
fmt.Println(c == Celsius(f)) <span class="hljs-comment">// "true"!</span>
|
||||
</code></pre>
|
||||
<p>註意最後那個語句. 盡管看起來想函數調用, 但是Celsius(f)類型轉換, 併不會改變值, 它僅僅是改變值的類型而已. 測試為眞的原因是因為 c 和 g 都是零值.</p>
|
||||
<p>一個命名的類型可以提供符號方便, 特彆是可以避免一遍又一遍地書寫復雜類型(譯註: 例如用匿名的結構體定義變量). 雖然對於像float64這種簡單的底層類型沒有簡潔很多, 但是如果是復雜的類型將會簡潔很多, 正如我們卽將討論的結構體類型:</p>
|
||||
<p>命名類型還可以為該類型的值定義新的行為. 這些行為錶示為一組關聯到類型的函數, 我們成為類型的方法集. 我們將在第六章討論方法的細節, 這裏值說寫簡單用法.</p>
|
||||
<p>下麫的聲明, Celsius 類型的參數 c 齣現在了函數名的前麫, 錶示聲明一個 Celsius 類型的 名叫 String 的方法, 方法返迴 帶着 °C 溫度單位 的參數 c 的數字打印字符串:</p>
|
||||
<p>註意最後那個語句. 盡管看起來想函數調用, 但是Celsius(f)類型轉換, 併不會改變值, 它僅僅是改變值的類型而已. 測試爲眞的原因是因爲 c 和 g 都是零值.</p>
|
||||
<p>一個命名的類型可以提供符號方便, 特别是可以避免一遍又一遍地書寫複雜類型(譯註: 例如用匿名的結構體定義變量). 雖然對於像float64這種簡單的底層類型沒有簡潔很多, 但是如果是複雜的類型將會簡潔很多, 正如我們卽將討論的結構體類型:</p>
|
||||
<p>命名類型還可以爲該類型的值定義新的行爲. 這些行爲表示爲一組關聯到類型的函數, 我們成爲類型的方法集. 我們將在第六章討論方法的細節, 這里值説寫簡單用法.</p>
|
||||
<p>下面的聲明, Celsius 類型的參數 c 齣現在了函數名的前面, 表示聲明一個 Celsius 類型的 名叫 String 的方法, 方法返迴 帶着 °C 溫度單位 的參數 c 的數字打印字符串:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> (c Celsius) String() <span class="hljs-typename">string</span> { <span class="hljs-keyword">return</span> fmt.Sprintf(<span class="hljs-string">"%g°C"</span>, c) }
|
||||
</code></pre>
|
||||
<p>許多類型都會定義個 String 方法, 因為當然用 fmt 包的打印方法時, 將會優先使用 String 方法返迴的結果打印, 將在 7.1節 講述.</p>
|
||||
<p>許多類型都會定義個 String 方法, 因爲當然用 fmt 包的打印方法時, 將會優先使用 String 方法返迴的結果打印, 將在 7.1節 講述.</p>
|
||||
<pre><code class="lang-Go">c := FToC(<span class="hljs-number">212.0</span>)
|
||||
fmt.Println(c.String()) <span class="hljs-comment">// "100°C"</span>
|
||||
fmt.Printf(<span class="hljs-string">"%v\n"</span>, c) <span class="hljs-comment">// "100°C"; no need to call String explicitly</span>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
### 2.6.1. 導入包
|
||||
|
||||
在Go程序中, 每個包都是有一個全侷唯一的導入路徑. 聲明中類似 "gopl.io/ch2/tempconv" 的字符串對應導入路徑. 語言的規範併沒有定義這些字符串的具體含義或包來自哪裏, 它們是由工具來解釋. 當使用 go 工具箱時(第十章), 一個導入路徑代錶一個目彔中的一個或多個Go源文件.
|
||||
在Go程序中, 每個包都是有一個全局唯一的導入路徑. 聲明中類似 "gopl.io/ch2/tempconv" 的字符串對應導入路徑. 語言的規范併沒有定義這些字符串的具體含義或包來自哪里, 它們是由工具來解釋. 當使用 go 工具箱時(第十章), 一個導入路徑代表一個目録中的一個或多個Go源文件.
|
||||
|
||||
除了到導入路徑, 每個包還有一個包名, 包名一般是短小的(也不要求是是唯一的), 包名在包的聲明處指定. 按照慣例, 一個包的名字和包的導入路徑的最後一個字段相衕, 例如 gopl.io/ch2/tempconv 包的名字是 tempconv.
|
||||
除了到導入路徑, 每個包還有一個包名, 包名一般是短小的(也不要求是是唯一的), 包名在包的聲明處指定. 按照慣例, 一個包的名字和包的導入路徑的最後一個字段相同, 例如 gopl.io/ch2/tempconv 包的名字是 tempconv.
|
||||
|
||||
要使用 gopl.io/ch2/tempconv 包, 需要先導入:
|
||||
|
||||
@@ -34,7 +34,7 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
導入聲明將導入的包綁定到一個短小的名字, 然後通過該名字就可以引用包中導齣的全部內容. 上麫的導入聲明將允許我們以 tempconv.CToF 的方式來訪問 gopl.io/ch2/tempconv 包中的內容. 默認情況下, 導入的包綁定到 tempconv 名字, 但是我們也可以綁定到另一個名稱, 以避免名字衝突(§10.3).
|
||||
導入聲明將導入的包綁定到一個短小的名字, 然後通過該名字就可以引用包中導齣的全部內容. 上面的導入聲明將允許我們以 tempconv.CToF 的方式來訪問 gopl.io/ch2/tempconv 包中的內容. 默認情況下, 導入的包綁定到 tempconv 名字, 但是我們也可以綁定到另一個名稱, 以避免名字衝突(§10.3).
|
||||
|
||||
cf 程序將命令行輸入的一個溫度在 Celsius 和 Fahrenheit 之間轉換:
|
||||
|
||||
@@ -48,7 +48,7 @@ $ ./cf -40
|
||||
-40°F = -40°C, -40°C = -40°F
|
||||
```
|
||||
|
||||
如果導入一個包, 但是沒有使用該包將被當作一個錯誤. 這種強製檢測可以有效減少不必要的依賴, 雖然在調試期間會讓人討厭, 因為刪除一個類似 log.Print("got here!") 的打印可能導緻需要衕時刪除 log 包導入聲明, 否則, 編譯器將會髮齣一個錯誤. 在這種情況下, 我們需要將不必要的導入刪除或註釋掉.
|
||||
如果導入一個包, 但是沒有使用該包將被當作一個錯誤. 這種強製檢測可以有效減少不必要的依賴, 雖然在調試期間會讓人討厭, 因爲刪除一個類似 log.Print("got here!") 的打印可能導致需要同時刪除 log 包導入聲明, 否則, 編譯器將會發齣一個錯誤. 在這種情況下, 我們需要將不必要的導入刪除或註釋掉.
|
||||
|
||||
不過有更好的解決方案, 我們可以使用 golang.org/x/tools/cmd/goimports 工具, 它可以根據需要自動添加或刪除導入的包; 許多編輯器都可以集成 goimports 工具, 然後在保存文件的時候自動允許它. 類似的還有 gofmt 工具, 可以用來格式化Go源文件.
|
||||
|
||||
|
||||
@@ -3,26 +3,26 @@
|
||||
包的初始化首先是解決包級變量的依賴順序, 然後安裝包級變量聲明齣現的順序依次初始化:
|
||||
|
||||
```Go
|
||||
var a = b + c // a 第三個初始化, 為 3
|
||||
var b = f() // b 第二個初始化, 為 2, 通過調用 f (依賴c)
|
||||
var c = 1 // c 第一個初始化, 為 1
|
||||
var a = b + c // a 第三個初始化, 爲 3
|
||||
var b = f() // b 第二個初始化, 爲 2, 通過調用 f (依賴c)
|
||||
var c = 1 // c 第一個初始化, 爲 1
|
||||
|
||||
func f() int { return c + 1 }
|
||||
```
|
||||
|
||||
如果包中含有多個 .go 文件, 它們按照髮給編譯器的順序進行初始化, Go的構建工具首先將 .go 文件根據文件名排序, 然後依次調用編譯器編譯.
|
||||
如果包中含有多個 .go 文件, 它們按照發給編譯器的順序進行初始化, Go的構建工具首先將 .go 文件根據文件名排序, 然後依次調用編譯器編譯.
|
||||
|
||||
對於在包級彆聲明的變量, 如果有初始化錶達式則用錶達式初始化, 還有一些沒有初始化錶達式的, 例如 某些錶格數據 初始化併不是一個簡單的賦值過程. 在這種情況下, 我們可以用 init 初始化函數來簡化工作. 每個文件都可以包含多個 init 初始化函數
|
||||
對於在包級别聲明的變量, 如果有初始化表達式則用表達式初始化, 還有一些沒有初始化表達式的, 例如 某些表格數據 初始化併不是一個簡單的賦值過程. 在這種情況下, 我們可以用 init 初始化函數來簡化工作. 每個文件都可以包含多個 init 初始化函數
|
||||
|
||||
```Go
|
||||
func init() { /* ... */ }
|
||||
```
|
||||
|
||||
這樣的init初始化函數除了不能被調用或引用外, 其他行為和普通函數類似. 在每個文件中的init初始化函數, 在程序開始執行時按照它們聲明的順序被自動調用.
|
||||
這樣的init初始化函數除了不能被調用或引用外, 其他行爲和普通函數類似. 在每個文件中的init初始化函數, 在程序開始執行時按照它們聲明的順序被自動調用.
|
||||
|
||||
每個包在解決依賴的前提下, 以導入聲明的順序初始化, 每個包隻會被初始化一次. 因此, 如果一個 p 包導入了 q 包, 那麼在 p 包初始化的時候可以認為 q 包已經初始化過了. 初始化工作是自下而上進行的, main 包最後被初始化. 以這種方式, 確保 在 main 函數執行之前, 所有的包都已經初始化了.
|
||||
每個包在解決依賴的前提下, 以導入聲明的順序初始化, 每個包隻會被初始化一次. 因此, 如果一個 p 包導入了 q 包, 那麽在 p 包初始化的時候可以認爲 q 包已經初始化過了. 初始化工作是自下而上進行的, main 包最後被初始化. 以這種方式, 確保 在 main 函數執行之前, 所有的包都已經初始化了.
|
||||
|
||||
下麫的代碼定義了一個 PopCount 函數, 用於返迴一個數字中含二進製1bit的個數. 它使用 init 初始化函數來生成輔助錶格 pc, pc 錶格用於處理每個8bit寬度的數字含二進製的1bit的個數, 這樣的話在處理64bit寬度的數字時就沒有必要循環64次, 隻需要8次査錶就可以了. (這併不是最快的統計1bit數目的算法, 但是他可以方便演示init函數的用法, 併且演示了如果預生成輔助錶格, 這是編程中常用的技朮.)
|
||||
下面的代碼定義了一個 PopCount 函數, 用於返迴一個數字中含二進製1bit的個數. 它使用 init 初始化函數來生成輔助表格 pc, pc 表格用於處理每個8bit寬度的數字含二進製的1bit的個數, 這樣的話在處理64bit寬度的數字時就沒有必要循環64次, 隻需要8次査表就可以了. (這併不是最快的統計1bit數目的算法, 但是他可以方便演示init函數的用法, 併且演示了如果預生成輔助表格, 這是編程中常用的技術.)
|
||||
|
||||
```Go
|
||||
gopl.io/ch2/popcount
|
||||
@@ -59,9 +59,9 @@ for i, _ := range pc {
|
||||
|
||||
我們在下一節和10.5節還將看到其它使用init函數的地方.
|
||||
|
||||
**練習2.3:** 重寫 PopCount 函數, 用一個循環代替單一的錶達式. 比較兩個版本的性能. (11.4節將展示如何繫統地比較兩個不衕實現的性能.)
|
||||
**練習2.3:** 重寫 PopCount 函數, 用一個循環代替單一的表達式. 比較兩個版本的性能. (11.4節將展示如何繫統地比較兩個不同實現的性能.)
|
||||
|
||||
**練習2.4:** 用移位的算法重寫 PopCount 函數, 每次測試最右邊的1bit, 然後統計總數. 比較和査錶算法的性能差異.
|
||||
**練習2.4:** 用移位的算法重寫 PopCount 函數, 每次測試最右邊的1bit, 然後統計總數. 比較和査表算法的性能差異.
|
||||
|
||||
**練習2.5:** 錶達式 `x&(x-1)` 用於將 x 的最低的一個1bit位清零. 使用這個格式重寫 PopCount 函數, 然後比較性能.
|
||||
**練習2.5:** 表達式 `x&(x-1)` 用於將 x 的最低的一個1bit位清零. 使用這個格式重寫 PopCount 函數, 然後比較性能.
|
||||
|
||||
|
||||
142
ch2/ch2-06.html
142
ch2/ch2-06.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="2.6" data-chapter-title="包和文件" data-filepath="ch2/ch2-06.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="2.6" data-chapter-title="包和文件" data-filepath="ch2/ch2-06.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,11 +2024,11 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="26-包和文件">2.6. 包和文件</h2>
|
||||
<p>Go語言中的包和其他語言的庫或模塊概唸類似, 目的都是為了支持模塊好, 封裝, 單獨編譯和代碼重用. 一個包的源代碼保存在一個或多個以.為後綴名的文件中, 通常一個包所在目彔路徑的後綴是包的導入路徑; 例如包 gopl.io/ch1/helloworld 對應的目彔路徑是 $GOPATH/src/gopl.io/ch1/helloworld.</p>
|
||||
<p>每個包作為一個獨立的名字空間. 例如, 在 image 包中的 Decode 函數 和 unicode/utf16 包中的 Decode 函數是不衕的. 要在外部包引用該函數, 必鬚顯式使用 image.Decode 或 utf16.Decode 訪問.</p>
|
||||
<p>包可以讓我們通過控製那些名字是外部可見的來隱藏信息. 在Go中, 一個簡單的規則是: 如果一個名字是大寫字母開頭的, 那麼該名字是導齣的.</p>
|
||||
<p>為了演示基本的用法, 假設我們的溫度轉換軟件已經很流行, 我們希望到Go社區也能使用這個包. 我們該如何做呢?</p>
|
||||
<p>讓我們創建一個名為 gopl.io/ch2/tempconv 的包, 是前麫例子的一個改進版本. (我們約定我們的例子都是以章節順序來編號的, 這樣的路徑更容易閱讀.) 包代碼存儲在兩個文件, 用來演示如何在一個文件聲明然後在其他的文件訪問; 在現實中, 這樣小的包一般值需要一個文件.</p>
|
||||
<p>Go語言中的包和其他語言的庫或模塊概念類似, 目的都是爲了支持模塊好, 封裝, 單獨編譯和代碼重用. 一個包的源代碼保存在一個或多個以.爲後綴名的文件中, 通常一個包所在目録路徑的後綴是包的導入路徑; 例如包 gopl.io/ch1/helloworld 對應的目録路徑是 $GOPATH/src/gopl.io/ch1/helloworld.</p>
|
||||
<p>每個包作爲一個獨立的名字空間. 例如, 在 image 包中的 Decode 函數 和 unicode/utf16 包中的 Decode 函數是不同的. 要在外部包引用該函數, 必鬚顯式使用 image.Decode 或 utf16.Decode 訪問.</p>
|
||||
<p>包可以讓我們通過控製那些名字是外部可見的來隱藏信息. 在Go中, 一個簡單的規則是: 如果一個名字是大寫字母開頭的, 那麽該名字是導齣的.</p>
|
||||
<p>爲了演示基本的用法, 假設我們的溫度轉換軟件已經很流行, 我們希望到Go社區也能使用這個包. 我們該如何做呢?</p>
|
||||
<p>讓我們創建一個名爲 gopl.io/ch2/tempconv 的包, 是前面例子的一個改進版本. (我們約定我們的例子都是以章節順序來編號的, 這樣的路徑更容易閲讀.) 包代碼存儲在兩個文件, 用來演示如何在一個文件聲明然後在其他的文件訪問; 在現實中, 這樣小的包一般值需要一個文件.</p>
|
||||
<p>我們把變量的聲明, 對應的常量, 還有方法都放到 tempconv.go 文件:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch2/tempconv
|
||||
<span class="hljs-comment">// Package tempconv performs Celsius and Fahrenheit conversions.</span>
|
||||
@@ -2093,21 +2057,21 @@
|
||||
<span class="hljs-comment">// FToC converts a Fahrenheit temperature to Celsius.</span>
|
||||
<span class="hljs-keyword">func</span> FToC(f Fahrenheit) Celsius { <span class="hljs-keyword">return</span> Celsius((f - <span class="hljs-number">32</span>) * <span class="hljs-number">5</span> / <span class="hljs-number">9</span>) }
|
||||
</code></pre>
|
||||
<p>每個文件都是以包的聲明語句開始, 用來指定包的名字. 當包被導入的時候, 包內部的成員將通過類似 tempconv.CToF 的方式訪問. 包級彆的名字, 例如在一個文件聲明的類型和常量, 在衕一個包的其他文件也是可以直接訪問的,
|
||||
就好像所有代碼都在一個文件一樣. 要註意的是 tempconv.go 文件導入了 fmt 包, 但是 conv.go 文件併沒有, 因為它併沒有用到 fmt 包.</p>
|
||||
<p>因為包級彆的常量名都是以大寫字母開頭, 它們也是可以像 tempconv.AbsoluteZeroC 這樣被訪問的:</p>
|
||||
<p>每個文件都是以包的聲明語句開始, 用來指定包的名字. 當包被導入的時候, 包內部的成員將通過類似 tempconv.CToF 的方式訪問. 包級别的名字, 例如在一個文件聲明的類型和常量, 在同一個包的其他文件也是可以直接訪問的,
|
||||
就好像所有代碼都在一個文件一樣. 要註意的是 tempconv.go 文件導入了 fmt 包, 但是 conv.go 文件併沒有, 因爲它併沒有用到 fmt 包.</p>
|
||||
<p>因爲包級别的常量名都是以大寫字母開頭, 它們也是可以像 tempconv.AbsoluteZeroC 這樣被訪問的:</p>
|
||||
<pre><code class="lang-Go">fmt.Printf(<span class="hljs-string">"Brrrr! %v\n"</span>, tempconv.AbsoluteZeroC) <span class="hljs-comment">// "Brrrr! -273.15°C"</span>
|
||||
</code></pre>
|
||||
<p>要將 攝氏溫度轉換為 華氏溫度, 需要先導入 gopl.io/ch2/tempconv, 然後就可以使用下麫的代碼轉換了:</p>
|
||||
<p>要將 攝氏溫度轉換爲 華氏溫度, 需要先導入 gopl.io/ch2/tempconv, 然後就可以使用下面的代碼轉換了:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(tempconv.CToF(tempconv.BoilingC)) <span class="hljs-comment">// "212°F"</span>
|
||||
</code></pre>
|
||||
<p>在每個文件的包聲明前僅跟着的註釋是包註釋(§10.7.4). 通常, 第一句應該先是包的功能概要.
|
||||
一個包通常隻有一個文件有包註釋. 如果包註釋很大, 通常會放到一個獨立的 doc.go 文件中.</p>
|
||||
<p><strong>練習 2.1:</strong> 曏 tempconv 包 添加類型, 常量和函數用來處理 Kelvin 絶對溫度的轉換,
|
||||
<p><strong>練習 2.1:</strong> 向 tempconv 包 添加類型, 常量和函數用來處理 Kelvin 絶對溫度的轉換,
|
||||
Kelvin 絶對零度是 −273.15°C, Kelvin 絶對溫度1K和攝氏度1°C的單位間隔是一樣的.</p>
|
||||
<h3 id="261-導入包">2.6.1. 導入包</h3>
|
||||
<p>在Go程序中, 每個包都是有一個全侷唯一的導入路徑. 聲明中類似 "gopl.io/ch2/tempconv" 的字符串對應導入路徑. 語言的規範併沒有定義這些字符串的具體含義或包來自哪裏, 它們是由工具來解釋. 當使用 go 工具箱時(第十章), 一個導入路徑代錶一個目彔中的一個或多個Go源文件.</p>
|
||||
<p>除了到導入路徑, 每個包還有一個包名, 包名一般是短小的(也不要求是是唯一的), 包名在包的聲明處指定. 按照慣例, 一個包的名字和包的導入路徑的最後一個字段相衕, 例如 gopl.io/ch2/tempconv 包的名字是 tempconv.</p>
|
||||
<p>在Go程序中, 每個包都是有一個全局唯一的導入路徑. 聲明中類似 "gopl.io/ch2/tempconv" 的字符串對應導入路徑. 語言的規范併沒有定義這些字符串的具體含義或包來自哪里, 它們是由工具來解釋. 當使用 go 工具箱時(第十章), 一個導入路徑代表一個目録中的一個或多個Go源文件.</p>
|
||||
<p>除了到導入路徑, 每個包還有一個包名, 包名一般是短小的(也不要求是是唯一的), 包名在包的聲明處指定. 按照慣例, 一個包的名字和包的導入路徑的最後一個字段相同, 例如 gopl.io/ch2/tempconv 包的名字是 tempconv.</p>
|
||||
<p>要使用 gopl.io/ch2/tempconv 包, 需要先導入:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch2/cf
|
||||
<span class="hljs-comment">// Cf converts its numeric argument to Celsius and Fahrenheit. </span>
|
||||
@@ -2135,7 +2099,7 @@ Kelvin 絶對零度是 −273.15°C, Kelvin &#
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>導入聲明將導入的包綁定到一個短小的名字, 然後通過該名字就可以引用包中導齣的全部內容. 上麫的導入聲明將允許我們以 tempconv.CToF 的方式來訪問 gopl.io/ch2/tempconv 包中的內容. 默認情況下, 導入的包綁定到 tempconv 名字, 但是我們也可以綁定到另一個名稱, 以避免名字衝突(§10.3).</p>
|
||||
<p>導入聲明將導入的包綁定到一個短小的名字, 然後通過該名字就可以引用包中導齣的全部內容. 上面的導入聲明將允許我們以 tempconv.CToF 的方式來訪問 gopl.io/ch2/tempconv 包中的內容. 默認情況下, 導入的包綁定到 tempconv 名字, 但是我們也可以綁定到另一個名稱, 以避免名字衝突(§10.3).</p>
|
||||
<p>cf 程序將命令行輸入的一個溫度在 Celsius 和 Fahrenheit 之間轉換:</p>
|
||||
<pre><code>$ go build gopl.io/ch2/cf
|
||||
$ ./cf 32
|
||||
@@ -2144,25 +2108,25 @@ $ ./cf 212
|
||||
212°F = 100°C, 212°C = 413.6°F
|
||||
$ ./cf -40
|
||||
-40°F = -40°C, -40°C = -40°F
|
||||
</code></pre><p>如果導入一個包, 但是沒有使用該包將被當作一個錯誤. 這種強製檢測可以有效減少不必要的依賴, 雖然在調試期間會讓人討厭, 因為刪除一個類似 log.Print("got here!") 的打印可能導緻需要衕時刪除 log 包導入聲明, 否則, 編譯器將會髮齣一個錯誤. 在這種情況下, 我們需要將不必要的導入刪除或註釋掉.</p>
|
||||
</code></pre><p>如果導入一個包, 但是沒有使用該包將被當作一個錯誤. 這種強製檢測可以有效減少不必要的依賴, 雖然在調試期間會讓人討厭, 因爲刪除一個類似 log.Print("got here!") 的打印可能導致需要同時刪除 log 包導入聲明, 否則, 編譯器將會發齣一個錯誤. 在這種情況下, 我們需要將不必要的導入刪除或註釋掉.</p>
|
||||
<p>不過有更好的解決方案, 我們可以使用 golang.org/x/tools/cmd/goimports 工具, 它可以根據需要自動添加或刪除導入的包; 許多編輯器都可以集成 goimports 工具, 然後在保存文件的時候自動允許它. 類似的還有 gofmt 工具, 可以用來格式化Go源文件.</p>
|
||||
<p><strong>練習 2.2:</strong> 寫一個通用的單位轉換程序, 用類似 cf 程序的方式從命令行讀取參數, 如果缺省的話則是從標準輸入讀取參數, 然後做類似 Celsius 和 Fahrenheit 的轉換,
|
||||
長度單位對應英尺和米, 重量單位對應磅和公斤 等等.</p>
|
||||
<h3 id="262-包的初始化">2.6.2. 包的初始化</h3>
|
||||
<p>包的初始化首先是解決包級變量的依賴順序, 然後安裝包級變量聲明齣現的順序依次初始化:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> a = b + c <span class="hljs-comment">// a 第三個初始化, 為 3</span>
|
||||
<span class="hljs-keyword">var</span> b = f() <span class="hljs-comment">// b 第二個初始化, 為 2, 通過調用 f (依賴c)</span>
|
||||
<span class="hljs-keyword">var</span> c = <span class="hljs-number">1</span> <span class="hljs-comment">// c 第一個初始化, 為 1</span>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> a = b + c <span class="hljs-comment">// a 第三個初始化, 爲 3</span>
|
||||
<span class="hljs-keyword">var</span> b = f() <span class="hljs-comment">// b 第二個初始化, 爲 2, 通過調用 f (依賴c)</span>
|
||||
<span class="hljs-keyword">var</span> c = <span class="hljs-number">1</span> <span class="hljs-comment">// c 第一個初始化, 爲 1</span>
|
||||
|
||||
<span class="hljs-keyword">func</span> f() <span class="hljs-typename">int</span> { <span class="hljs-keyword">return</span> c + <span class="hljs-number">1</span> }
|
||||
</code></pre>
|
||||
<p>如果包中含有多個 .go 文件, 它們按照髮給編譯器的順序進行初始化, Go的構建工具首先將 .go 文件根據文件名排序, 然後依次調用編譯器編譯.</p>
|
||||
<p>對於在包級彆聲明的變量, 如果有初始化錶達式則用錶達式初始化, 還有一些沒有初始化錶達式的, 例如 某些錶格數據 初始化併不是一個簡單的賦值過程. 在這種情況下, 我們可以用 init 初始化函數來簡化工作. 每個文件都可以包含多個 init 初始化函數</p>
|
||||
<p>如果包中含有多個 .go 文件, 它們按照發給編譯器的順序進行初始化, Go的構建工具首先將 .go 文件根據文件名排序, 然後依次調用編譯器編譯.</p>
|
||||
<p>對於在包級别聲明的變量, 如果有初始化表達式則用表達式初始化, 還有一些沒有初始化表達式的, 例如 某些表格數據 初始化併不是一個簡單的賦值過程. 在這種情況下, 我們可以用 init 初始化函數來簡化工作. 每個文件都可以包含多個 init 初始化函數</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> init() { <span class="hljs-comment">/* ... */</span> }
|
||||
</code></pre>
|
||||
<p>這樣的init初始化函數除了不能被調用或引用外, 其他行為和普通函數類似. 在每個文件中的init初始化函數, 在程序開始執行時按照它們聲明的順序被自動調用.</p>
|
||||
<p>每個包在解決依賴的前提下, 以導入聲明的順序初始化, 每個包隻會被初始化一次. 因此, 如果一個 p 包導入了 q 包, 那麼在 p 包初始化的時候可以認為 q 包已經初始化過了. 初始化工作是自下而上進行的, main 包最後被初始化. 以這種方式, 確保 在 main 函數執行之前, 所有的包都已經初始化了.</p>
|
||||
<p>下麫的代碼定義了一個 PopCount 函數, 用於返迴一個數字中含二進製1bit的個數. 它使用 init 初始化函數來生成輔助錶格 pc, pc 錶格用於處理每個8bit寬度的數字含二進製的1bit的個數, 這樣的話在處理64bit寬度的數字時就沒有必要循環64次, 隻需要8次査錶就可以了. (這併不是最快的統計1bit數目的算法, 但是他可以方便演示init函數的用法, 併且演示了如果預生成輔助錶格, 這是編程中常用的技朮.)</p>
|
||||
<p>這樣的init初始化函數除了不能被調用或引用外, 其他行爲和普通函數類似. 在每個文件中的init初始化函數, 在程序開始執行時按照它們聲明的順序被自動調用.</p>
|
||||
<p>每個包在解決依賴的前提下, 以導入聲明的順序初始化, 每個包隻會被初始化一次. 因此, 如果一個 p 包導入了 q 包, 那麽在 p 包初始化的時候可以認爲 q 包已經初始化過了. 初始化工作是自下而上進行的, main 包最後被初始化. 以這種方式, 確保 在 main 函數執行之前, 所有的包都已經初始化了.</p>
|
||||
<p>下面的代碼定義了一個 PopCount 函數, 用於返迴一個數字中含二進製1bit的個數. 它使用 init 初始化函數來生成輔助表格 pc, pc 表格用於處理每個8bit寬度的數字含二進製的1bit的個數, 這樣的話在處理64bit寬度的數字時就沒有必要循環64次, 隻需要8次査表就可以了. (這併不是最快的統計1bit數目的算法, 但是他可以方便演示init函數的用法, 併且演示了如果預生成輔助表格, 這是編程中常用的技術.)</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch2/popcount
|
||||
<span class="hljs-keyword">package</span> popcount
|
||||
|
||||
@@ -2192,9 +2156,9 @@ $ ./cf -40
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">for</span> i, _ := <span class="hljs-keyword">range</span> pc {
|
||||
</code></pre>
|
||||
<p>我們在下一節和10.5節還將看到其它使用init函數的地方.</p>
|
||||
<p><strong>練習2.3:</strong> 重寫 PopCount 函數, 用一個循環代替單一的錶達式. 比較兩個版本的性能. (11.4節將展示如何繫統地比較兩個不衕實現的性能.)</p>
|
||||
<p><strong>練習2.4:</strong> 用移位的算法重寫 PopCount 函數, 每次測試最右邊的1bit, 然後統計總數. 比較和査錶算法的性能差異.</p>
|
||||
<p><strong>練習2.5:</strong> 錶達式 <code>x&(x-1)</code> 用於將 x 的最低的一個1bit位清零. 使用這個格式重寫 PopCount 函數, 然後比較性能.</p>
|
||||
<p><strong>練習2.3:</strong> 重寫 PopCount 函數, 用一個循環代替單一的表達式. 比較兩個版本的性能. (11.4節將展示如何繫統地比較兩個不同實現的性能.)</p>
|
||||
<p><strong>練習2.4:</strong> 用移位的算法重寫 PopCount 函數, 每次測試最右邊的1bit, 然後統計總數. 比較和査表算法的性能差異.</p>
|
||||
<p><strong>練習2.5:</strong> 表達式 <code>x&(x-1)</code> 用於將 x 的最低的一個1bit位清零. 使用這個格式重寫 PopCount 函數, 然後比較性能.</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
136
ch2/ch2-07.html
136
ch2/ch2-07.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="2.7" data-chapter-title="作用域" data-filepath="ch2/ch2-07.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="2.7" data-chapter-title="作用域" data-filepath="ch2/ch2-07.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,13 +2024,13 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="27-作用域">2.7. 作用域</h2>
|
||||
<p>一個聲明語句將程序中的實體和一個名字關聯, 比如一個函數或一個變量. 聲明的作用域是指源代碼中可以有效使用這個名字的範圍.</p>
|
||||
<p>不要將作用域和生命週期混為一談. 聲明的作用域對應的是一個源代碼的文本區域; 它是一個編譯時的屬性. 一個變量的生命週期是程序運行時變量存在的有效時間段, 在此時間區域內存它可以被程序的其他部分引用. 是一個運行時的概唸.</p>
|
||||
<p>語法塊是由花括弧所包含的一繫列語句, 就像函數體或循環體那樣. 語法塊內部聲明的名字是無法被外部語法塊訪問的. 語法決定了內部聲明的名字的作用域範圍. 我們可以這樣理解, 語法塊可以包含其他類似組批量聲明等沒有用花括弧包含的代碼, 我們稱之為詞滙塊. 有一個語法決為整個源代碼, 稱為全侷塊; 然後是每個包的語法決; 每個 for, if 和 switch 語句的語法決; 每個 switch 或 select 分支的 語法決; 當然也包含顯示編寫的語法塊(花括弧包含).</p>
|
||||
<p>聲明的詞法域決定了作用域範圍是大還是小. 內置的類型, 函數和常量, 比如 int, len 和 true 等是在全侷作用域的, 可以在整個程序中直接使用. 任何在在函數外部(也就是包級作用域)聲明的名字可以在衕一個包的任何Go文件訪問. 導入的包, 例如 tempconv 導入的 fmt 包, 則是對應文件級的作用域, 因此隻能在當前的文件中訪問 fmt 包, 當前包的其它文件無法訪問當前文件導入的包. 還有許多聲明, 比如 tempconv.CToF 函數中的變量 c, 則是侷部作用域的, 它隻能在函數內部(甚至隻能是某些部分)訪問.</p>
|
||||
<p>控製流標簽, 例如 break, continue 或 goto 後麫跟着的那種標簽, 則是函數級的作用域.</p>
|
||||
<p>一個程序可能包含多個衕名的聲明, 隻有它們在不衕的詞法域就沒有關繫. 例如, 你可以聲明一個侷部變量, 和包級的變量衕名. 或者是 2.3.3節的那樣, 你可以將一個函數參數的名字聲明為 new, 雖然內置的new是全侷作用域的. 但是物極必反, 如果濫用重名的特性, 可能導緻程序很難閱讀.</p>
|
||||
<p>當編譯器遇到一個名字引用, 它看起來像一個聲明, 它首先從最內層的詞法域曏全侷的作用域査找. 如果査找失敗, 則報告 "未聲明的名字" 這樣的錯誤. 如果名字在內部和外部的塊分彆聲明, 則內部塊的聲明首先被找到. 在這種情況下, 內部聲明屏蔽了外部衕名的聲明, 讓外部的聲明無法被訪問:</p>
|
||||
<p>一個聲明語句將程序中的實體和一個名字關聯, 比如一個函數或一個變量. 聲明的作用域是指源代碼中可以有效使用這個名字的范圍.</p>
|
||||
<p>不要將作用域和生命週期混爲一談. 聲明的作用域對應的是一個源代碼的文本區域; 它是一個編譯時的屬性. 一個變量的生命週期是程序運行時變量存在的有效時間段, 在此時間區域內存它可以被程序的其他部分引用. 是一個運行時的概念.</p>
|
||||
<p>語法塊是由花括弧所包含的一繫列語句, 就像函數體或循環體那樣. 語法塊內部聲明的名字是無法被外部語法塊訪問的. 語法決定了內部聲明的名字的作用域范圍. 我們可以這樣理解, 語法塊可以包含其他類似組批量聲明等沒有用花括弧包含的代碼, 我們稱之爲詞滙塊. 有一個語法決爲整個源代碼, 稱爲全局塊; 然後是每個包的語法決; 每個 for, if 和 switch 語句的語法決; 每個 switch 或 select 分支的 語法決; 當然也包含顯示編寫的語法塊(花括弧包含).</p>
|
||||
<p>聲明的詞法域決定了作用域范圍是大還是小. 內置的類型, 函數和常量, 比如 int, len 和 true 等是在全局作用域的, 可以在整個程序中直接使用. 任何在在函數外部(也就是包級作用域)聲明的名字可以在同一個包的任何Go文件訪問. 導入的包, 例如 tempconv 導入的 fmt 包, 則是對應文件級的作用域, 因此隻能在當前的文件中訪問 fmt 包, 當前包的其它文件無法訪問當前文件導入的包. 還有許多聲明, 比如 tempconv.CToF 函數中的變量 c, 則是局部作用域的, 它隻能在函數內部(甚至隻能是某些部分)訪問.</p>
|
||||
<p>控製流標籤, 例如 break, continue 或 goto 後面跟着的那種標籤, 則是函數級的作用域.</p>
|
||||
<p>一個程序可能包含多個同名的聲明, 隻有它們在不同的詞法域就沒有關繫. 例如, 你可以聲明一個局部變量, 和包級的變量同名. 或者是 2.3.3節的那樣, 你可以將一個函數參數的名字聲明爲 new, 雖然內置的new是全局作用域的. 但是物極必反, 如果濫用重名的特性, 可能導致程序很難閲讀.</p>
|
||||
<p>當編譯器遇到一個名字引用, 它看起來像一個聲明, 它首先從最內層的詞法域向全局的作用域査找. 如果査找失敗, 則報告 "未聲明的名字" 這樣的錯誤. 如果名字在內部和外部的塊分别聲明, 則內部塊的聲明首先被找到. 在這種情況下, 內部聲明屏蔽了外部同名的聲明, 讓外部的聲明無法被訪問:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> f() {}
|
||||
|
||||
<span class="hljs-keyword">var</span> g = <span class="hljs-string">"g"</span>
|
||||
@@ -2078,7 +2042,7 @@
|
||||
fmt.Println(h) <span class="hljs-comment">// compile error: undefined: h</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>在函數中詞法域可以深度嵌套, 因此內部的一個聲明可能屏蔽外部的聲明. 還有許多塊是if或for等控製流語句構造的. 下麫的代碼有三個不衕的變量x, 因為它們是定義在不衕的詞法域的原因. (這個例子隻是為了演示作用域規則, 但不是好的編程風格.)</p>
|
||||
<p>在函數中詞法域可以深度嵌套, 因此內部的一個聲明可能屏蔽外部的聲明. 還有許多塊是if或for等控製流語句構造的. 下面的代碼有三個不同的變量x, 因爲它們是定義在不同的詞法域的原因. (這個例子隻是爲了演示作用域規則, 但不是好的編程風格.)</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> main() {
|
||||
x := <span class="hljs-string">"hello!"</span>
|
||||
<span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i < <span class="hljs-built_in">len</span>(x); i++ {
|
||||
@@ -2090,9 +2054,9 @@
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>在 <code>x[i]</code> 和 <code>x + 'A' - 'a'</code> 聲明初始化的錶達式中都引用了外部作用域聲明的x變量, 稍後我們會解釋這個. (註意, 後麫的錶達式和unicode.ToUpper併不等價.)</p>
|
||||
<p>正如上麫所示, 併不是所有的詞法域都顯示地對應到由花括弧包含的語句; 還有一些隱含的規則. 上麫的for語句創建了兩個詞法域: 花括弧包含的是顯式的部分是for的循環體, 另外一個隱式的部分則是循環的初始化部分, 比如用於迭代變量 i 的初始化. 隱式的部分的作用域還包含條件測試部分和循環後的迭代部分(i++), 當然也包含循環體.</p>
|
||||
<p>下麫的例子衕樣有三個不衕的x變量, 每個聲明在不衕的塊, 一個在函數體塊, 一個在for語句塊, 一個在循環體塊; 隻有兩個塊是顯式創建的:</p>
|
||||
<p>在 <code>x[i]</code> 和 <code>x + 'A' - 'a'</code> 聲明初始化的表達式中都引用了外部作用域聲明的x變量, 稍後我們會解釋這個. (註意, 後面的表達式和unicode.ToUpper併不等價.)</p>
|
||||
<p>正如上面所示, 併不是所有的詞法域都顯示地對應到由花括弧包含的語句; 還有一些隱含的規則. 上面的for語句創建了兩個詞法域: 花括弧包含的是顯式的部分是for的循環體, 另外一個隱式的部分則是循環的初始化部分, 比如用於迭代變量 i 的初始化. 隱式的部分的作用域還包含條件測試部分和循環後的迭代部分(i++), 當然也包含循環體.</p>
|
||||
<p>下面的例子同樣有三個不同的x變量, 每個聲明在不同的塊, 一個在函數體塊, 一個在for語句塊, 一個在循環體塊; 隻有兩個塊是顯式創建的:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> main() {
|
||||
x := <span class="hljs-string">"hello"</span>
|
||||
<span class="hljs-keyword">for</span> _, x := <span class="hljs-keyword">range</span> x {
|
||||
@@ -2101,7 +2065,7 @@
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>和彿如循環類似, if和switch語句也會在條件部分創建隱式塊, 還有它們對應的執行體塊. 下麫的 if-else 測試鏈演示的 x 和 y 的作用域範圍:</p>
|
||||
<p>和彿如循環類似, if和switch語句也會在條件部分創建隱式塊, 還有它們對應的執行體塊. 下面的 if-else 測試鏈演示的 x 和 y 的作用域范圍:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">if</span> x := f(); x == <span class="hljs-number">0</span> {
|
||||
fmt.Println(x)
|
||||
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> y := g(x); x == y {
|
||||
@@ -2111,8 +2075,8 @@
|
||||
}
|
||||
fmt.Println(x, y) <span class="hljs-comment">// compile error: x and y are not visible here</span>
|
||||
</code></pre>
|
||||
<p>第二個if語句嵌套在第一個內部, 因此一個if語句條件塊聲明的變量在第二個if中也可以訪問. switch語句的每個分支也有類似的規則: 條件部分為一個隱式塊, 然後每個是每個分支的主體塊.</p>
|
||||
<p>在包級彆, 聲明的順序併不會影響作用域範圍, 因此一個先聲明的可以引用它自身或者是引用後麫的一個聲明, 這可以讓我們定義一些相互嵌套或遞歸的類型或函數. 但是如果一個變量或常量遞歸引用了自身, 則會產生編譯錯誤.</p>
|
||||
<p>第二個if語句嵌套在第一個內部, 因此一個if語句條件塊聲明的變量在第二個if中也可以訪問. switch語句的每個分支也有類似的規則: 條件部分爲一個隱式塊, 然後每個是每個分支的主體塊.</p>
|
||||
<p>在包級别, 聲明的順序併不會影響作用域范圍, 因此一個先聲明的可以引用它自身或者是引用後面的一個聲明, 這可以讓我們定義一些相互嵌套或遞歸的類型或函數. 但是如果一個變量或常量遞歸引用了自身, 則會産生編譯錯誤.</p>
|
||||
<p>在這個程序中:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">if</span> f, err := os.Open(fname); err != <span class="hljs-constant">nil</span> { <span class="hljs-comment">// compile error: unused: f</span>
|
||||
<span class="hljs-keyword">return</span> err
|
||||
@@ -2120,8 +2084,8 @@ fmt.Println(x, y) <span class="hljs-comment">// compile error: x and y are not v
|
||||
f.ReadByte() <span class="hljs-comment">// compile error: undefined f</span>
|
||||
f.Close() <span class="hljs-comment">// compile error: undefined f</span>
|
||||
</code></pre>
|
||||
<p>變量 f 的作用域隻有if語句內, 因此後麫的語句將無法引入它, 將導緻編譯錯誤. 你可能會收到一個侷部變量f沒有聲明的錯誤提示, 具體錯誤信息依賴編譯器的實現.</p>
|
||||
<p>通常需要在if之前聲明變量, 這樣可以確保後麫的語句依然可以訪問變量:</p>
|
||||
<p>變量 f 的作用域隻有if語句內, 因此後面的語句將無法引入它, 將導致編譯錯誤. 你可能會收到一個局部變量f沒有聲明的錯誤提示, 具體錯誤信息依賴編譯器的實現.</p>
|
||||
<p>通常需要在if之前聲明變量, 這樣可以確保後面的語句依然可以訪問變量:</p>
|
||||
<pre><code class="lang-Go">f, err := os.Open(fname)
|
||||
<span class="hljs-keyword">if</span> err != <span class="hljs-constant">nil</span> {
|
||||
<span class="hljs-keyword">return</span> err
|
||||
@@ -2139,7 +2103,7 @@ f.Close()
|
||||
}
|
||||
</code></pre>
|
||||
<p>但這不是Go推薦的做法, Go的習慣是在if中處理錯誤然後直接返迴, 這樣可以確保正常成功執行的語句不需要代碼縮進.</p>
|
||||
<p>要特彆註意短的變量聲明的作用域範圍, 考慮下麫的程序, 它的目的是穫取當前的工作目彔然後保存到一個包級的變量中. 這可以通過直接調用 os.Getwd 完成, 但是將這個從主邏輯中分離齣來可能會更好, 特彆是在需要處理錯誤的時候. 函數 log.Fatalf 打印信息, 然後調用 os.Exit(1) 終止程序.</p>
|
||||
<p>要特别註意短的變量聲明的作用域范圍, 考慮下面的程序, 它的目的是穫取當前的工作目録然後保存到一個包級的變量中. 這可以通過直接調用 os.Getwd 完成, 但是將這個從主邏輯中分離齣來可能會更好, 特别是在需要處理錯誤的時候. 函數 log.Fatalf 打印信息, 然後調用 os.Exit(1) 終止程序.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> cwd <span class="hljs-typename">string</span>
|
||||
|
||||
<span class="hljs-keyword">func</span> init() {
|
||||
@@ -2149,8 +2113,8 @@ f.Close()
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>雖然cwd在外部已經聲明過, 但是 <code>:=</code> 語句還是將 cwd 和 err 重新聲明為侷部變量. 內部聲明的 cwd 將屏蔽外部的聲明, 因此上麫的代碼併不會更新包級聲明的 cwd 變量.</p>
|
||||
<p>當前的編譯器將檢測到侷部聲明的cwd併沒有本使用, 然後報告這可能是一個錯誤, 但是這種檢測併不可靠. 一些小的代碼變更, 例如增加一個侷部cwd的打印語句, 就可能導緻這種檢測失效.</p>
|
||||
<p>雖然cwd在外部已經聲明過, 但是 <code>:=</code> 語句還是將 cwd 和 err 重新聲明爲局部變量. 內部聲明的 cwd 將屏蔽外部的聲明, 因此上面的代碼併不會更新包級聲明的 cwd 變量.</p>
|
||||
<p>當前的編譯器將檢測到局部聲明的cwd併沒有本使用, 然後報告這可能是一個錯誤, 但是這種檢測併不可靠. 一些小的代碼變更, 例如增加一個局部cwd的打印語句, 就可能導致這種檢測失效.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> cwd <span class="hljs-typename">string</span>
|
||||
|
||||
<span class="hljs-keyword">func</span> init() {
|
||||
@@ -2161,7 +2125,7 @@ f.Close()
|
||||
log.Printf(<span class="hljs-string">"Working directory = %s"</span>, cwd)
|
||||
}
|
||||
</code></pre>
|
||||
<p>全侷的cwd變量依然是沒有被正確初始化的, 而且看似正常的日誌輸齣更是這個BUG更加隱晦.</p>
|
||||
<p>全局的cwd變量依然是沒有被正確初始化的, 而且看似正常的日誌輸齣更是這個BUG更加隱晦.</p>
|
||||
<p>有許多方式可以避免齣現類似潛在的問題. 最直接的是通過單獨聲明err變量, 來避免使用 <code>:=</code> 的簡短聲明方式:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> cwd <span class="hljs-typename">string</span>
|
||||
|
||||
@@ -2173,8 +2137,8 @@ f.Close()
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>我們已經看到包, 文件, 聲明和語句如何來錶達一個程序結構. 在下麫的兩個章節, 我們將探討數據的結構.</p>
|
||||
<p><strong>譯註: 本章的詞法域和作用域概唸有些混淆, 需要重譯一遍.</strong></p>
|
||||
<p>我們已經看到包, 文件, 聲明和語句如何來表達一個程序結構. 在下面的兩個章節, 我們將探討數據的結構.</p>
|
||||
<p><strong>譯註: 本章的詞法域和作用域概念有些混淆, 需要重譯一遍.</strong></p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
96
ch2/ch2.html
96
ch2/ch2.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="2" data-chapter-title="程序結構" data-filepath="ch2/ch2.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="2" data-chapter-title="程序結構" data-filepath="ch2/ch2.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,8 +2024,8 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h1 id="第2章-程序結構">第2章 程序結構</h1>
|
||||
<p>Go語言和任何其他語言一樣, 一個大的程序是有很多小的基礎構件組成的. 變量保存值. 簡單的加法和減法運算被組閤成較大的錶達式. 基礎類型被聚閤為數組或結構體. 然後使用if和for之類的控製語句來組織和控製錶達式的執行順序. 然後多個語句被組織到函數中, 以便代碼的隔離和復用. 函數以源文件和包的方式組織.</p>
|
||||
<p>我們已經在前麫的章節的例子中看到了大部分的例子. 在本章中, 我們將深入討論Go程序的基礎結構的一些細節. 每個示例程序都是刻意寫的簡單, 這樣我們可以減少被復雜的算法和數據結構所幹擾, 從而專註於語言本身的學習. </p>
|
||||
<p>Go語言和任何其他語言一樣, 一個大的程序是有很多小的基礎構件組成的. 變量保存值. 簡單的加法和減法運算被組合成較大的表達式. 基礎類型被聚合爲數組或結構體. 然後使用if和for之類的控製語句來組織和控製表達式的執行順序. 然後多個語句被組織到函數中, 以便代碼的隔離和複用. 函數以源文件和包的方式組織.</p>
|
||||
<p>我們已經在前面的章節的例子中看到了大部分的例子. 在本章中, 我們將深入討論Go程序的基礎結構的一些細節. 每個示例程序都是刻意寫的簡單, 這樣我們可以減少被複雜的算法和數據結構所榦擾, 從而專註於語言本身的學習. </p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
209
ch3/ch3-01.html
209
ch3/ch3-01.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3.1" data-chapter-title="整型" data-filepath="ch3/ch3-01.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3.1" data-chapter-title="整型" data-filepath="ch3/ch3-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,7 +2024,122 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="31-整型">3.1. 整型</h2>
|
||||
<p>TODO</p>
|
||||
<p>Go語言的數值類型包括幾種不同大小的整形數, 浮點數, 和複數. 每種數值類型都決定了對應的大小范圍和是否有正負符號. 讓我們先從整形數類型開始介紹.</p>
|
||||
<p>Go同時提供了有符號和無符號的整數運算. 這里有四種int8, int16, int32 和 int64截然不同大小的有符號整形數類型, 分别對應 8, 16, 32, 64 bit 大小的有符號整形數, 與此對應的是 uint8, uint16, uint32, 和 uint64 四種無符號整形數類型.</p>
|
||||
<p>這里還有兩種對應特定平颱最天然或最有效率的大小有符號和無符號整數int和uint; 其中int是應用最廣泛的數值類型. 這兩種類型都有同樣的大小, 32 或 64 bit, 但是我們不能對此做任何的假設; 因爲不同的編譯器在相同的硬件平颱上可能産生不同的大小.</p>
|
||||
<p>字符rune類型是和int32等價的類型, 通常用於表示一個Unicode碼點. 這兩個名稱可以互換使用. 同樣byte也是uint8類型的等價類型, byte類型用於強調數值是一個原始的數據而不是一個小的整數.</p>
|
||||
<p>最好, 還有一個無符號的整數類型 uintptr, 沒有指定具體的bit大小但是足以容納指針. uintptr 類型隻有在底層編程是纔需要, 特别是Go語言和C函數庫或操作繫統相交互的地方. 我們將在第十三章的 unsafe 包相關部分看到類似的例子.</p>
|
||||
<p>不管它們的大小, int, uint, 和 uintptr 是不同類型大小的兄弟類型. 其中 int 和 int32 也是不同的類型, 卽使int的大小也是32bit, 在需要將int當作int32類型的地方需要一個顯式的類型轉換, 反之亦然.</p>
|
||||
<p>有符號數采用2的補碼形式表示, 也就是最高位用作符號位, 一個nbit的有符號數的值域是 <code>-2^(n-1)</code> 到 <code>(2^(n-1)) - 1</code>. 無符號整數的所有bit位都用於表示非負數, 值域是 0 到 <code>(2^n) - 1</code>. 例如, int8 的值域是 -128 到 127, 而 uint8 的值域是 0 到 255.</p>
|
||||
<p>下面是Go中關於算術, 邏輯和比較的二元運算符按照先級遞減的順序的列表:</p>
|
||||
<pre><code>* / % << >> & &^
|
||||
+ - | ^
|
||||
== != < <= > >=
|
||||
&&
|
||||
||
|
||||
</code></pre><p>二元運算符有五種優先級. 在同一優先級, 使用左優先結合律, 使用括號可以明確優先順序, 括號也可以用於提陞優先級, 例如 <code>mask & (1 << 28)</code>.</p>
|
||||
<p>對於上表中前兩行的運算符, 例如 + 有一個相應的賦值結合運算符 +=, 可以用於簡化賦值語句.</p>
|
||||
<p>整數的算術運算符 +, -, *, 和 / 可以適用與整數, 浮點數和複數, 但是取模運算符 % 僅用於整數. 不同編程語言間, % 取模運算的行爲併不相同. 在Go語言中, % 取模運算符的符號和被取模數的符號總是一致的, 因此 <code>-5%3</code> 和 <code>-5%-3</code> 結果都是 -2.除法運算符 <code>/</code> 的行爲依賴於操作數是否爲整數, 因此 <code>5.0/4.0</code> 的結果是 1.25, 但是 5/4 的結果是 1, 因此整數除法會向着0方向截斷餘數.</p>
|
||||
<p>如果一個算術運算的結果, 不管是有符號或者是無符號的, 如果需要更多的bit位纔能表示, 就説明是溢齣了. 超齣的高位的bit位部分將被丟棄. 如果原始的數值是有符號類型, 那麽最終結果可能是負的, 如果最左邊的bit爲是1的話, 例如int8的例子:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> u <span class="hljs-typename">uint8</span> = <span class="hljs-number">255</span>
|
||||
fmt.Println(u, u+<span class="hljs-number">1</span>, u*u) <span class="hljs-comment">// "255 0 1"</span>
|
||||
|
||||
<span class="hljs-keyword">var</span> i <span class="hljs-typename">int8</span> = <span class="hljs-number">127</span>
|
||||
fmt.Println(i, i+<span class="hljs-number">1</span>, i*i) <span class="hljs-comment">// "127 -128 1"</span>
|
||||
</code></pre>
|
||||
<p>兩個相同的整數類型可以使用下面的二元比較運算符進行比較; 比較表達式的結果是布爾類型.</p>
|
||||
<pre><code>== equal to
|
||||
!= not equal to
|
||||
< less than
|
||||
<= less than or equal to
|
||||
> greater than
|
||||
>= greater than or equal to
|
||||
</code></pre><p>事實上, 布爾型, 數字類型 和 字符串 等基本類型都是可比較的, 也就是説兩個相同類型的值可以用 == 和 != 進行比較. 此外, 整數, 浮點數和字符串可以根據比較結果排序. 許多其他類型的值是不可比較, 因此也就是不可排序的. 對於我們遇到的每種類型, 我們需要保證規則是類似的.</p>
|
||||
<p>這里是一元的加法和減法運算符:</p>
|
||||
<pre><code>+ 一元加法 (無效果)
|
||||
- 負數
|
||||
</code></pre><p>對於整數, +x 是 0+x 的簡寫, -x 是 0-x 的簡寫; 對於浮點數和複數, +x 就是 x, -x 則是 x 的負數.</p>
|
||||
<p>Go語言還提供了以下的bit位操作運算符, 前面4個操作運算符併不區分是有符號還是無符號數:</p>
|
||||
<pre><code>& 位運算 AND
|
||||
| 位運算 OR
|
||||
^ 位運算 XOR
|
||||
&^ 位清空 (AND NOT)
|
||||
<< 左移
|
||||
>> 右移
|
||||
</code></pre><p>位操作運算符 <code>^</code> 作爲二元運算符時是按位異或(XOR), 當用作一元運算符時表示按位取反; 也就是説, 它返迴一個每個bit位都取反的數. 位操作運算符 <code>&^</code> 用於按位置零(AND NOT): 表達式 <code>z = x &^ y</code> 結果z的bit位1, 如果對應y中bit位爲1, 否則對應的bit位等於x相應的bit位的值.</p>
|
||||
<p>下面的代碼演示了如何使用位操作解釋uint8類型值的8個獨立的bit位. 它使用了 Printf 函數的 %b 參數打印二進製格式的數字; 其中 %08b 中08表示打印至少8個數字, 不足的前綴用0填充.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> x <span class="hljs-typename">uint8</span> = <span class="hljs-number">1</span><<<span class="hljs-number">1</span> | <span class="hljs-number">1</span><<<span class="hljs-number">5</span>
|
||||
<span class="hljs-keyword">var</span> y <span class="hljs-typename">uint8</span> = <span class="hljs-number">1</span><<<span class="hljs-number">1</span> | <span class="hljs-number">1</span><<<span class="hljs-number">2</span>
|
||||
|
||||
fmt.Printf(<span class="hljs-string">"%08b\n"</span>, x) <span class="hljs-comment">// "00100010", the set {1, 5}</span>
|
||||
fmt.Printf(<span class="hljs-string">"%08b\n"</span>, y) <span class="hljs-comment">// "00000110", the set {1, 2}</span>
|
||||
|
||||
fmt.Printf(<span class="hljs-string">"%08b\n"</span>, x&y) <span class="hljs-comment">// "00000010", the intersection {1}</span>
|
||||
fmt.Printf(<span class="hljs-string">"%08b\n"</span>, x|y) <span class="hljs-comment">// "00100110", the union {1, 2, 5}</span>
|
||||
fmt.Printf(<span class="hljs-string">"%08b\n"</span>, x^y) <span class="hljs-comment">// "00100100", the symmetric difference {2, 5}</span>
|
||||
fmt.Printf(<span class="hljs-string">"%08b\n"</span>, x&^y) <span class="hljs-comment">// "00100000", the difference {5}</span>
|
||||
|
||||
<span class="hljs-keyword">for</span> i := <span class="hljs-typename">uint</span>(<span class="hljs-number">0</span>); i < <span class="hljs-number">8</span>; i++ {
|
||||
<span class="hljs-keyword">if</span> x&(<span class="hljs-number">1</span><<i) != <span class="hljs-number">0</span> { <span class="hljs-comment">// membership test</span>
|
||||
fmt.Println(i) <span class="hljs-comment">// "1", "5"</span>
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf(<span class="hljs-string">"%08b\n"</span>, x<<<span class="hljs-number">1</span>) <span class="hljs-comment">// "01000100", the set {2, 6}</span>
|
||||
fmt.Printf(<span class="hljs-string">"%08b\n"</span>, x>><span class="hljs-number">1</span>) <span class="hljs-comment">// "00010001", the set {0, 4}</span>
|
||||
</code></pre>
|
||||
<p>(6.5節給齣了一個可以遠大於一個字節的整數集的實現.)</p>
|
||||
<p>在 x<<n 和="" x="">>n 移位運算中, 決定了移位操作bit數部分必鬚是無符號數; 被操作的 x 數可以是有符號或無符號數. 算術上, 一個 x<<n 左移運算等價於乘以="" 2^n,="" 一個="" x="">>n 右移運算等價於除以 2^n.</n></n></p>
|
||||
<p>左移運算用零填充右邊空缺的bit位, 無符號數的右移運算也是用0填充左邊空缺的bit位, 但是有符號數的右移運算會用符號位的值填充左邊空缺的bit位. 因爲這個原因, 最好用無符號運算, 這樣你可以將整數完全當作一個bit位模式處理.</p>
|
||||
<p>盡管Go提供了無符號數和運算, 卽使數值本身不可能齣現負數我們還是傾向於使用有符號的int類型, 就是數組的長度那樣, 雖然使用 uint 似乎是一個更合理的選擇. 事實上, 內置的 len 函數返迴一個有符號的int, 我們可以像下面這個逆序循環那樣處理.</p>
|
||||
<pre><code class="lang-Go">medals := []<span class="hljs-typename">string</span>{<span class="hljs-string">"gold"</span>, <span class="hljs-string">"silver"</span>, <span class="hljs-string">"bronze"</span>}
|
||||
<span class="hljs-keyword">for</span> i := <span class="hljs-built_in">len</span>(medals) - <span class="hljs-number">1</span>; i >= <span class="hljs-number">0</span>; i-- {
|
||||
fmt.Println(medals[i]) <span class="hljs-comment">// "bronze", "silver", "gold"</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>另一個選擇將是災難性的. 如果 len 返迴一個無符號數, 那麽 i 也將是無符號的 uint, 然後條件 i >= 0 則永遠爲眞. 在三次迭代之後, 也就是 i == 0 時, i-- 語句將不會産生 -1, 而是變成一個uint的最大值(可能是 2^64 - 1), 然後 medals[i] 表達式將發生運行時 panic 異常(§5.9), 也就是試圖訪問一個切片范圍以外的元素.</p>
|
||||
<p>齣於這個原因, 無符號數往往隻有在位運算或其它特殊的運算常見纔會使用, 就像 bit 集合, 分形二進製文件格式, 或者是哈希和加密操作等. 它們通常併不用於僅僅是表達非負數量的場合.</p>
|
||||
<p>一般來説, 需要一個顯式的轉換將一個值從一種類型轉化位另一種類型, 併且算術和邏輯運算的二元操作中必鬚是相同的類型. 雖然這偶爾會導致很長的表達式, 但是它消除了所有的類型相關的問題, 也使得程序容易理解.</p>
|
||||
<p>從其他類似場景下, 考慮下面這個代碼:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> apples <span class="hljs-typename">int32</span> = <span class="hljs-number">1</span>
|
||||
<span class="hljs-keyword">var</span> oranges <span class="hljs-typename">int16</span> = <span class="hljs-number">2</span>
|
||||
<span class="hljs-keyword">var</span> compote <span class="hljs-typename">int</span> = apples + oranges <span class="hljs-comment">// compile error</span>
|
||||
</code></pre>
|
||||
<p>當嚐試編譯這三個語句時, 將産生一個錯誤信息:</p>
|
||||
<pre><code>invalid operation: apples + oranges (mismatched types int32 and int16)
|
||||
</code></pre><p>這種類型不匹配的問題可以有幾種不同的方法脩複, 最常見方法是將它們都顯式轉型位一個常見類型:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> compote = <span class="hljs-typename">int</span>(apples) + <span class="hljs-typename">int</span>(oranges)
|
||||
</code></pre>
|
||||
<p>如2.5節所述, 對於每種類型T, 類型轉換操作T(x)將x轉換位T類型, 如果轉換允許的話. 許多 整形數之間的相互轉換併不會改變數值; 它們隻是告訴編譯器如何解釋這個值. 但是對於將一個大尺寸的整數類型轉位一個小尺寸的整數類型, 或者是將一個浮點數轉位整數, 可能會改變數值或丟失精度:</p>
|
||||
<pre><code class="lang-Go">f := <span class="hljs-number">3.141</span> <span class="hljs-comment">// a float64</span>
|
||||
i := <span class="hljs-typename">int</span>(f)
|
||||
fmt.Println(f, i) <span class="hljs-comment">// "3.141 3"</span>
|
||||
f = <span class="hljs-number">1.99</span>
|
||||
fmt.Println(<span class="hljs-typename">int</span>(f)) <span class="hljs-comment">// "1"</span>
|
||||
</code></pre>
|
||||
<p>浮點數到整數的轉換將丟失任何小數部分, 向數軸零方向截斷. 你應該避免操作目標類型表示范圍的數值類型轉換, 因爲截斷的行爲依賴於具體的實現:</p>
|
||||
<pre><code class="lang-Go">f := <span class="hljs-number">1e100</span> <span class="hljs-comment">// a float64</span>
|
||||
i := <span class="hljs-typename">int</span>(f) <span class="hljs-comment">// 結果依賴於具體實現</span>
|
||||
</code></pre>
|
||||
<p>任何大小的整數字面值都可以用以0開始的八進製格式書寫, 例如 0666, 或用以0x或0X開頭的十六進製格式書寫, 例如 0xdeadbeef. 十六進製數字可以用大寫或小寫字母. 如今八進製數據通常用於POSIX操作繫統上的文件訪問權限標誌, 十六進製數字則更強調數字值的bit位模式.</p>
|
||||
<p>當使用 fmt 包打印一個數值時, 我們可以用 %d, %o, 或 %x 控製輸齣的進製格式, 就像下面的例子:</p>
|
||||
<pre><code class="lang-Go">o := <span class="hljs-number">0666</span>
|
||||
fmt.Printf(<span class="hljs-string">"%d %[1]o %#[1]o\n"</span>, o) <span class="hljs-comment">// "438 666 0666"</span>
|
||||
x := <span class="hljs-typename">int64</span>(<span class="hljs-number">0xdeadbeef</span>)
|
||||
fmt.Printf(<span class="hljs-string">"%d %[1]x %#[1]x %#[1]X\n"</span>, x)
|
||||
<span class="hljs-comment">// Output:</span>
|
||||
<span class="hljs-comment">// 3735928559 deadbeef 0xdeadbeef 0XDEADBEEF</span>
|
||||
</code></pre>
|
||||
<p>請註意 fmt 的兩個使用技巧. 通常 Printf 格式化字符串包含多個 % 參數時將對應相同數量的額外操作數, 但是 % 之後的 <code>[1]</code> 副詞告訴Printf函數再次使用第一個操作數. 第二, % 後的 <code>#</code> 副詞告訴 Printf 在用 %o, %x 或 %X 輸齣時生成 0, 0x 或 0X前綴.</p>
|
||||
<p>字符面值通過一對單引號直接包含對應字符. 最簡單的例子是 ASCII 中類似 'a' 字符面值, 但是我們可以通過轉義的數值來表示任意的Unicode碼點對應的字符, 馬上將會看到例子.</p>
|
||||
<p>字符使用 <code>%c</code> 參數打印, 或者是 <code>%q</code> 參數打印帶單引號的字符:</p>
|
||||
<pre><code class="lang-Go">ascii := <span class="hljs-string">'a'</span>
|
||||
unicode := <span class="hljs-string">'國'</span>
|
||||
newline := <span class="hljs-string">'\n'</span>
|
||||
fmt.Printf(<span class="hljs-string">"%d %[1]c %[1]q\n"</span>, ascii) <span class="hljs-comment">// "97 a 'a'"</span>
|
||||
fmt.Printf(<span class="hljs-string">"%d %[1]c %[1]q\n"</span>, unicode) <span class="hljs-comment">// "22269 國 '國'"</span>
|
||||
fmt.Printf(<span class="hljs-string">"%d %[1]q\n"</span>, newline) <span class="hljs-comment">// "10 '\n'"</span>
|
||||
</code></pre>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
211
ch3/ch3-02.html
211
ch3/ch3-02.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3.2" data-chapter-title="浮點數" data-filepath="ch3/ch3-02.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3.2" data-chapter-title="浮點數" data-filepath="ch3/ch3-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,7 +2024,122 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="32-浮點數">3.2. 浮點數</h2>
|
||||
<p>TODO</p>
|
||||
<p>Go語言提供了兩種精度的浮點數, float32 和 float64. 它們的算術規范由 IEEE754 國際標準定義, 該浮點數規范被所有現代的CPU支持.</p>
|
||||
<p>這些數值類型的范圍可以從很微小到很鉅大. 浮點數的范圍極限值可以在 matn 包找到. 常量 math.MaxFloat32 表示 float32 能表示的最大數值, 大約是 3.4e38, 對應的 math.MaxFloat64 常量大約是 1.8e308. 它們能表示的最小值近似分别是1.4e-45 和 4.9e-324.</p>
|
||||
<p>一個 float32 類型的浮點數可以提供大約6個十進製數的精度, 而 float64 則可以提供約 15個十進製數精度; 通常應該優先使用 float64 類型, 因爲 float32 類型的纍計計算誤差很容易擴散, 併且 float32 能精度表示的正整數併不是很大:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> f <span class="hljs-typename">float32</span> = <span class="hljs-number">16777216</span> <span class="hljs-comment">// 1 << 24</span>
|
||||
fmt.Println(f == f+<span class="hljs-number">1</span>) <span class="hljs-comment">// "true"!</span>
|
||||
</code></pre>
|
||||
<p>浮點數的字面值可以直接寫小數部分, 想這樣:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> e = <span class="hljs-number">2.71828</span> <span class="hljs-comment">// (approximately)</span>
|
||||
</code></pre>
|
||||
<p>小數點前面或後面的數字都可能被省略(例如 .707 或 1.). 很小或很大的數最好用科學計數法書寫, 通過e或E來指定指數部分:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> Avogadro = <span class="hljs-number">6.02214129e23</span>
|
||||
<span class="hljs-keyword">const</span> Planck = <span class="hljs-number">6.62606957e-34</span>
|
||||
</code></pre>
|
||||
<p>用 Printf 函數的 %g 參數打印浮點數, 將采用緊湊的表示形式打印, 併提供足夠的精度, 但是對應表格的數據, 使用 %e (帶指數) 或 %f 的形式打印可能更合適. 所有的這三個打印形式都可以指定打印的寬度和控製打印精度.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">for</span> x := <span class="hljs-number">0</span>; x < <span class="hljs-number">8</span>; x++ {
|
||||
fmt.Printf(<span class="hljs-string">"x = %d e^x = %8.3f\n"</span>, x, math.Exp(<span class="hljs-typename">float64</span>(x)))
|
||||
}
|
||||
</code></pre>
|
||||
<p>上面代碼打印e的冪, 打印精度是小數點後三個小數精度和8個字符寬度:</p>
|
||||
<pre><code>x = 0 e^x = 1.000
|
||||
x = 1 e^x = 2.718
|
||||
x = 2 e^x = 7.389
|
||||
x = 3 e^x = 20.086
|
||||
x = 4 e^x = 54.598
|
||||
x = 5 e^x = 148.413
|
||||
x = 6 e^x = 403.429
|
||||
x = 7 e^x = 1096.633
|
||||
</code></pre><p>math 包中除了提供大量常用的數學函數外, 還提供了IEEE754標準中特殊數值的創建和測試: 正無窮大和負無窮大, 分别用於表示太大溢齣的數字和除零的結果; 還有 NaN 非數, 一般用於表示無效的除法操作結果 0/0 或 Sqrt(-1).</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> z <span class="hljs-typename">float64</span>
|
||||
fmt.Println(z, -z, <span class="hljs-number">1</span>/z, -<span class="hljs-number">1</span>/z, z/z) <span class="hljs-comment">// "0 -0 +Inf -Inf NaN"</span>
|
||||
</code></pre>
|
||||
<p>函數 math.IsNaN 用於測試一個數是否是非數 NaN, math.NaN 則返迴非數對應的值. 雖然可以用 math.NaN 來表示一個非法的結果, 但是測試一個結果是否是非數 NaN 則是充滿風險, 因爲 NaN 和任何數都是不相等的:</p>
|
||||
<pre><code class="lang-Go">nan := math.NaN()
|
||||
fmt.Println(nan == nan, nan < nan, nan > nan) <span class="hljs-comment">// "false false false"</span>
|
||||
</code></pre>
|
||||
<p>如果一個函數返迴的浮點數結果可能失敗, 最好的做法是用單獨的標誌報告失敗, 像這樣:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> compute() (value <span class="hljs-typename">float64</span>, ok <span class="hljs-typename">bool</span>) {
|
||||
<span class="hljs-comment">// ...</span>
|
||||
<span class="hljs-keyword">if</span> failed {
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>, <span class="hljs-constant">false</span>
|
||||
}
|
||||
<span class="hljs-keyword">return</span> result, <span class="hljs-constant">true</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>接下來的程序演示了浮點計算圖形. 它是帶有兩個參數的 z = f(x, y) 函數的三維形式, 使用了可縮放矢量圖形(SVG)格式輸齣, 一個用於矢量線繪製的XML標準. 圖3.1顯示了 sin(r)/r 函數的輸齣圖形, 其中 r 是 sqrt(x<em>x+y</em>y).</p>
|
||||
<p><img src="../images/ch3-01.png" alt=""></p>
|
||||
<pre><code class="lang-Go">gopl.io/ch3/surface
|
||||
<span class="hljs-comment">// Surface computes an SVG rendering of a 3-D surface function.</span>
|
||||
<span class="hljs-keyword">package</span> main
|
||||
|
||||
<span class="hljs-keyword">import</span> (
|
||||
<span class="hljs-string">"fmt"</span>
|
||||
<span class="hljs-string">"math"</span>
|
||||
)
|
||||
|
||||
<span class="hljs-keyword">const</span> (
|
||||
width, height = <span class="hljs-number">600</span>, <span class="hljs-number">320</span> <span class="hljs-comment">// canvas size in pixels</span>
|
||||
cells = <span class="hljs-number">100</span> <span class="hljs-comment">// number of grid cells</span>
|
||||
xyrange = <span class="hljs-number">30.0</span> <span class="hljs-comment">// axis ranges (-xyrange..+xyrange)</span>
|
||||
xyscale = width / <span class="hljs-number">2</span> / xyrange <span class="hljs-comment">// pixels per x or y unit</span>
|
||||
zscale = height * <span class="hljs-number">0.4</span> <span class="hljs-comment">// pixels per z unit</span>
|
||||
angle = math.Pi / <span class="hljs-number">6</span> <span class="hljs-comment">// angle of x, y axes (=30°)</span>
|
||||
)
|
||||
|
||||
<span class="hljs-keyword">var</span> sin30, cos30 = math.Sin(angle), math.Cos(angle) <span class="hljs-comment">// sin(30°), cos(30°)</span>
|
||||
|
||||
<span class="hljs-keyword">func</span> main() {
|
||||
fmt.Printf(<span class="hljs-string">"<svg xmlns='http://www.w3.org/2000/svg' "</span>+
|
||||
<span class="hljs-string">"style='stroke: grey; fill: white; stroke-width: 0.7' "</span>+
|
||||
<span class="hljs-string">"width='%d' height='%d'>"</span>, width, height)
|
||||
<span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i < cells; i++ {
|
||||
<span class="hljs-keyword">for</span> j := <span class="hljs-number">0</span>; j < cells; j++ {
|
||||
ax, ay := corner(i+<span class="hljs-number">1</span>, j)
|
||||
bx, by := corner(i, j)
|
||||
cx, cy := corner(i, j+<span class="hljs-number">1</span>)
|
||||
dx, dy := corner(i+<span class="hljs-number">1</span>, j+<span class="hljs-number">1</span>)
|
||||
fmt.Printf(<span class="hljs-string">"<polygon points='%g,%g %g,%g %g,%g %g,%g'/>\n"</span>,
|
||||
ax, ay, bx, by, cx, cy, dx, dy)
|
||||
}
|
||||
}
|
||||
fmt.Println(<span class="hljs-string">"</svg>"</span>)
|
||||
}
|
||||
|
||||
<span class="hljs-keyword">func</span> corner(i, j <span class="hljs-typename">int</span>) (<span class="hljs-typename">float64</span>, <span class="hljs-typename">float64</span>) {
|
||||
<span class="hljs-comment">// Find point (x,y) at corner of cell (i,j).</span>
|
||||
x := xyrange * (<span class="hljs-typename">float64</span>(i)/cells - <span class="hljs-number">0.5</span>)
|
||||
y := xyrange * (<span class="hljs-typename">float64</span>(j)/cells - <span class="hljs-number">0.5</span>)
|
||||
|
||||
<span class="hljs-comment">// Compute surface height z.</span>
|
||||
z := f(x, y)
|
||||
|
||||
<span class="hljs-comment">// Project (x,y,z) isometrically onto 2-D SVG canvas (sx,sy).</span>
|
||||
sx := width/<span class="hljs-number">2</span> + (x-y)*cos30*xyscale
|
||||
sy := height/<span class="hljs-number">2</span> + (x+y)*sin30*xyscale - z*zscale
|
||||
<span class="hljs-keyword">return</span> sx, sy
|
||||
}
|
||||
|
||||
<span class="hljs-keyword">func</span> f(x, y <span class="hljs-typename">float64</span>) <span class="hljs-typename">float64</span> {
|
||||
r := math.Hypot(x, y) <span class="hljs-comment">// distance from (0,0)</span>
|
||||
<span class="hljs-keyword">return</span> math.Sin(r) / r
|
||||
}
|
||||
</code></pre>
|
||||
<p>要註意的是 corner 返迴了兩個結果, 對應 corner 的坐標參數.</p>
|
||||
<p>要解釋程序是如何工作的需要了解基本的幾何知識, 但是我們可以跳過幾何原理, 因爲程序的重點是演示浮點運算. 程序的本質是三個不同的坐標繫中映射關繫, 如圖3.2所示. 第一個是 100x100 的二維網格, 對應整數整數坐標(i,j), 從遠處的 (0, 0) 位置開始. 我們從遠處像前面繪製, 因此遠處先繪製的多邊形有可能被前面後繪製的多邊形覆蓋.</p>
|
||||
<p>第二個坐標繫是一個三維的網格浮點坐標(x,y,z), 其中x和y是i和j的線性函數, 通過平移轉換位center的中心, 然後用xyrange繫數縮放. 高度z是函數f(x,y)的值.</p>
|
||||
<p>第三個坐標繫是一個二維的畵布, 起點(0,0)在左上角. 畵布中點的坐標用(sx, sy)表示. 我們使用等角投影將三維點</p>
|
||||
<p><img src="../images/ch3-02.png" alt=""></p>
|
||||
<p>(x,y,z) 投影到二維的畵布中. 畵布中從遠處到右邊的點對應較大的x值和較大的y值. 併且畵布中x和y值越大, 則對應的z值越小. x和y的垂直和水平縮放繫數來自30度角的正絃和餘絃值. z的縮放繫數0.4, 是一個任意選擇的參數.</p>
|
||||
<p>對於二維網格中的每一個單位, main函數計算單元的四個頂點在畵布中對應多邊形ABCD的頂點, 其中B對應(i,j)頂點位置, A, C, 和 D是相鄰的頂點, 然後輸齣SVG的繪製指令.</p>
|
||||
<p><strong>練習3.1:</strong> 如果 f 函數返迴的是無限製的 float64 值, 那麽SVG文件可能輸齣無效的<polygon></polygon>多邊形元素(雖然許多SVG渲染器會妥善處理這類問題). 脩改程序跳過無效的多邊形.</p>
|
||||
<p><strong>練習3.2:</strong> 試驗math包中其他函數的渲染圖形. 你是否能輸齣一個egg box, moguls, 或 a saddle 圖案?</p>
|
||||
<p><strong>練習3.3:</strong>根據高度給每個多邊形上色, 那樣峯值部將是紅色(#ff0000), 谷部將是藍色(#0000ff).</p>
|
||||
<p><strong>3.4:</strong> 參考1.7節Lissajous例子的函數, 構造一個web服務器, 用於計算函數麴面然後返迴SVG數據給客戶端. 服務器必鬚設置 Content-Type 頭部:</p>
|
||||
<pre><code class="lang-Go">w.Header().Set(<span class="hljs-string">"Content-Type"</span>, <span class="hljs-string">"image/svg+xml"</span>)
|
||||
</code></pre>
|
||||
<p>(這一步在Lissajous例子中不是必鬚的, 因爲服務器使用標準的PNG圖像格式, 可以根據前面的512個字節自動輸齣對應的頭部.) 允許客戶端通過HTTP請求參數設置高度, 寬度, 和顔色等參數.</p>
|
||||
|
||||
|
||||
</section>
|
||||
@@ -2074,7 +2153,7 @@
|
||||
<a href="../ch3/ch3-01.html" class="navigation navigation-prev " aria-label="Previous page: 整型"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../ch3/ch3-03.html" class="navigation navigation-next " aria-label="Next page: 復數"><i class="fa fa-angle-right"></i></a>
|
||||
<a href="../ch3/ch3-03.html" class="navigation navigation-next " aria-label="Next page: 複數"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
172
ch3/ch3-03.html
172
ch3/ch3-03.html
@@ -5,7 +5,7 @@
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>復數 | Go编程语言</title>
|
||||
<title>複數 | Go编程语言</title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="generator" content="GitBook 2.5.2">
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3.3" data-chapter-title="復數" data-filepath="ch3/ch3-03.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3.3" data-chapter-title="複數" data-filepath="ch3/ch3-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2059,8 +2023,80 @@
|
||||
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="33-復數">3.3. 復數</h2>
|
||||
<p>TODO</p>
|
||||
<h2 id="33-複數">3.3. 複數</h2>
|
||||
<p>Go提供了兩種精度的複數類似, complex64 和 complex128, 分别對應 float32 和 float64精度. 內置的 complex 函數用於構建複數, 內建的 real 和 imag 函數返迴複數的實部和虛部:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> x <span class="hljs-typename">complex128</span> = <span class="hljs-built_in">complex</span>(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>) <span class="hljs-comment">// 1+2i</span>
|
||||
<span class="hljs-keyword">var</span> y <span class="hljs-typename">complex128</span> = <span class="hljs-built_in">complex</span>(<span class="hljs-number">3</span>, <span class="hljs-number">4</span>) <span class="hljs-comment">// 3+4i</span>
|
||||
fmt.Println(x*y) <span class="hljs-comment">// "(-5+10i)"</span>
|
||||
fmt.Println(<span class="hljs-built_in">real</span>(x*y)) <span class="hljs-comment">// "-5"</span>
|
||||
fmt.Println(<span class="hljs-built_in">imag</span>(x*y)) <span class="hljs-comment">// "10"</span>
|
||||
</code></pre>
|
||||
<p>如果一個浮點數面值或一個十進製整數面值後面跟着一個i, 例如 3.141592i 或 2i, 它將構成一個複數的虛部, 複數的實部是0:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(<span class="hljs-number">1i</span> * <span class="hljs-number">1i</span>) <span class="hljs-comment">// "(-1+0i)", i^2 = -1</span>
|
||||
</code></pre>
|
||||
<p>在常量算術規則下, 一個複數常量可以加到另一個常量(整數或浮點數, 實部或虛部), 我們可以用自然的方式寫複數, 就像 1+2i, 或與之等價的寫法 2i+1. 上面x和y的聲明語句還可以簡化:</p>
|
||||
<pre><code class="lang-Go">x := <span class="hljs-number">1</span> + <span class="hljs-number">2i</span>
|
||||
y := <span class="hljs-number">3</span> + <span class="hljs-number">4i</span>
|
||||
</code></pre>
|
||||
<p>複數也可以用 == 和 != 進行相等比較. 隻有兩個複數的實部和虛部都相等的時候它們纔是相等的.</p>
|
||||
<p>math/cmplx 包提供了複數處理的許多函數, 例如求複數的平方根函數和求冪函數.</p>
|
||||
<pre><code class="lang-Go">fmt.Println(cmplx.Sqrt(-<span class="hljs-number">1</span>)) <span class="hljs-comment">// "(0+1i)"</span>
|
||||
</code></pre>
|
||||
<p>下面的程序使用complex128複數算法來生成一個Mandelbrot圖像.</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch3/mandelbrot
|
||||
|
||||
<span class="hljs-comment">// Mandelbrot emits a PNG image of the Mandelbrot fractal.</span>
|
||||
<span class="hljs-keyword">package</span> main
|
||||
|
||||
<span class="hljs-keyword">import</span> (
|
||||
<span class="hljs-string">"image"</span>
|
||||
<span class="hljs-string">"image/color"</span>
|
||||
<span class="hljs-string">"image/png"</span>
|
||||
<span class="hljs-string">"math/cmplx"</span>
|
||||
<span class="hljs-string">"os"</span>
|
||||
)
|
||||
|
||||
|
||||
<span class="hljs-keyword">func</span> main() {
|
||||
<span class="hljs-keyword">const</span> (
|
||||
xmin, ymin, xmax, ymax = -<span class="hljs-number">2</span>, -<span class="hljs-number">2</span>, +<span class="hljs-number">2</span>, +<span class="hljs-number">2</span>
|
||||
width, height = <span class="hljs-number">1024</span>, <span class="hljs-number">1024</span>
|
||||
)
|
||||
|
||||
img := image.NewRGBA(image.Rect(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, width, height))
|
||||
<span class="hljs-keyword">for</span> py := <span class="hljs-number">0</span>; py < height; py++ {
|
||||
y := <span class="hljs-typename">float64</span>(py)/height*(ymax-ymin) + ymin
|
||||
<span class="hljs-keyword">for</span> px := <span class="hljs-number">0</span>; px < width; px++ {
|
||||
x := <span class="hljs-typename">float64</span>(px)/width*(xmax-xmin) + xmin
|
||||
z := <span class="hljs-built_in">complex</span>(x, y)
|
||||
<span class="hljs-comment">// Image point (px, py) represents complex value z.</span>
|
||||
img.Set(px, py, mandelbrot(z))
|
||||
}
|
||||
}
|
||||
png.Encode(os.Stdout, img) <span class="hljs-comment">// <span class="hljs-doctag">NOTE:</span> ignoring errors</span>
|
||||
}
|
||||
|
||||
<span class="hljs-keyword">func</span> mandelbrot(z <span class="hljs-typename">complex128</span>) color.Color {
|
||||
<span class="hljs-keyword">const</span> iterations = <span class="hljs-number">200</span>
|
||||
<span class="hljs-keyword">const</span> contrast = <span class="hljs-number">15</span>
|
||||
|
||||
<span class="hljs-keyword">var</span> v <span class="hljs-typename">complex128</span>
|
||||
<span class="hljs-keyword">for</span> n := <span class="hljs-typename">uint8</span>(<span class="hljs-number">0</span>); n < iterations; n++ {
|
||||
v = v*v + z
|
||||
<span class="hljs-keyword">if</span> cmplx.Abs(v) > <span class="hljs-number">2</span> {
|
||||
<span class="hljs-keyword">return</span> color.Gray{<span class="hljs-number">255</span> - contrast*n}
|
||||
}
|
||||
}
|
||||
<span class="hljs-keyword">return</span> color.Black
|
||||
}
|
||||
</code></pre>
|
||||
<p>遍歷1024x1024圖像每個點的兩個嵌套的循環對應 -2 到 +2 區間的複數平面. 程序反複測試每個點對應複數值平方值加一個增量值對應的點是否超齣半徑爲2的圓. 如果超過了, 通過根據逃逸的迭代次數對應的灰度顔色來代替. 如果不是, 該點屬於Mandelbrot集合, 使用黑色顔色標記. 最終程序將生成的PNG格式分形圖像圖像輸齣到標準輸齣, 如圖3.3所示.</p>
|
||||
<p><strong>練習3.5:</strong> 實現一個綵色的Mandelbrot圖像, 使用 image.NewRGBA 創建圖像, 使用 color.RGBA 或 color.YCbCr 生成顔色.</p>
|
||||
<p><strong>練習3.6:</strong> 超采樣技術可以降低每個像素對計算顔色值和平均值的影響. 簡單的方法是將每個像素分層四個子像素, 實現它.</p>
|
||||
<p><strong>練習3.7:</strong> 另一個生成分形圖像的方式是使用牛頓法來求解一個複數方程, 例如 z^4 − 1 = 0. 每個起點到四個根的迭代次數對應陰影的灰度. 方程根對應的點用顔色表示.</p>
|
||||
<p><img src="../images/ch3-03.png" alt=""></p>
|
||||
<p><strong>練習3.8:</strong> 通過提高精度來生成更多級别的分形. 使用四種不同精度類型的數字實現相同的分形: complex64, complex128, big.Float, and big.Rat. (後面兩種類型在 math/big 包聲明. Float是有指定限精度的浮點數; Rat是無效精度的有理數.) 它們間的性能和內存使用對比如何? 當渲染圖可見時縮放的級别是多少?</p>
|
||||
<p><strong>練習3.9:</strong> 編寫一個web服務器, 用於給客戶端生成分形的圖像. 運行客戶端用過HTTP參數參數指定x,y和zoom參數.</p>
|
||||
|
||||
|
||||
</section>
|
||||
@@ -2074,7 +2110,7 @@
|
||||
<a href="../ch3/ch3-02.html" class="navigation navigation-prev " aria-label="Previous page: 浮點數"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../ch3/ch3-04.html" class="navigation navigation-next " aria-label="Next page: 佈爾型"><i class="fa fa-angle-right"></i></a>
|
||||
<a href="../ch3/ch3-04.html" class="navigation navigation-next " aria-label="Next page: 布爾型"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
130
ch3/ch3-04.html
130
ch3/ch3-04.html
@@ -5,7 +5,7 @@
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>佈爾型 | Go编程语言</title>
|
||||
<title>布爾型 | Go编程语言</title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="generator" content="GitBook 2.5.2">
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3.4" data-chapter-title="佈爾型" data-filepath="ch3/ch3-04.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3.4" data-chapter-title="布爾型" data-filepath="ch3/ch3-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2059,8 +2023,38 @@
|
||||
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="34-佈爾型">3.4. 佈爾型</h2>
|
||||
<p>TODO</p>
|
||||
<h2 id="34-布爾型">3.4. 布爾型</h2>
|
||||
<p>一個布爾類型的值隻有兩種 true 和 false. if 和 for 語句的條件部分都是布爾類型的值, 併且 == 和 < 等比較操作也會産生布爾型的值. 一元操作符 <code>!</code> 對應邏輯非操作, 因此 <code>!true</code> 的值爲 <code>false</code>, 也可以説是 <code>(!true==false)==true</code>, 雖然表達方式不一樣, 不過我們一般會采用簡潔的布爾表達式, 就像用 x 來表示 <code>x==true</code>.</p>
|
||||
<p>布爾值可以和 && (AND) 和 || (OR) 操作符結合, 併且可能會有短路行爲: 如果運算符左邊值已經可以確定整個布爾表達式的值, 那麽運算符右邊的值將不在被評估, 因此下面的表達式總是安全的:</p>
|
||||
<pre><code class="lang-Go">s != <span class="hljs-string">""</span> && s[<span class="hljs-number">0</span>] == <span class="hljs-string">'x'</span>
|
||||
</code></pre>
|
||||
<p>其中 s[0] 應用於空字符串會導致 panic 異常.</p>
|
||||
<p>因爲 <code>&&</code> 的優先級比 <code>||</code> 高 (助記: <code>&&</code> 對應邏輯乘法, <code>||</code> 對應邏輯加法, 乘法比加法優先級要高), 下面形式的布爾表達式是不需要加小括弧的:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">if</span> <span class="hljs-string">'a'</span> <= c && c <= <span class="hljs-string">'z'</span> ||
|
||||
<span class="hljs-string">'A'</span> <= c && c <= <span class="hljs-string">'Z'</span> ||
|
||||
<span class="hljs-string">'0'</span> <= c && c <= <span class="hljs-string">'9'</span> {
|
||||
<span class="hljs-comment">// ...ASCII letter or digit...</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>布爾值併不會隱式轉換爲數字值0或1, 反之亦然. 必鬚使用一個顯式的if語句輔助轉換:</p>
|
||||
<pre><code class="lang-Go">i := <span class="hljs-number">0</span>
|
||||
<span class="hljs-keyword">if</span> b {
|
||||
i = <span class="hljs-number">1</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>如果需要經常做類似的轉換, 包裝成一個函數會更方便:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// btoi returns 1 if b is true and 0 if false.</span>
|
||||
<span class="hljs-keyword">func</span> btoi(b <span class="hljs-typename">bool</span>) <span class="hljs-typename">int</span> {
|
||||
<span class="hljs-keyword">if</span> b {
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>
|
||||
}
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>數字到布爾型的逆轉換則非常簡單, 不過爲了保持對稱, 我們也可以包裝一個函數:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// itob reports whether i is non-zero.</span>
|
||||
<span class="hljs-keyword">func</span> itob(i <span class="hljs-typename">int</span>) <span class="hljs-typename">bool</span> { <span class="hljs-keyword">return</span> i != <span class="hljs-number">0</span> }
|
||||
</code></pre>
|
||||
|
||||
|
||||
</section>
|
||||
@@ -2071,7 +2065,7 @@
|
||||
</div>
|
||||
|
||||
|
||||
<a href="../ch3/ch3-03.html" class="navigation navigation-prev " aria-label="Previous page: 復數"><i class="fa fa-angle-left"></i></a>
|
||||
<a href="../ch3/ch3-03.html" class="navigation navigation-prev " aria-label="Previous page: 複數"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../ch3/ch3-05.html" class="navigation navigation-next " aria-label="Next page: 字符串"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
381
ch3/ch3-05.html
381
ch3/ch3-05.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3.5" data-chapter-title="字符串" data-filepath="ch3/ch3-05.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3.5" data-chapter-title="字符串" data-filepath="ch3/ch3-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,7 +2024,292 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="35-字符串">3.5. 字符串</h2>
|
||||
<p>TODO</p>
|
||||
<p>一個字符串是一個不可改變的字節序列. 字符串可以包含任意的數據, 包括字節值0, 但是通常包含人類可讀的文本. 文本字符串通常被解釋爲采用UTF8編碼的Unicode碼點(rune)序列, 我們稍後會詳細討論這個問題.</p>
|
||||
<p>內置的 len 函數可以返迴一個字符串的字節數目(不是rune字符數目), 索引操作 s[i] 返迴第i個字節的字節值, i 必鬚滿足 0 ≤ i< len(s) 條件約束.</p>
|
||||
<pre><code class="lang-Go">s := <span class="hljs-string">"hello, world"</span>
|
||||
fmt.Println(<span class="hljs-built_in">len</span>(s)) <span class="hljs-comment">// "12"</span>
|
||||
fmt.Println(s[<span class="hljs-number">0</span>], s[<span class="hljs-number">7</span>]) <span class="hljs-comment">// "104 119" ('h' and 'w')</span>
|
||||
</code></pre>
|
||||
<p>Attempting to access a byte outside this range results in a panic:</p>
|
||||
<p>如果視圖訪問超齣字符串范圍的字節將會導致panic異常:</p>
|
||||
<pre><code class="lang-Go">c := s[<span class="hljs-built_in">len</span>(s)] <span class="hljs-comment">// panic: index out of range</span>
|
||||
</code></pre>
|
||||
<p>第i個字節併不一定是字符串的第i個字符, 因此對於非ASCII字符的UTF8編碼會要兩個或多個字節. 我們簡單説下字符的工作方式.</p>
|
||||
<p>子字符串操作s[i:j]基於原始的s字符串的第i個字節開始到第j個字節(併不包含j本身)生成一個新字符串. 生成的子字符串將包含 j-i 個字節.</p>
|
||||
<pre><code class="lang-Go">fmt.Println(s[<span class="hljs-number">0</span>:<span class="hljs-number">5</span>]) <span class="hljs-comment">// "hello"</span>
|
||||
</code></pre>
|
||||
<p>同樣, 如果索引超齣字符串范圍或者j小於i的話將導致panic異常.</p>
|
||||
<p>不管i還是j都可能被忽略, 當它們被忽略時將采用0作爲開始位置, 采用 len(s) 作爲接受的位置.</p>
|
||||
<pre><code class="lang-Go">fmt.Println(s[:<span class="hljs-number">5</span>]) <span class="hljs-comment">// "hello"</span>
|
||||
fmt.Println(s[<span class="hljs-number">7</span>:]) <span class="hljs-comment">// "world"</span>
|
||||
fmt.Println(s[:]) <span class="hljs-comment">// "hello, world"</span>
|
||||
</code></pre>
|
||||
<p>其中 + 操作符將兩個字符串鏈接構造一個新字符串:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(<span class="hljs-string">"goodbye"</span> + s[<span class="hljs-number">5</span>:]) <span class="hljs-comment">// "goodbye, world"</span>
|
||||
</code></pre>
|
||||
<p>字符串可以用 == 和 < 進行比較; 比較通過逐個字節比較完成的, 因此比較的結果是字符串自然編碼的順序.</p>
|
||||
<p>字符串的值是不可變的: 一個字符串包含的字節序列永遠不會被改變, 當然我們也可以給一個字符串變量分配一個新字符串值. 可以像下面這樣將一個字符串追加到另一個字符串</p>
|
||||
<pre><code class="lang-Go">s := <span class="hljs-string">"left foot"</span>
|
||||
t := s
|
||||
s += <span class="hljs-string">", right foot"</span>
|
||||
</code></pre>
|
||||
<p>這併不會導致原始的字符串值被改變, 但是 s 將因爲 += 語句持有一個新的字符串值, 但是 t 依然是包含原先的字符串值.</p>
|
||||
<pre><code class="lang-Go">fmt.Println(s) <span class="hljs-comment">// "left foot, right foot"</span>
|
||||
fmt.Println(t) <span class="hljs-comment">// "left foot"</span>
|
||||
</code></pre>
|
||||
<p>因爲字符串是不可脩改的, 因此嚐試脩改字符串內部數據的操作是被禁止的:</p>
|
||||
<pre><code class="lang-Go">s[<span class="hljs-number">0</span>] = <span class="hljs-string">'L'</span> <span class="hljs-comment">// compile error: cannot assign to s[0]</span>
|
||||
</code></pre>
|
||||
<p>不變性意味如果兩個字符串共享相同的底層數據是安全的, 這使得複製任何長度的字符串代價是低廉的. 同樣, 一個字符串 s 和對應的子字符串 s[7:] 也可以安全地共享相同的內存, 因此字符串切片操作代價也是低廉的. 在這兩種情況下都沒有必要分配新的內存. 圖3.4 演示了一個字符串和兩個字串共享相同的底層數據.</p>
|
||||
<h3 id="351-字符串面值">3.5.1. 字符串面值</h3>
|
||||
<p>字符串值也可以用字符串面值方式編寫, 隻要將一繫列字節序列包含在雙引號卽可:</p>
|
||||
<pre><code>"Hello, 世界"
|
||||
</code></pre><p><img src="../images/ch3-04.png" alt=""></p>
|
||||
<p>因爲Go語言源文件總是用UTF8編碼, 併且Go的文本字符串也以UTF8編碼的方式處理, 我們可以將Unicode碼點也寫到字符串面值中.</p>
|
||||
<p>在一個雙引號包含的字符串面值中, 可以用以反斜槓\開頭的轉義序列插入任意的數據. 下面換行, 迴車和 製表符等常見的ASCII控製代碼的轉義方式:</p>
|
||||
<pre><code>\a 響鈴
|
||||
\b 退格
|
||||
\f 換頁
|
||||
\n 換行
|
||||
\r 迴車
|
||||
\t 製表符
|
||||
\v 垂直製表符
|
||||
\' 單引號 (隻用在 '\'' 形式的rune符號面值中)
|
||||
\" 雙引號 (隻用在 "..." 形式的字符串面值中)
|
||||
\\ 反斜槓
|
||||
</code></pre><p>可以通過十六進製或八進製轉義在字符串面值包含任意的字節. 一個十六進製的轉義是 \xhh, 其中兩個h表示十六進製數字(大寫或小寫都可以). 一個八進製轉義是 \ooo, 包含三個八進製的o數字(0到7), 但是不能超過\377. 每一個單一的字節表達一個特定的值. 稍後我們將看到如何將一個Unicode碼點寫到字符串面值中.</p>
|
||||
<p>一個原生的字符串面值形式是 <code>...</code>, 使用反引號 ``` 代替雙引號. 在原生的字符串面值中, 沒有轉義操作; 全部的內容都是字面的意思, 包含退格和換行, 因此一個程序中的原生字符串面值可能跨越多行. 唯一的特殊處理是是刪除迴車以保證在所有平颱上的值都是一樣的, 包括那些把迴車也放入文本文件的繫統.</p>
|
||||
<p>原生字符串面值用於編寫正則表達式會很方便, 因爲正則表達式往往會包含很多反斜槓. 原生字符串面值同時廣泛應用於HTML模闆, JSON面值, 命令行提示信息, 以及那些需要擴展到多行的場景.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> GoUsage = <span class="hljs-string">`Go is a tool for managing Go source code.
|
||||
|
||||
Usage:
|
||||
go command [arguments]
|
||||
...`</span>
|
||||
</code></pre>
|
||||
<h3 id="352-unicode">3.5.2. Unicode</h3>
|
||||
<p>在很久以前, 世界比較簡單的, 起碼計算機就隻有一個ASCII字符集: 美國信息交換標準代碼. ASCII, 更準確地説是美國的ASCII, 使用 7bit 來表示 128 個字符: 包含英文字母的大小寫, 數字, 各種標點符號和設置控製符. 對於早期的計算機程序, 這些足夠了, 但是這也導致了世界上很多其他地區的用戶無法直接使用自己的書寫繫統. 隨着互聯網的發展, 混合多種語言的數據變了很常見. 如何有效處理這些包含了各種語言的豐富多樣的數據呢?</p>
|
||||
<p>答案就是使用Unicode(unicode.org), 它收集了這個世界上所有的書寫繫統, 包括重音符號和其他變音符號, 製表符和迴車符, 還有很多神祕符號, 每個符號都分配一個Unicode碼點, Unicode碼點對應Go語言中的rune類型.</p>
|
||||
<p>第八版本的Unicode標準收集了超過120,000個字符, 涵蓋超過100種語言. 這些在計算機程序和數據中是如何體現的那? 通用的表示一個Unicode碼點的數據類型是int32, 也就是Go語言中rune對應的類型; 它的同義詞rune符文正是這個意思.</p>
|
||||
<p>我們可以將一個符文序列表示爲一個int32序列. 這種編碼方式叫UTF-32或UCS-4, 每個Unicode碼點都使用同樣的大小32bit來表示. 這種方式比較簡單統一, 它會浪費很多存儲空間, 因爲大數據計算機可讀的文本是ASCII字符, 本來每個ASCII字符隻需要8bit或1字節就能表示. 卽使是常用的字符也遠少於65,536個, 也就是説用16bit編碼方式就能表達常用字符. 但是, 還有更好的編碼方法嗎?</p>
|
||||
<h3 id="353-utf8">3.5.3. UTF-8</h3>
|
||||
<p>UTF8是一個將Unicode碼點編碼爲字節序列的變長編碼. UTF8編碼由Go語言之父 Ken Thompson 和 Rob Pike 共同發明, 現在已經是Unicode的標準. UTF8使用1到4個字節來表示每個Unicode碼點符號, ASCII部分字符隻使用1個字節, 常用字符部分使用2或3個字節. 每個符號編碼後第一個字節的高端bit位用於表示總共有多少個字節. 如果第一個字節的高端bit爲0, 則表示對應7bit的ASCII字符, 每個字符一個字節, 和傳統的ASCII編碼兼容. 如果第一個字節的高端bit是110, 則説明需要2個字節; 後續的每個高端bit都以10開頭. 更大的Unicode碼點也是采用類似的策略處理.</p>
|
||||
<pre><code>0xxxxxx runes 0-127 (ASCII)
|
||||
11xxxxx 10xxxxxx 128-2047 (values <128 unused)
|
||||
110xxxx 10xxxxxx 10xxxxxx 2048-65535 (values <2048 unused)
|
||||
1110xxx 10xxxxxx 10xxxxxx 10xxxxxx 65536-0x10ffff (other values unused)
|
||||
</code></pre><p>變長的編碼無法直接通過索引來訪問第n個字符, 但是UTF8穫得了很多額外的優點. 首先UTF8編碼比較緊湊, 兼容ASCII, 併且可以自動同步: 它可以通過向前迴朔最多2個字節就能確定當前字符編碼的開始字節的位置. 它也是一個前綴編碼, 所以當從左向右解碼時不會有任何歧義也併不需要向前査看. 沒有任何字符的編碼是其它字符編碼的子串, 或是其它編碼序列的字串, 因此蒐索一個字符時隻要蒐索它的字節編碼序列卽可, 不用擔心前後的上下文會對蒐索結果産生榦擾. 同時UTF8編碼的順序和Unicode碼點的順序一致, 因此可以直接排序UTF8編碼序列. 同業也沒有嵌入的NUL(0)字節, 可以很好地兼容那些使用NUL作爲字符串結尾的編程語言.</p>
|
||||
<p>Go的源文件采用UTF8編碼, 併且Go處理UTF8編碼的文本也很齣色. unicode 包提供了諸多處理 rune 字符相關功能的函數函數(區分字母和數組, 或者是字母的大寫和小寫轉換等), unicode/utf8 包了提供了rune 字符序列的UTF8編碼和解碼的功能.</p>
|
||||
<p>有很多Unicode字符很難直接從鍵盤輸入, 併且很多字符有着相似的結構; 有一些甚至是不可見的字符. Go字符串面值中的Unicode轉義字符讓我們可以通過Unicode碼點輸入特殊的字符. 有兩種形式, \uhhhh 對應16bit的碼點值, \Uhhhhhhhh 對應32bit的碼點值, 其中h是一個十六進製數字; 一般很少需要使用32bit的形式. 每一個對應碼點的UTF8編碼. 例如: 下面的字母串面值都表示相同的值:</p>
|
||||
<pre><code>"世界"
|
||||
"\xe4\xb8\x96\xe7\x95\x8c"
|
||||
"\u4e16\u754c"
|
||||
"\U00004e16\U0000754c"
|
||||
</code></pre><p>上面三個轉義序列爲第一個字符串提供替代寫法, 但是它們的值都是相同的.</p>
|
||||
<p>Unicode轉義也可以使用在rune字符中. 下面三個字符是等價的:</p>
|
||||
<pre><code>'世' '\u4e16' '\U00004e16'
|
||||
</code></pre><p>對於小於256碼點值可以寫在一個十六進製轉義字節中, 例如 '\x41' 對應 'A' 字符, 但是對於更大的碼點則必鬚使用 \u 或 \U 轉義形式. 因此, '\xe4\xb8\x96' 併不是一個合法的rune字符, 雖然這三個字節對應一個有效的UTF8編碼的碼點.</p>
|
||||
<p>得意於UTF8優良的設計, 諸多字符串操作都不需要解碼. 我們可以不用解碼直接測試一個字符串是否是另一個字符串的前綴:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> HasPrefix(s, prefix <span class="hljs-typename">string</span>) <span class="hljs-typename">bool</span> {
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-built_in">len</span>(s) >= <span class="hljs-built_in">len</span>(prefix) && s[:<span class="hljs-built_in">len</span>(prefix)] == prefix
|
||||
}
|
||||
</code></pre>
|
||||
<p>或者是後綴測試:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> HasSuffix(s, suffix <span class="hljs-typename">string</span>) <span class="hljs-typename">bool</span> {
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-built_in">len</span>(s) >= <span class="hljs-built_in">len</span>(suffix) && s[<span class="hljs-built_in">len</span>(s)-<span class="hljs-built_in">len</span>(suffix):] == suffix
|
||||
}
|
||||
</code></pre>
|
||||
<p>或者是包含子串測試:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> Contains(s, substr <span class="hljs-typename">string</span>) <span class="hljs-typename">bool</span> {
|
||||
<span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i < <span class="hljs-built_in">len</span>(s); i++ {
|
||||
<span class="hljs-keyword">if</span> HasPrefix(s[i:], substr) {
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-constant">true</span>
|
||||
}
|
||||
}
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-constant">false</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>對於UTF8編碼後文本的處理和原始的字節處理邏輯一樣. 但是對應很多其它編碼則併不是這樣的. (上面的函數都來自 strings 字符串處理包, 雖然它們的實現包含了一個用哈希技術優化的 Contains 實現.)</p>
|
||||
<p>另以方面, 如果我們眞的關心每個Unicode字符, 我們可以使用其它機製. 考慮前面的第一個例子中的字符串, 它包混合了中西兩種字符. 圖3.5展示了它的內存表示形式. 字符串包含13個字節, 以UTF8形式編碼, 但是隻對應9個Unicode字符:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">import</span> <span class="hljs-string">"unicode/utf8"</span>
|
||||
|
||||
s := <span class="hljs-string">"Hello, 世界"</span>
|
||||
fmt.Println(<span class="hljs-built_in">len</span>(s)) <span class="hljs-comment">// "13"</span>
|
||||
fmt.Println(utf8.RuneCountInString(s)) <span class="hljs-comment">// "9"</span>
|
||||
</code></pre>
|
||||
<p>爲了處理這些眞實的字符, 我們需要一個UTF8解碼器. unicode/utf8 包提供了實現, 我們可以這樣使用:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i < <span class="hljs-built_in">len</span>(s); {
|
||||
r, size := utf8.DecodeRuneInString(s[i:])
|
||||
fmt.Printf(<span class="hljs-string">"%d\t%c\n"</span>, i, r)
|
||||
i += size
|
||||
}
|
||||
</code></pre>
|
||||
<p>每一次調用 DecodeRuneInString 函數都返迴一個 r 和 長度, r 對應字符本身, 長度對應r采用UTF8編碼後的字節數目. 長度可以用於更新第i個字符在字符串中的字節索引位置. 但是這種方式是笨拙的, 我們需要更簡潔的語法. 幸運的是, Go的range循環在處理字符串的時候, 會自動隱式解碼UTF8字符串. 下面的循環運行如圖3.5所示; 需要註意的是對於非ASCII, 索引更新的步長超過1個字節.</p>
|
||||
<p><img src="../images/ch3-05.png" alt=""></p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">for</span> i, r := <span class="hljs-keyword">range</span> <span class="hljs-string">"Hello, 世界"</span> {
|
||||
fmt.Printf(<span class="hljs-string">"%d\t%q\t%d\n"</span>, i, r, r)
|
||||
}
|
||||
</code></pre>
|
||||
<p>我們可以使用一個簡單的循環來統計字符串中字符的數目, 像這樣:</p>
|
||||
<pre><code class="lang-Go">n := <span class="hljs-number">0</span>
|
||||
<span class="hljs-keyword">for</span> _, _ = <span class="hljs-keyword">range</span> s {
|
||||
n++
|
||||
}
|
||||
</code></pre>
|
||||
<p>想其它形式的循環那樣, 我們可以忽略不需要的變量:</p>
|
||||
<pre><code class="lang-Go">n := <span class="hljs-number">0</span>
|
||||
<span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> s {
|
||||
n++
|
||||
}
|
||||
</code></pre>
|
||||
<p>或者我們可以直接調用 utf8.RuneCountInString(s) 函數.</p>
|
||||
<p>正如我們前面提到了, 文本字符串采用UTF8編碼隻是一種慣例,但是對於循環的眞正字符串併不是一個慣例, 這是正確的. 如果用於循環的字符串隻是一個普通的二進製數據, 或者是含有錯誤編碼的UTF8數據, 將會發送什麽?</p>
|
||||
<p>每一個UTF8字符解碼, 不管是顯示地調用 utf8.DecodeRuneInString 解碼或在 range 循環中隱式地解碼, 如果遇到一個錯誤的輸入字節, 將生成一個特别的Unicode字符 '\uFFFD', 在印刷中這個符號通常是一個黑色六角或鑽石形狀, 里面包含一個白色的問號(?). 當程序遇到這樣的一個字符, 通常是一個信號, 説明輸入併不是一個完美沒有錯誤的的UTF8編碼字符串.</p>
|
||||
<p>UTF8作爲交換格式是非常方便的, 但是在程序內部采用rune類型可能更方便, 因爲rune大小一致, 支持數組索引和方便切割.</p>
|
||||
<p>string 接受到 []rune 的轉換, 可以將一個UTF8編碼的字符串解碼爲Unicode字符序列:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// "program" in Japanese katakana</span>
|
||||
s := <span class="hljs-string">"プログラム"</span>
|
||||
fmt.Printf(<span class="hljs-string">"% x\n"</span>, s) <span class="hljs-comment">// "e3 83 97 e3 83 ad e3 82 b0 e3 83 a9 e3 83 a0"</span>
|
||||
r := []<span class="hljs-typename">rune</span>(s)
|
||||
fmt.Printf(<span class="hljs-string">"%x\n"</span>, r) <span class="hljs-comment">// "[30d7 30ed 30b0 30e9 30e0]"</span>
|
||||
</code></pre>
|
||||
<p>(在第一個Printf中的 <code>% x</code> 參數用於在每個十六進製數字前插入一個空格.)</p>
|
||||
<p>如果是將一個 []rune 類型的Unicode字符切片或數組轉爲string, 則對它們進行UTF8編碼:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(<span class="hljs-typename">string</span>(r)) <span class="hljs-comment">// "プログラム"</span>
|
||||
</code></pre>
|
||||
<p>將一個整數轉型爲字符串意思是生成整數作爲Unicode碼點的UTF8編碼的字符串: </p>
|
||||
<pre><code class="lang-Go">fmt.Println(<span class="hljs-typename">string</span>(<span class="hljs-number">65</span>)) <span class="hljs-comment">// "A", not "65"</span>
|
||||
fmt.Println(<span class="hljs-typename">string</span>(<span class="hljs-number">0x4eac</span>)) <span class="hljs-comment">// "京"</span>
|
||||
</code></pre>
|
||||
<p>如果對應碼點的字符是無效的, 則用'\uFFFD'無效字符作爲替換:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(<span class="hljs-typename">string</span>(<span class="hljs-number">1234567</span>)) <span class="hljs-comment">// "(?)"</span>
|
||||
</code></pre>
|
||||
<h3 id="354-字符串和byte切片">3.5.4. 字符串和Byte切片</h3>
|
||||
<p>標準庫中有四個包對字符串處理尤爲重要: bytes, strings, strconv, 和 unicode. strings 包提供了許多如字符串的査詢, 替換, 比較, 截斷, 拆分, 和合併等功能.</p>
|
||||
<p>bytes 包也提供了很多類似功能的函數, 但是針對和字符串有着相同結構的 []byte 類型. 因爲字符串是隻讀的, 因此逐步構建字符串會導致很多分配和複製. 在這種情況下, 使用 bytes.Buffer 類型會更有效, 稍後我們將展示.</p>
|
||||
<p>strconv 包提供了 布爾型, 整型數, 浮點數和對應字符串間的相互轉換, 還提供了雙引號的字符串面值形式的轉換.</p>
|
||||
<p>unicode 包提供了類似 IsDigit, IsLetter, IsUpper, 和 IsLower 等功能, 它們用於給字符分類. 每個函數有一個單一的rune類型的參數, 然後返迴一個布爾值. 像 ToUpper 和 ToLower 之類的轉換函數將用於rune字符的大小寫轉換. 所有的這些函數都是遵循Unicode標準定義的字母,數字等分類規范. strings 包也有類似的函數, 它們是 ToUpper 和 ToLower, 將原始字符串的每個字符都做相應的轉換, 然後返迴新的字符串.</p>
|
||||
<p>下面的 basename 函數的靈感由Unix shell的同名工具而來. 在我們實現的版本中, basename(s) 將看起來像是繫統路徑的前綴刪除, 同時將看似文件類型的後綴名刪除:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(basename(<span class="hljs-string">"a/b/c.go"</span>)) <span class="hljs-comment">// "c"</span>
|
||||
fmt.Println(basename(<span class="hljs-string">"c.d.go"</span>)) <span class="hljs-comment">// "c.d"</span>
|
||||
fmt.Println(basename(<span class="hljs-string">"abc"</span>)) <span class="hljs-comment">// "abc"</span>
|
||||
</code></pre>
|
||||
<p>第一個版本併沒有使用任何庫, 全部手工實現:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch3/basename1
|
||||
<span class="hljs-comment">// basename removes directory components and a .suffix.</span>
|
||||
<span class="hljs-comment">// e.g., a => a, a.go => a, a/b/c.go => c, a/b.c.go => b.c</span>
|
||||
<span class="hljs-keyword">func</span> basename(s <span class="hljs-typename">string</span>) <span class="hljs-typename">string</span> {
|
||||
<span class="hljs-comment">// Discard last '/' and everything before.</span>
|
||||
<span class="hljs-keyword">for</span> i := <span class="hljs-built_in">len</span>(s) - <span class="hljs-number">1</span>; i >= <span class="hljs-number">0</span>; i-- {
|
||||
<span class="hljs-keyword">if</span> s[i] == <span class="hljs-string">'/'</span> {
|
||||
s = s[i+<span class="hljs-number">1</span>:]
|
||||
<span class="hljs-keyword">break</span>
|
||||
}
|
||||
}
|
||||
<span class="hljs-comment">// Preserve everything before last '.'.</span>
|
||||
<span class="hljs-keyword">for</span> i := <span class="hljs-built_in">len</span>(s) - <span class="hljs-number">1</span>; i >= <span class="hljs-number">0</span>; i-- {
|
||||
<span class="hljs-keyword">if</span> s[i] == <span class="hljs-string">'.'</span> {
|
||||
s = s[:i]
|
||||
<span class="hljs-keyword">break</span>
|
||||
}
|
||||
}
|
||||
<span class="hljs-keyword">return</span> s
|
||||
}
|
||||
</code></pre>
|
||||
<p>一個簡化的版本使用了 strings.LastIndex 庫函數:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch3/basename2
|
||||
|
||||
<span class="hljs-keyword">func</span> basename(s <span class="hljs-typename">string</span>) <span class="hljs-typename">string</span> {
|
||||
slash := strings.LastIndex(s, <span class="hljs-string">"/"</span>) <span class="hljs-comment">// -1 if "/" not found</span>
|
||||
s = s[slash+<span class="hljs-number">1</span>:]
|
||||
<span class="hljs-keyword">if</span> dot := strings.LastIndex(s, <span class="hljs-string">"."</span>); dot >= <span class="hljs-number">0</span> {
|
||||
s = s[:dot]
|
||||
}
|
||||
<span class="hljs-keyword">return</span> s
|
||||
}
|
||||
</code></pre>
|
||||
<p>path 和 path/filepath 包提供了關於文件名更一般的函數操作. 使用斜槓分隔路徑可以在任何操作繫統上工作. 斜槓本身不應該用於文件名, 但是在其他一些領域可能是有效的, 例如URL路徑組件. 相比之下, path/filepath 包使用操作繫統本身的路徑規則, 例如 POSIX 繫統使用 /foo/bar, Microsoft Windows 使用 c:\foo\bar 等.</p>
|
||||
<p>讓我們繼續另一個字符串的例子. 任務是將一個表示整值的字符串, 每隔三個字符插入一個逗號, 例如 "12345" 處理後成爲 "12,345". 這個版本隻適用於整數類型; 支持浮點數類型的支持留做練習.</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch3/comma
|
||||
|
||||
<span class="hljs-comment">// comma inserts commas in a non-negative decimal integer string.</span>
|
||||
<span class="hljs-keyword">func</span> comma(s <span class="hljs-typename">string</span>) <span class="hljs-typename">string</span> {
|
||||
n := <span class="hljs-built_in">len</span>(s)
|
||||
<span class="hljs-keyword">if</span> n <= <span class="hljs-number">3</span> {
|
||||
<span class="hljs-keyword">return</span> s
|
||||
}
|
||||
<span class="hljs-keyword">return</span> comma(s[:n-<span class="hljs-number">3</span>]) + <span class="hljs-string">","</span> + s[n-<span class="hljs-number">3</span>:]
|
||||
}
|
||||
</code></pre>
|
||||
<p>輸入 comma 的參數是一個字符串. 如果輸入字符串的長度小於或等於3的話, 則不需要插入逗號. 否則, comma 將在最後三個字符前切割爲兩個兩個子串, 然後用前面的子串遞歸調用自身.</p>
|
||||
<p>一個字符串包含的字節數組, 一旦創建, 是不可變的. 相比之下, 一個字節切片的原始則可以自由地脩改.</p>
|
||||
<p>字符串和字節切片可以相互轉換:</p>
|
||||
<pre><code class="lang-Go">s := <span class="hljs-string">"abc"</span>
|
||||
b := []<span class="hljs-typename">byte</span>(s)
|
||||
s2 := <span class="hljs-typename">string</span>(b)
|
||||
</code></pre>
|
||||
<p>從概念上講, []byte(s) 轉換是分配了一個新的字節數組保存了字符串數據的拷貝, 然後引用這個字節數組. 編譯器的優化可以避免在一些場景下分配和複製字符串數據, 但總的來説需要確保在b被脩改的情況下, 原始的s字符串也不會改變. 將一個字節切片轉到字符串的 string(b) 操作則是構造一個拷貝, 以確保s2字符串是隻讀的.</p>
|
||||
<p>爲了避免轉換中不必要的內存分配, bytes包和strings同時提供了許多類似的實用函數. 下面是strings包中的六個函數:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> Contains(s, substr <span class="hljs-typename">string</span>) <span class="hljs-typename">bool</span>
|
||||
<span class="hljs-keyword">func</span> Count(s, sep <span class="hljs-typename">string</span>) <span class="hljs-typename">int</span>
|
||||
<span class="hljs-keyword">func</span> Fields(s <span class="hljs-typename">string</span>) []<span class="hljs-typename">string</span>
|
||||
<span class="hljs-keyword">func</span> HasPrefix(s, prefix <span class="hljs-typename">string</span>) <span class="hljs-typename">bool</span>
|
||||
<span class="hljs-keyword">func</span> Index(s, sep <span class="hljs-typename">string</span>) <span class="hljs-typename">int</span>
|
||||
<span class="hljs-keyword">func</span> Join(a []<span class="hljs-typename">string</span>, sep <span class="hljs-typename">string</span>) <span class="hljs-typename">string</span>
|
||||
</code></pre>
|
||||
<p>bytes 包中對應的六個函數:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> Contains(b, subslice []<span class="hljs-typename">byte</span>) <span class="hljs-typename">bool</span>
|
||||
<span class="hljs-keyword">func</span> Count(s, sep []<span class="hljs-typename">byte</span>) <span class="hljs-typename">int</span>
|
||||
<span class="hljs-keyword">func</span> Fields(s []<span class="hljs-typename">byte</span>) [][]<span class="hljs-typename">byte</span>
|
||||
<span class="hljs-keyword">func</span> HasPrefix(s, prefix []<span class="hljs-typename">byte</span>) <span class="hljs-typename">bool</span>
|
||||
<span class="hljs-keyword">func</span> Index(s, sep []<span class="hljs-typename">byte</span>) <span class="hljs-typename">int</span>
|
||||
<span class="hljs-keyword">func</span> Join(s [][]<span class="hljs-typename">byte</span>, sep []<span class="hljs-typename">byte</span>) []<span class="hljs-typename">byte</span>
|
||||
</code></pre>
|
||||
<p>唯一的區别是字符串類型參數被替換成了字節切片類型的參數.</p>
|
||||
<p>bytes 包還提供了 Buffer 類型用於字節切片的緩存. 一個 Buffer 開始是空的, 但是隨着 string, byte, 和 []byte 等類型數據的寫入可以動態增長, 一個 bytes.Buffer 變量併不需要處理化, 因此零值也是有效的:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch3/printints
|
||||
|
||||
<span class="hljs-comment">// intsToString is like fmt.Sprintf(values) but adds commas.</span>
|
||||
<span class="hljs-keyword">func</span> intsToString(values []<span class="hljs-typename">int</span>) <span class="hljs-typename">string</span> {
|
||||
<span class="hljs-keyword">var</span> buf bytes.Buffer
|
||||
buf.WriteByte(<span class="hljs-string">'['</span>)
|
||||
<span class="hljs-keyword">for</span> i, v := <span class="hljs-keyword">range</span> values {
|
||||
<span class="hljs-keyword">if</span> i > <span class="hljs-number">0</span> {
|
||||
buf.WriteString(<span class="hljs-string">", "</span>)
|
||||
}
|
||||
fmt.Fprintf(&buf, <span class="hljs-string">"%d"</span>, v)
|
||||
}
|
||||
buf.WriteByte(<span class="hljs-string">']'</span>)
|
||||
<span class="hljs-keyword">return</span> buf.String()
|
||||
}
|
||||
|
||||
<span class="hljs-keyword">func</span> main() {
|
||||
fmt.Println(intsToString([]<span class="hljs-typename">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>})) <span class="hljs-comment">// "[1, 2, 3]"</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>當向 bytes.Buffer 添加任意字符的UTF8編碼, 最好使用 bytes.Buffer 的 WriteRune 方法, 但是 WriteByte 方法對於寫入類似 '[' 和 ']' 等 ASCII 字符則更有效.</p>
|
||||
<p>bytes.Buffer 類型有着諸多實用的功能, 我們在第七章討論接口時層涉及到, 我們將看看如何將它用作一個I/O 的輸入和輸齣對象, 例如 Fprintf 的 io.Writer 輸齣, 或作爲輸入源 io.Reader.</p>
|
||||
<p><strong>練習3.10:</strong> 編寫一個非遞歸版本的comma函數, 使用 bytes.Buffer 代替字符串鏈接操作.</p>
|
||||
<p><strong>練習3.11:</strong> 完善 comma 函數, 以支持浮點數處理和一個可選的正負號處理.</p>
|
||||
<p><strong>練習3.12:</strong> 編寫一個函數, 判斷兩個字符串是否是是相互打亂的, 也就是説它們有着相同的字符, 但是對應不同的順序.</p>
|
||||
<h3 id="355-字符串和數字的轉換">3.5.5. 字符串和數字的轉換</h3>
|
||||
<p>除了字符串, 字符, 字節 之間的轉換, 字符串和數值之間的轉換也比較常見. 由 strconv 包提供這類轉換功能.</p>
|
||||
<p>將一個整數轉爲字符串, 一種方法是用 fmt.Sprintf; 另一個方法是用 strconv.Itoa(“整數到ASCII”):</p>
|
||||
<pre><code class="lang-Go">x := <span class="hljs-number">123</span>
|
||||
y := fmt.Sprintf(<span class="hljs-string">"%d"</span>, x)
|
||||
fmt.Println(y, strconv.Itoa(x)) <span class="hljs-comment">// "123 123"</span>
|
||||
</code></pre>
|
||||
<p>FormatInt和FormatUint可以用不同的進製來格式化數字:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(strconv.FormatInt(<span class="hljs-typename">int64</span>(x), <span class="hljs-number">2</span>)) <span class="hljs-comment">// "1111011"</span>
|
||||
</code></pre>
|
||||
<p>fmt.Printf 函數的 %b, %d, %u, 和 %x 等參數提供功能往往比strconv 包的 Format 函數方便很多, 特别是在需要包含附加信息的時候:</p>
|
||||
<pre><code class="lang-Go">s := fmt.Sprintf(<span class="hljs-string">"x=%b"</span>, x) <span class="hljs-comment">// "x=1111011"</span>
|
||||
</code></pre>
|
||||
<p>如果要將一個字符串解析爲整數, 可以使用 strconv 包的 Atoi 或 ParseInt 函數, 還有用於解析無符號整數的 ParseUint 函數:</p>
|
||||
<pre><code class="lang-Go">x, err := strconv.Atoi(<span class="hljs-string">"123"</span>) <span class="hljs-comment">// x is an int</span>
|
||||
y, err := strconv.ParseInt(<span class="hljs-string">"123"</span>, <span class="hljs-number">10</span>, <span class="hljs-number">64</span>) <span class="hljs-comment">// base 10, up to 64 bits</span>
|
||||
</code></pre>
|
||||
<p>ParseInt 函數的第三個參數是用於指定整型數的大小; 例如16表示int16, 0則表示int. 在任何情況下, 返迴的結果 y 總是 int64 類型, 你可以通過強製類型轉換將它轉爲更小的整數類型.</p>
|
||||
<p>有時候也會使用 fmt.Scanf 來解析輸入的字符串和數字, 特别是當字符串和數字混合在一行的時候, 它可以靈活處理不完整或不規則的輸入.</p>
|
||||
|
||||
|
||||
</section>
|
||||
@@ -2071,7 +2320,7 @@
|
||||
</div>
|
||||
|
||||
|
||||
<a href="../ch3/ch3-04.html" class="navigation navigation-prev " aria-label="Previous page: 佈爾型"><i class="fa fa-angle-left"></i></a>
|
||||
<a href="../ch3/ch3-04.html" class="navigation navigation-prev " aria-label="Previous page: 布爾型"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../ch3/ch3-06.html" class="navigation navigation-next " aria-label="Next page: 常量"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
259
ch3/ch3-06.html
259
ch3/ch3-06.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3.6" data-chapter-title="常量" data-filepath="ch3/ch3-06.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3.6" data-chapter-title="常量" data-filepath="ch3/ch3-06.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,7 +2024,170 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="36-常量">3.6. 常量</h2>
|
||||
<p>TODO</p>
|
||||
<p>常量表達式的值在編譯期計算, 而不是在運行期. 每種常量的潛在類型都是基礎類型: boolean, string, 或數字.</p>
|
||||
<p>一個常量的聲明語句定義了常量的名字, 和變量的聲明語法類似, 常量的值不可脩改, 這樣可以防止在運行期被意外或惡意的脩改. 例如, 常量比變量更適合用於表達像 π 之類的數學常數, 因爲它們的值不會變化:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> pi = <span class="hljs-number">3.14159</span> <span class="hljs-comment">// approximately; math.Pi is a better approximation</span>
|
||||
</code></pre>
|
||||
<p>和變量聲明一樣, 可以批量聲明多個常量; 這比較適合聲明一組相關的常量:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> (
|
||||
e = <span class="hljs-number">2.71828182845904523536028747135266249775724709369995957496696763</span>
|
||||
pi = <span class="hljs-number">3.14159265358979323846264338327950288419716939937510582097494459</span>
|
||||
)
|
||||
</code></pre>
|
||||
<p>許多常量的運算可以在編譯期完成, 這樣可以減少運行時的工作, 也方便其他編譯優化. 當操作數是常量時, 一些運行時的錯誤可以在編譯時發現, 例如整數除零, 字符串索引越界, 任何導致無效浮點數的操作等.</p>
|
||||
<p>常量間的所有算術運算, 邏輯運算和比較運算的結果也是常量, 對常量的類型轉換操作或以下函數調用都是返迴常量結果: len, cap, real, imag, complex, 和 unsafe.Sizeof(§13.1).</p>
|
||||
<p>因爲它們的值是在編譯期就確定的, 因此常量可以是構成類型的一部分, 例如用於指定數組類型的長度:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> IPv4Len = <span class="hljs-number">4</span>
|
||||
|
||||
<span class="hljs-comment">// parseIPv4 parses an IPv4 address (d.d.d.d).</span>
|
||||
<span class="hljs-keyword">func</span> parseIPv4(s <span class="hljs-typename">string</span>) IP {
|
||||
<span class="hljs-keyword">var</span> p [IPv4Len]<span class="hljs-typename">byte</span>
|
||||
<span class="hljs-comment">// ...</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>一個常量的聲明也可以包含一個類型和一個值, 但是如果沒有顯式指明類型, 那麽將從右邊的表達式推斷類型. 在下面的代碼中, time.Duration 是一個命名類型, 底層類型是 int64, time.Minute 是對應類型的常量. 下面聲明的兩個常量都是 time.Duration 類型, 可以通過 %T 參數打印類型信息:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> noDelay time.Duration = <span class="hljs-number">0</span>
|
||||
<span class="hljs-keyword">const</span> timeout = <span class="hljs-number">5</span> * time.Minute
|
||||
fmt.Printf(<span class="hljs-string">"%T %[1]v\n"</span>, noDelay) <span class="hljs-comment">// "time.Duration 0"</span>
|
||||
fmt.Printf(<span class="hljs-string">"%T %[1]v\n"</span>, timeout) <span class="hljs-comment">// "time.Duration 5m0s</span>
|
||||
fmt.Printf(<span class="hljs-string">"%T %[1]v\n"</span>, time.Minute) <span class="hljs-comment">// "time.Duration 1m0s"</span>
|
||||
</code></pre>
|
||||
<p>如果是批量聲明的常量, 除了第一個外其他常量的右邊的表發生可以省略, 如果省略則表示使用前面的表達式, 對應的常量類型也一樣. 例如:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> (
|
||||
a = <span class="hljs-number">1</span>
|
||||
b
|
||||
c = <span class="hljs-number">2</span>
|
||||
d
|
||||
)
|
||||
|
||||
fmt.Println(a, b, c, d) <span class="hljs-comment">// "1 1 2 2"</span>
|
||||
</code></pre>
|
||||
<p>如果隻是簡單地複製右邊的常量表達式, 併沒有太實用的價值. 但是它可以帶來其他的特性, 那就是 iota 常量生成器.</p>
|
||||
<h3 id="361-iota-常量生成器">3.6.1. iota 常量生成器</h3>
|
||||
<p>常量聲明可以使用 iota 常量生成器, 用於生成一組相似的常量值, 但是不用每行都寫一遍. 在一個 const 聲明語句中, 在開始一行 iota 將會被置爲0, 然後在每一個有常量聲明的行加一.</p>
|
||||
<p>下面是來自 time 包的例子, 它首先定義了Weekday命名類型, 然後爲一週的每天定義一個常量, 從週日0開始. 這種類型一般被稱爲枚舉類型.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">type</span> Weekday <span class="hljs-typename">int</span>
|
||||
|
||||
<span class="hljs-keyword">const</span> (
|
||||
Sunday Weekday = <span class="hljs-constant">iota</span>
|
||||
Monday
|
||||
Tuesday
|
||||
Wednesday
|
||||
Thursday
|
||||
Friday
|
||||
Saturday
|
||||
)
|
||||
</code></pre>
|
||||
<p>週一將對應0, 週一爲1, 如此等等.</p>
|
||||
<p>我們也可以在複雜的常量表達式中使用 iota, 下面是來自 net 包的例子, 用於給一個無符號整數的最低5bit的每個bit給定一個名字:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">type</span> Flags <span class="hljs-typename">uint</span>
|
||||
|
||||
<span class="hljs-keyword">const</span> (
|
||||
FlagUp Flags = <span class="hljs-number">1</span> << <span class="hljs-constant">iota</span> <span class="hljs-comment">// is up</span>
|
||||
FlagBroadcast <span class="hljs-comment">// supports broadcast access capability</span>
|
||||
FlagLoopback <span class="hljs-comment">// is a loopback interface</span>
|
||||
FlagPointToPoint <span class="hljs-comment">// belongs to a point-to-point link</span>
|
||||
FlagMulticast <span class="hljs-comment">// supports multicast access capability</span>
|
||||
)
|
||||
</code></pre>
|
||||
<p>隨着 iota 的遞增, 每個常量對應表達式 1 << iota, 是連續的2的冪, 分别對應一個bit位置. 使用這些常量可以測試, 設置, 或清除對應的bit位的值:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch3/netflag
|
||||
|
||||
<span class="hljs-keyword">func</span> IsUp(v Flags) <span class="hljs-typename">bool</span> { <span class="hljs-keyword">return</span> v&FlagUp == FlagUp }
|
||||
<span class="hljs-keyword">func</span> TurnDown(v *Flags) { *v &^= FlagUp }
|
||||
<span class="hljs-keyword">func</span> SetBroadcast(v *Flags) { *v |= FlagBroadcast }
|
||||
<span class="hljs-keyword">func</span> IsCast(v Flags) <span class="hljs-typename">bool</span> { <span class="hljs-keyword">return</span> v&(FlagBroadcast|FlagMulticast) != <span class="hljs-number">0</span> }
|
||||
|
||||
unc main() {
|
||||
<span class="hljs-keyword">var</span> v Flags = FlagMulticast | FlagUp
|
||||
fmt.Printf(<span class="hljs-string">"%b %t\n"</span>, v, IsUp(v)) <span class="hljs-comment">// "10001 true"</span>
|
||||
TurnDown(&v)
|
||||
fmt.Printf(<span class="hljs-string">"%b %t\n"</span>, v, IsUp(v)) <span class="hljs-comment">// "10000 false"</span>
|
||||
SetBroadcast(&v)
|
||||
fmt.Printf(<span class="hljs-string">"%b %t\n"</span>, v, IsUp(v)) <span class="hljs-comment">// "10010 false"</span>
|
||||
fmt.Printf(<span class="hljs-string">"%b %t\n"</span>, v, IsCast(v)) <span class="hljs-comment">// "10010 true"</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>下面是一個更複雜的例子, 每個常量都是1024的冪:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> (
|
||||
_ = <span class="hljs-number">1</span> << (<span class="hljs-number">10</span> * <span class="hljs-constant">iota</span>)
|
||||
KiB <span class="hljs-comment">// 1024</span>
|
||||
MiB <span class="hljs-comment">// 1048576</span>
|
||||
GiB <span class="hljs-comment">// 1073741824</span>
|
||||
TiB <span class="hljs-comment">// 1099511627776 (exceeds 1 << 32)</span>
|
||||
PiB <span class="hljs-comment">// 1125899906842624</span>
|
||||
EiB <span class="hljs-comment">// 1152921504606846976</span>
|
||||
ZiB <span class="hljs-comment">// 1180591620717411303424 (exceeds 1 << 64)</span>
|
||||
YiB <span class="hljs-comment">// 1208925819614629174706176</span>
|
||||
)
|
||||
</code></pre>
|
||||
<p>iota 機製也有其局限性. 例如, 它併不能用於産生1000的冪(KB,MB,等等), 因爲併沒有計算冪的運算符.</p>
|
||||
<p><strong>練習3.13:</strong> 編寫KB,MB的常量聲明, 然後擴展到YB.</p>
|
||||
<h3 id="362-無類型常量">3.6.2. 無類型常量</h3>
|
||||
<p>Go語言的常量有點不尋常. 雖然一個常量可以有任意有一個確定的基礎類型, 例如 int 或 float64, 或者是類似 time.Duration 這樣命名的基礎類型, 但是許多常量併沒有一個明確的基礎類型. 編譯期爲這些沒有明確的基礎類型的數字常量提供比基礎類型或機器更高精度的算術運算; 你可以認爲至少有256bit的運算精度. 這里有六種未明確類型的常量類型, 分别是 無類型的布爾型, 無類型的整數, 無類型的字符, 無類型的浮點數, 無類型的複數, 無類型的字符串.</p>
|
||||
<p>通過延遲明確具體類型, 無類型的常量不僅可以提供更高的精度, 而且可以直接用於更多的表達式而不需要類型轉換. 例如 例子中的 ZiB 和 YiB 的值已經超齣任何Go中整數類型能表達的范圍, 但是它們依然是合法的常量, 而且可以像下面表達式這樣使用:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(YiB/ZiB) <span class="hljs-comment">// "1024"</span>
|
||||
</code></pre>
|
||||
<p>另一個例子, math.Pi 無類型的浮點數常量, 可以直接用於任意需要浮點數或複數的地方:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> x <span class="hljs-typename">float32</span> = math.Pi
|
||||
<span class="hljs-keyword">var</span> y <span class="hljs-typename">float64</span> = math.Pi
|
||||
<span class="hljs-keyword">var</span> z <span class="hljs-typename">complex128</span> = math.Pi
|
||||
</code></pre>
|
||||
<p>如果 math.Pi 被確定爲特定類型, 比如 float64, 那麽結果精度可能會不一樣, 同時對於需要float32或complex128類型值的地方會需要一個明確的類型轉換:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> Pi64 <span class="hljs-typename">float64</span> = math.Pi
|
||||
|
||||
<span class="hljs-keyword">var</span> x <span class="hljs-typename">float32</span> = <span class="hljs-typename">float32</span>(Pi64)
|
||||
<span class="hljs-keyword">var</span> y <span class="hljs-typename">float64</span> = Pi64
|
||||
<span class="hljs-keyword">var</span> z <span class="hljs-typename">complex128</span> = <span class="hljs-typename">complex128</span>(Pi64)
|
||||
</code></pre>
|
||||
<p>對於常量面值, 不同的寫法對應不同的類型. 例如 0, 0.0, 0i, 和 '\u0000' 雖然有着相同的常量值, 但是它們分别對應無類型的整數,無類型的浮點數,無類型的複數,和無類型的字符等不同的常量類型. 同樣, true 和 false 也是無類型的布爾類型, 字符串面值常量是無類型的字符串.</p>
|
||||
<p>前面説過除法運算符 / 根據操作數的類型生成對應類型的結果. 因此, 不同寫法的常量除法表達式可能對應不同的結果:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> f <span class="hljs-typename">float64</span> = <span class="hljs-number">212</span>
|
||||
fmt.Println((f - <span class="hljs-number">32</span>) * <span class="hljs-number">5</span> / <span class="hljs-number">9</span>) <span class="hljs-comment">// "100"; (f - 32) * 5 is a float64</span>
|
||||
fmt.Println(<span class="hljs-number">5</span> / <span class="hljs-number">9</span> * (f - <span class="hljs-number">32</span>)) <span class="hljs-comment">// "0"; 5/9 is an untyped integer, 0</span>
|
||||
fmt.Println(<span class="hljs-number">5.0</span> / <span class="hljs-number">9.0</span> * (f - <span class="hljs-number">32</span>)) <span class="hljs-comment">// "100"; 5.0/9.0 is an untyped float</span>
|
||||
</code></pre>
|
||||
<p>隻有常量可以是無類型的. 當一個無類型的常量被賦值給一個變量, 就像上面的第一行語句, 或者是像其餘三個語句中右邊表達式中含有明確類型的值, 無類型的常量將會被隱式轉換爲對應的類型, 如果可能的話.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> f <span class="hljs-typename">float64</span> = <span class="hljs-number">3</span> + <span class="hljs-number">0i</span> <span class="hljs-comment">// untyped complex -> float64</span>
|
||||
f = <span class="hljs-number">2</span> <span class="hljs-comment">// untyped integer -> float64</span>
|
||||
f = <span class="hljs-number">1e123</span> <span class="hljs-comment">// untyped floating-point -> float64</span>
|
||||
f = <span class="hljs-string">'a'</span> <span class="hljs-comment">// untyped rune -> float64</span>
|
||||
</code></pre>
|
||||
<p>上面的語句相當於:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> f <span class="hljs-typename">float64</span> = <span class="hljs-typename">float64</span>(<span class="hljs-number">3</span> + <span class="hljs-number">0i</span>)
|
||||
f = <span class="hljs-typename">float64</span>(<span class="hljs-number">2</span>)
|
||||
f = <span class="hljs-typename">float64</span>(<span class="hljs-number">1e123</span>)
|
||||
f = <span class="hljs-typename">float64</span>(<span class="hljs-string">'a'</span>)
|
||||
</code></pre>
|
||||
<p>無論是隱式或顯式, 將一種類型轉換爲另一種類型要求目標可以表示原始值. 對於浮點數和複數, 可能會有舍入處理:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> (
|
||||
deadbeef = <span class="hljs-number">0xdeadbeef</span> <span class="hljs-comment">// untyped int with value 3735928559</span>
|
||||
a = <span class="hljs-typename">uint32</span>(deadbeef) <span class="hljs-comment">// uint32 with value 3735928559</span>
|
||||
b = <span class="hljs-typename">float32</span>(deadbeef) <span class="hljs-comment">// float32 with value 3735928576 (rounded up)</span>
|
||||
c = <span class="hljs-typename">float64</span>(deadbeef) <span class="hljs-comment">// float64 with value 3735928559 (exact)</span>
|
||||
d = <span class="hljs-typename">int32</span>(deadbeef) <span class="hljs-comment">// compile error: constant overflows int32</span>
|
||||
e = <span class="hljs-typename">float64</span>(<span class="hljs-number">1e309</span>) <span class="hljs-comment">// compile error: constant overflows float64</span>
|
||||
f = <span class="hljs-typename">uint</span>(-<span class="hljs-number">1</span>) <span class="hljs-comment">// compile error: constant underflows uint</span>
|
||||
)
|
||||
</code></pre>
|
||||
<p>對於一個沒有顯式類型的變量聲明(包括短變量聲明語法), 無類型的常量會被隱式轉爲默認的變量類型, 就像下面的例子:</p>
|
||||
<pre><code class="lang-Go">i := <span class="hljs-number">0</span> <span class="hljs-comment">// untyped integer; implicit int(0)</span>
|
||||
r := <span class="hljs-string">'\000'</span> <span class="hljs-comment">// untyped rune; implicit rune('\000')</span>
|
||||
f := <span class="hljs-number">0.0</span> <span class="hljs-comment">// untyped floating-point; implicit float64(0.0)</span>
|
||||
c := <span class="hljs-number">0i</span> <span class="hljs-comment">// untyped complex; implicit complex128(0i)</span>
|
||||
</code></pre>
|
||||
<p>註意默認類型是規則的: 無類型的整數常量默認轉換爲int, 對應不確定的尺寸, 但是浮點數好複數常量則默認轉換爲float64和complex128. Go語言本身併沒有不確定的尺寸的浮點數和複數類型, 因爲如何不知道浮點數類型的話很難寫齣正確的數值算法.</p>
|
||||
<p>如果要給變量一個不同的類型, 我們必鬚顯式地將無類型的常量轉化爲所需的類型, 或給聲明的變量指定類型, 像下面例子這樣:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> i = <span class="hljs-typename">int8</span>(<span class="hljs-number">0</span>)
|
||||
<span class="hljs-keyword">var</span> i <span class="hljs-typename">int8</span> = <span class="hljs-number">0</span>
|
||||
</code></pre>
|
||||
<p>當嚐試將這些無類型的常量轉爲一個接口值時(見第7章), 這些默認類型將顯得尤爲重要, 因爲要靠它們明確接口對應的動態類型.</p>
|
||||
<pre><code class="lang-Go">fmt.Printf(<span class="hljs-string">"%T\n"</span>, <span class="hljs-number">0</span>) <span class="hljs-comment">// "int"</span>
|
||||
fmt.Printf(<span class="hljs-string">"%T\n"</span>, <span class="hljs-number">0.0</span>) <span class="hljs-comment">// "float64"</span>
|
||||
fmt.Printf(<span class="hljs-string">"%T\n"</span>, <span class="hljs-number">0i</span>) <span class="hljs-comment">// "complex128"</span>
|
||||
fmt.Printf(<span class="hljs-string">"%T\n"</span>, <span class="hljs-string">'\000'</span>) <span class="hljs-comment">// "int32" (rune)</span>
|
||||
</code></pre>
|
||||
<p>現在我們已經講述了Go語言中全部的基礎數據類型. 下一步將演示如何用基礎數據類型組合成數組或結構體等複雜數據類型, 然後構建用於解決實際編程問題的數據結構, 這將是第四章的討論主題.</p>
|
||||
|
||||
|
||||
</section>
|
||||
@@ -2074,7 +2201,7 @@
|
||||
<a href="../ch3/ch3-05.html" class="navigation navigation-prev " aria-label="Previous page: 字符串"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../ch4/ch4.html" class="navigation navigation-next " aria-label="Next page: 復閤數據類型"><i class="fa fa-angle-right"></i></a>
|
||||
<a href="../ch4/ch4.html" class="navigation navigation-next " aria-label="Next page: 複合數據類型"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
96
ch3/ch3.html
96
ch3/ch3.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3" data-chapter-title="基礎數據類型" data-filepath="ch3/ch3.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3" data-chapter-title="基礎數據類型" data-filepath="ch3/ch3.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,8 +2024,8 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h1 id="第3章-基礎數據類型">第3章 基礎數據類型</h1>
|
||||
<p>雖然從底層而言,所有的數據都是比特,但計算機操作的是固定位數的數,如整數、浮點數、比特組、內存地址。將這些數,進一步組織在一起,可錶達更多的對象,如數據包、像素點、詩歌,甚至任何對象.Go提供了豐富的數據組織形式,這依賴於Go內置的數據類型。這些內置的數據類型,兼顧了硬件的特性和錶達復雜數據結構的便捷性。</p>
|
||||
<p>Go將數據類型分為四類:基礎類型、復閤類型、引用類型和接口類型。本章介紹基礎類型,包括:數字,字符串和佈爾型。復閤數據類型——數組(§4.1)和結構體(§4.2)——通過組閤簡單類型,錶達更加復雜的數據結構。引用類型包括指鍼(§2.3.2)、切片(§4.2))字典(§4.3)、函數(§5)、通道(§8).雖然種類很多,但它們都是對程序中一個變量或狀態的間接引用。這意味着對任一引用的脩改都會影響所有該引用的拷貝。我們將在第7章介紹接口類型。</p>
|
||||
<p>雖然從底層而言,所有的數據都是比特,但計算機操作的是固定位數的數,如整數、浮點數、比特組、內存地址。將這些數,進一步組織在一起,可表達更多的對象,如數據包、像素點、詩歌,甚至任何對象.Go提供了豐富的數據組織形式,這依賴於Go內置的數據類型。這些內置的數據類型,兼顧了硬件的特性和表達複雜數據結構的便捷性。</p>
|
||||
<p>Go將數據類型分爲四類:基礎類型、複合類型、引用類型和接口類型。本章介紹基礎類型,包括:數字,字符串和布爾型。複合數據類型——數組(§4.1)和結構體(§4.2)——通過組合簡單類型,表達更加複雜的數據結構。引用類型包括指針(§2.3.2)、切片(§4.2))字典(§4.3)、函數(§5)、通道(§8).雖然種類很多,但它們都是對程序中一個變量或狀態的間接引用。這意味着對任一引用的脩改都會影響所有該引用的拷貝。我們將在第7章介紹接口類型。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="4.1" data-chapter-title="數組" data-filepath="ch4/ch4-01.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="4.1" data-chapter-title="數組" data-filepath="ch4/ch4-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2071,7 +2035,7 @@
|
||||
</div>
|
||||
|
||||
|
||||
<a href="../ch4/ch4.html" class="navigation navigation-prev " aria-label="Previous page: 復閤數據類型"><i class="fa fa-angle-left"></i></a>
|
||||
<a href="../ch4/ch4.html" class="navigation navigation-prev " aria-label="Previous page: 複合數據類型"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../ch4/ch4-02.html" class="navigation navigation-next " aria-label="Next page: 切片"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="4.2" data-chapter-title="切片" data-filepath="ch4/ch4-02.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="4.2" data-chapter-title="切片" data-filepath="ch4/ch4-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="4.3" data-chapter-title="字典" data-filepath="ch4/ch4-03.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="4.3" data-chapter-title="字典" data-filepath="ch4/ch4-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="4.4" data-chapter-title="結構體" data-filepath="ch4/ch4-04.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="4.4" data-chapter-title="結構體" data-filepath="ch4/ch4-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="4.5" data-chapter-title="JSON" data-filepath="ch4/ch4-05.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="4.5" data-chapter-title="JSON" data-filepath="ch4/ch4-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="4.6" data-chapter-title="文本和HTML模闆" data-filepath="ch4/ch4-06.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="4.6" data-chapter-title="文本和HTML模闆" data-filepath="ch4/ch4-06.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
|
||||
96
ch4/ch4.html
96
ch4/ch4.html
@@ -5,7 +5,7 @@
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>復閤數據類型 | Go编程语言</title>
|
||||
<title>複合數據類型 | Go编程语言</title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="generator" content="GitBook 2.5.2">
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="4" data-chapter-title="復閤數據類型" data-filepath="ch4/ch4.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="4" data-chapter-title="複合數據類型" data-filepath="ch4/ch4.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2059,7 +2023,7 @@
|
||||
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h1 id="第四章-復閤數據類型">第四章 復閤數據類型</h1>
|
||||
<h1 id="第四章-複合數據類型">第四章 複合數據類型</h1>
|
||||
<p>TODO</p>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="5.1" data-chapter-title="函數聲明" data-filepath="ch5/ch5-01.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="5.1" data-chapter-title="函數聲明" data-filepath="ch5/ch5-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="5.2" data-chapter-title="遞歸" data-filepath="ch5/ch5-02.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="5.2" data-chapter-title="遞歸" data-filepath="ch5/ch5-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="5.3" data-chapter-title="多返迴值" data-filepath="ch5/ch5-03.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="5.3" data-chapter-title="多返迴值" data-filepath="ch5/ch5-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="5.4" data-chapter-title="錯誤" data-filepath="ch5/ch5-04.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="5.4" data-chapter-title="錯誤" data-filepath="ch5/ch5-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="5.5" data-chapter-title="函數值" data-filepath="ch5/ch5-05.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="5.5" data-chapter-title="函數值" data-filepath="ch5/ch5-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="5.6" data-chapter-title="匿名函數" data-filepath="ch5/ch5-06.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="5.6" data-chapter-title="匿名函數" data-filepath="ch5/ch5-06.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="5.7" data-chapter-title="可變參數" data-filepath="ch5/ch5-07.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="5.7" data-chapter-title="可變參數" data-filepath="ch5/ch5-07.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="5.8" data-chapter-title="Deferred函數" data-filepath="ch5/ch5-08.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="5.8" data-chapter-title="Deferred函數" data-filepath="ch5/ch5-08.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="5.9" data-chapter-title="Panic異常" data-filepath="ch5/ch5-09.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="5.9" data-chapter-title="Panic異常" data-filepath="ch5/ch5-09.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user