Fixes #198
This commit is contained in:
chai2010
2016-01-18 11:22:04 +08:00
parent 884ada9cd0
commit 9666211cd7
71 changed files with 107 additions and 105 deletions

View File

@@ -35,7 +35,7 @@ q := [...]int{1, 2, 3}
fmt.Printf("%T\n", q) // "[3]int"
```
數組的長度是數組類型的一個組成部分,因此[3]int和[4]int是兩種不同的數組類型。數組的長度必是常量表達式,因爲數組的長度需要在編譯階段確定。
數組的長度是數組類型的一個組成部分,因此[3]int和[4]int是兩種不同的數組類型。數組的長度必是常量表達式,因爲數組的長度需要在編譯階段確定。
```Go
q := [3]int{1, 2, 3}

View File

@@ -38,7 +38,7 @@ func appendInt(x []int, y int) []int {
}
```
每次調用appendInt函數先檢測slice底層數組是否有足夠的容量來保存新添加的元素。如果有足夠空間的話直接擴展slice依然在原有的底層數組之上將新添加的y元素複製到新擴展的空間併返迴slice。因此輸入的x和輸出的z共享相同的底層數組。
每次調用appendInt函數先檢測slice底層數組是否有足夠的容量來保存新添加的元素。如果有足夠空間的話直接擴展slice依然在原有的底層數組之上將新添加的y元素複製到新擴展的空間併返迴slice。因此輸入的x和輸出的z共享相同的底層數組。
如果沒有足夠的增長空間的話appendInt函數則會先分配一個足夠大的slice用於保存新的結果先將輸入的x複製到新的空間然後添加y元素。結果z和輸入的x引用的將是不同的底層數組。

View File

@@ -80,7 +80,7 @@ fmt.Println(s) // "[2 3 4 5 0 1]"
要註意的是slice類型的變量s和數組類型的變量a的初始化語法的差異。slice和數組的字面值語法很類似它們都是用花括弧包含一繫列的初始化元素但是對於slice併沒有指明序列的長度。這會隱式地創建一個合適大小的數組然後slice的指針指向底層的數組。就像數組字面值一樣slice的字面值也可以按順序指定初始化值序列或者是通過索引和元素值指定或者的兩種風格的混合語法初始化。
和數組不同的是slice之間不能比較因此我們不能使用==操作符來判斷兩個slice是否含有全部相等元素。不過標準庫提供了高度優化的bytes.Equal函數來判斷兩個字節型slice是否相等[]byte但是對於其他類型的slice我們必自己展開每個元素進行比較:
和數組不同的是slice之間不能比較因此我們不能使用==操作符來判斷兩個slice是否含有全部相等元素。不過標準庫提供了高度優化的bytes.Equal函數來判斷兩個字節型slice是否相等[]byte但是對於其他類型的slice我們必自己展開每個元素進行比較:
```Go
func equal(x, y []string) bool {
@@ -98,7 +98,7 @@ func equal(x, y []string) bool {
上面關於兩個slice的深度相等測試運行的時間併不比支持==操作的數組或字符串更多但是爲何slice不直接支持比較運算符呢這方面有兩個原因。第一個原因一個slice的元素是間接引用的一個slice甚至可以包含自身。雖然有很多辦法處理這種情形但是沒有一個是簡單有效的。
第二個原因因爲slice的元素是間接引用的一個固定值的slice在不同的時間可能包含不同的元素因爲底層數組的元素可能會被脩改。併且Go語言中map等哈希表之類的數據結構的key隻做簡單的淺拷貝它要求在整個聲明週期中相等的key必對相同的元素。對於像指針或chan之類的引用類型==相等測試可以判斷兩個是否是引用相同的對象。一個針對slice的淺相等測試的==操作符可能是有一定用處的也能臨時解決map類型的key問題但是slice和數組不同的相等測試行爲會讓人睏惑。因此安全的做飯是直接禁止slice之間的比較操作。
第二個原因因爲slice的元素是間接引用的一個固定值的slice在不同的時間可能包含不同的元素因爲底層數組的元素可能會被脩改。併且Go語言中map等哈希表之類的數據結構的key隻做簡單的淺拷貝它要求在整個聲明週期中相等的key必對相同的元素。對於像指針或chan之類的引用類型==相等測試可以判斷兩個是否是引用相同的對象。一個針對slice的淺相等測試的==操作符可能是有一定用處的也能臨時解決map類型的key問題但是slice和數組不同的相等測試行爲會讓人睏惑。因此安全的做飯是直接禁止slice之間的比較操作。
slice唯一合法的比較操作是和nil比較例如

View File

@@ -2,7 +2,7 @@
哈希表是一種巧妙併且實用的數據結構。它是一個無序的key/value對的集合其中所有的key都是不同的然後通過給定的key可以在常數時間複雜度內檢索、更新或刪除對應的value。
在Go語言中一個map就是一個哈希表的引用map類型可以寫爲map[K]V其中K和V分别對應key和value。map中所有的key都有相同的類型所以的value也有着相同的類型但是key和value之間可以是不同的數據類型。其中K對應的key必是支持==比較運算符的數據類型所以map可以通過測試key是否相等來判斷是否已經存在。雖然浮點數類型也是支持相等運算符比較的但是將浮點數用做key類型則是一個壞的想法正如第三章提到的最壞的情況是可能出現的NaN和任何浮點數都不相等。對於V對應的value數據類型則沒有任何的限製。
在Go語言中一個map就是一個哈希表的引用map類型可以寫爲map[K]V其中K和V分别對應key和value。map中所有的key都有相同的類型所以的value也有着相同的類型但是key和value之間可以是不同的數據類型。其中K對應的key必是支持==比較運算符的數據類型所以map可以通過測試key是否相等來判斷是否已經存在。雖然浮點數類型也是支持相等運算符比較的但是將浮點數用做key類型則是一個壞的想法正如第三章提到的最壞的情況是可能出現的NaN和任何浮點數都不相等。對於V對應的value數據類型則沒有任何的限製。
內置的make函數可以創建一個map
@@ -76,7 +76,7 @@ for name, age := range ages {
}
```
Map的迭代順序是不確定的併且不同的哈希函數實現可能導致不同的遍歷順序。在實踐中遍歷的順序是隨機的每一次遍歷的順序都不相同。這是故意的每次都使用隨機的遍歷順序可以強製要求程序不會依賴具體的哈希函數實現。如果要按順序遍歷key/value對我們必顯式地對key進行排序可以使用sort包的Strings函數對字符串slice進行排序。下面是常見的處理方式
Map的迭代順序是不確定的併且不同的哈希函數實現可能導致不同的遍歷順序。在實踐中遍歷的順序是隨機的每一次遍歷的順序都不相同。這是故意的每次都使用隨機的遍歷順序可以強製要求程序不會依賴具體的哈希函數實現。如果要按順序遍歷key/value對我們必顯式地對key進行排序可以使用sort包的Strings函數對字符串slice進行排序。下面是常見的處理方式
```Go
import "sort"
@@ -113,7 +113,7 @@ map上的大部分操作包括査找、刪除、len和range循環都可以安
ages["carol"] = 21 // panic: assignment to entry in nil map
```
在向map存數據前必先創建map。
在向map存數據前必先創建map。
通過key作爲索引下標來訪問map將産生一個value。如果key在map中是存在的那麽將得到與key對應的value如果key不存在那麽將得到value對應類型的零值正如我們前面看到的ages["bob"]那樣。這個規則很實用但是有時候可能需要知道對應的元素是否眞的是在map之中。例如如果元素類型是一個數字你可以需要區分一個已經存在的0和不存在而返迴零值的0可以像下面這樣測試
@@ -130,7 +130,7 @@ if age, ok := ages["bob"]; !ok { /* ... */ }
在這種場景下map的下標語法將産生兩個值第二個是一個布爾值用於報告元素是否眞的存在。布爾變量一般命名爲ok特别適合馬上用於if條件判斷部分。
和slice一樣map之間也不能進行相等比較唯一的例外是和nil進行比較。要判斷兩個map是否包含相同的key和value我們必通過一個循環實現:
和slice一樣map之間也不能進行相等比較唯一的例外是和nil進行比較。要判斷兩個map是否包含相同的key和value我們必通過一個循環實現:
```Go
func equal(x, y map[string]int) bool {
@@ -178,7 +178,7 @@ func main() {
Go程序員將這種忽略value的map當作一個字符串集合併非所有`map[string]bool`類型value都是無關緊要的有一些則可能會同時包含tue和false的值。
有時候我們需要一個map或set的key是slice類型但是map的key必是可比較的類型但是slice併不滿足這個條件。不過我們可以通過兩個步驟繞過這個限製。第一步定義一個輔助函數k將slice轉爲map對應的string類型的key確保隻有x和y相等時k(x) == k(y)才成立。然後創建一個key爲string類型的map在每次對map操作時先用k輔助函數將slice轉化爲string類型。
有時候我們需要一個map或set的key是slice類型但是map的key必是可比較的類型但是slice併不滿足這個條件。不過我們可以通過兩個步驟繞過這個限製。第一步定義一個輔助函數k將slice轉爲map對應的string類型的key確保隻有x和y相等時k(x) == k(y)才成立。然後創建一個key爲string類型的map在每次對map操作時先用k輔助函數將slice轉化爲string類型。
下面的例子演示了如何使用map來記録提交相同的字符串列表的次數。它使用了fmt.Sprintf函數將字符串列表轉換爲一個字符串以用於map的key通過%q參數忠實地記録每個字符串元素的信息

View File

@@ -50,7 +50,7 @@ func Bonus(e *Employee, percent int) int {
}
```
如果要在函數內部脩改結構體成員的話,用指針傳入是必因爲在Go語言中所有的函數參數都是值拷貝傳入的函數參數將不再是函數調用時的原始變量。
如果要在函數內部脩改結構體成員的話,用指針傳入是必因爲在Go語言中所有的函數參數都是值拷貝傳入的函數參數將不再是函數調用時的原始變量。
```Go
func AwardAnnualRaise(e *Employee) {

View File

@@ -52,7 +52,7 @@ w.Circle.Radius = 5
w.Spokes = 20
```
Go語言有一個特性讓我們隻聲明一個成員對應的數據類型而不指名成員的名字這類成員就叫匿名成員。匿名成員的數據類型必是命名的類型或指向一個命名的類型的指針。下面的代碼中Circle和Wheel各自都有一個匿名成員。我們可以説Point類型被嵌入到了Circle結構體同時Circle類型被嵌入到了Wheel結構體。
Go語言有一個特性讓我們隻聲明一個成員對應的數據類型而不指名成員的名字這類成員就叫匿名成員。匿名成員的數據類型必是命名的類型或指向一個命名的類型的指針。下面的代碼中Circle和Wheel各自都有一個匿名成員。我們可以説Point類型被嵌入到了Circle結構體同時Circle類型被嵌入到了Wheel結構體。
```Go
type Circle struct {
@@ -85,7 +85,7 @@ w = Wheel{8, 8, 5, 20} // compile error: unknown fields
w = Wheel{X: 8, Y: 8, Radius: 5, Spokes: 20} // compile error: unknown fields
```
結構體字面值必遵循形狀類型聲明時的結構,所以我們隻能用下面的兩種語法,它們彼此是等價的:
結構體字面值必遵循形狀類型聲明時的結構,所以我們隻能用下面的兩種語法,它們彼此是等價的:
```Go
gopl.io/ch4/embed
@@ -121,6 +121,6 @@ w.X = 8 // equivalent to w.circle.point.X = 8
但是在包外部因爲circle和point沒有導出不能訪問它們的成員因此簡短的匿名成員訪問語法也是禁止的。
到目前未知,我們看到匿名成員特性隻是對訪問嵌套成員的點運算符提供了簡短的語法。稍後,我們將會看到匿名成員併不要求是結構體類型;其實任何命令的類型都可以作爲結構體的匿名成員。但是爲什麽要嵌入一個沒有任何子成員類型的匿名成員類型呢?
到目前爲止,我們看到匿名成員特性隻是對訪問嵌套成員的點運算符提供了簡短的語法。稍後,我們將會看到匿名成員併不要求是結構體類型;其實任何命令的類型都可以作爲結構體的匿名成員。但是爲什麽要嵌入一個沒有任何子成員類型的匿名成員類型呢?
答案是匿名類型的方法集。簡短的點運算符語法可以用於選擇匿名成員嵌套的成員也可以用於訪問它們的方法。實際上外層的結構體不僅僅是獲得了匿名成員類型的所有成員而且也獲得了該類型導出的全部的方法。這個機製可以用於將一個有簡單行爲的對象組合成有複雜行爲的對象。組合是Go語言中面向對象編程的核心我們將在6.3節中專門討論。

View File

@@ -199,7 +199,7 @@ func SearchIssues(terms []string) (*IssuesSearchResult, error) {
}
```
在早些的例子中我們使用了json.Unmarshal函數來將JSON格式的字符串解碼爲字節slice。但是這個例子中我們使用了基於流式的解碼器json.Decoder它可以從一個輸入流解碼JSON數據盡管這不是必的。如您所料還有一個針對輸出流的json.Encoder編碼對象。
在早些的例子中我們使用了json.Unmarshal函數來將JSON格式的字符串解碼爲字節slice。但是這個例子中我們使用了基於流式的解碼器json.Decoder它可以從一個輸入流解碼JSON數據盡管這不是必的。如您所料還有一個針對輸出流的json.Encoder編碼對象。
我們調用Decode方法來填充變量。這里有多種方法可以格式化結構。下面是最簡單的一種以一個固定寬度打印每個issue但是在下一節我們將看到如果利用模闆來輸出複雜的格式。