ch4-2, ch4-3: review

This commit is contained in:
chai2010 2016-01-06 21:17:08 +08:00
parent 336e74c44f
commit 491d771cdd
4 changed files with 15 additions and 21 deletions

View File

@ -42,9 +42,9 @@ func appendInt(x []int, y int) []int {
如果沒有足夠的增長空間的話appendInt函數則會先分配一個足夠大的slice用於保存新的結果先將輸入的x複製到新的空間然後添加y元素。結果z和輸入的x引用的將是不同的底層數組。
雖然通過循環複製元素更直接不過內置的copy函數可以方便地將一個slice複製另一個相同類型的slice。copy函數的第一個參數是要複製的目標slice第二個參數是源slice目標和源的位置順序和dst = src賦值語句是一致的。兩個slice可以共享同一個底層數組甚至有重疊也沒有問題。copy函數將返迴成功複製的元素的個數我們這里沒有用到等於兩個slice中較小的長度所以我們不用擔心覆蓋會超出目slice的范圍。
雖然通過循環複製元素更直接不過內置的copy函數可以方便地將一個slice複製另一個相同類型的slice。copy函數的第一個參數是要複製的目標slice第二個參數是源slice目標和源的位置順序和`dst = src`賦值語句是一致的。兩個slice可以共享同一個底層數組甚至有重疊也沒有問題。copy函數將返迴成功複製的元素的個數我們這里沒有用到等於兩個slice中較小的長度所以我們不用擔心覆蓋會超出目slice的范圍。
爲了效率新分配的數組一般略大於保存x和y所需要的最低大小。通過在每次擴展數組時直接將長度翻倍從而避免了多次內存分配也確保了添加單個元素操的平均時間是一個常數時間。這個程序演示了效果
爲了提高內存使用效率新分配的數組一般略大於保存x和y所需要的最低大小。通過在每次擴展數組時直接將長度翻倍從而避免了多次內存分配也確保了添加單個元素操的平均時間是一個常數時間。這個程序演示了效果
```Go
func main() {
@ -76,17 +76,17 @@ func main() {
![](../images/ch4-02.png)
在下一次迭代時i=4現在沒有新的空餘的空間了因此appendInt函數分配一個容量爲8的底層數組將x的4個元素[0 1 2 3]複製到新空間的開頭然後添加新的元素i新元素的值是4。新的y的長度是5容量是8後面有3個空閒的位置三次迭代都不需要分配新的空間。當前迭代中y和x是對應不底層數組的view。這次操作如圖4.3所示。
在下一次迭代時i=4現在沒有新的空餘的空間了因此appendInt函數分配一個容量爲8的底層數組將x的4個元素[0 1 2 3]複製到新空間的開頭然後添加新的元素i新元素的值是4。新的y的長度是5容量是8後面有3個空閒的位置三次迭代都不需要分配新的空間。當前迭代中y和x是對應不底層數組的view。這次操作如圖4.3所示。
![](../images/ch4-03.png)
內置的append函數可能使用比appendInt更複雜的內存擴展策略。因此通常我們併不知道append調用是否導致了內存的分配因此我們也不能確認新的slice和原始的slice是否引用的是相同的底層數組空間。同樣我們不能確認在原先的slice上的操作是否會影響到新的slice。因此通常是將append返迴的結果直接賦值給輸入的slice變量
內置的append函數可能使用比appendInt更複雜的內存擴展策略。因此通常我們併不知道append調用是否導致了內存的重新分配因此我們也不能確認新的slice和原始的slice是否引用的是相同的底層數組空間。同樣我們不能確認在原先的slice上的操作是否會影響到新的slice。因此通常是將append返迴的結果直接賦值給輸入的slice變量
```Go
runes = append(runes, r)
```
更新slice變量不僅對調用append函數是必要的實際上對應任何可能導致長度、容量或底層數組變化的操作都是必要的。要正確地使用slice需要記住盡管底層數組的元素是間接訪問但是slice本身的指針、長度和容量是直接訪問的。要更新這些信息需要像上面例子那樣一個顯式的賦值操作。從這個角度看slice併不是一個純粹的引用類型它實際上是一個類似下面結構體的聚合類型
更新slice變量不僅對調用append函數是必要的實際上對應任何可能導致長度、容量或底層數組變化的操作都是必要的。要正確地使用slice需要記住盡管底層數組的元素是間接訪問但是slice對應結構體本身的指針、長度和容量部分是直接訪問的。要更新這些信息需要像上面例子那樣一個顯式的賦值操作。從這個角度看slice併不是一個純粹的引用類型它實際上是一個類似下面結構體的聚合類型
```Go
type IntSlice struct {
@ -119,5 +119,3 @@ func appendInt(x []int, y ...int) []int {
```
爲了避免重複,和前面相同的代碼併沒有顯示。

View File

@ -32,7 +32,7 @@ fmt.Printf("%q\n", nonempty(data)) // `["one" "three"]`
fmt.Printf("%q\n", data) // `["one" "three" "three"]`
```
因此我們通常會這樣使用nonempty函數data = nonempty(data)。
因此我們通常會這樣使用nonempty函數`data = nonempty(data)`
nonempty函數也可以使用append函數實現
@ -48,9 +48,9 @@ func nonempty2(strings []string) []string {
}
```
無論如何實現以這種方式重用一個slice一般要求最多爲每個輸入值産生一個輸出值事實上很多算法都是用來過濾或合併序列中相鄰的元素。這種slice用法是比較複雜的技巧雖然使用到了slice的一些黑魔法,但是對於某些場合是比較清晰和有效的。
無論如何實現以這種方式重用一個slice一般要求最多爲每個輸入值産生一個輸出值,事實上很多這類算法都是用來過濾或合併序列中相鄰的元素。這種slice用法是比較複雜的技巧雖然使用到了slice的一些技巧,但是對於某些場合是比較清晰和有效的。
一個slice可以原來實現一個stack。最初給定的空slice對應一個空的stack然後可以使用append函數將新的值壓入stack
一個slice可以用來模擬一個stack。最初給定的空slice對應一個空的stack然後可以使用append函數將新的值壓入stack
```Go
stack = append(stack, v) // push v
@ -68,7 +68,7 @@ top := stack[len(stack)-1] // top of stack
stack = stack[:len(stack)-1] // pop
```
要刪除slice中間的某個元素併保存原有的元素順序可以通過內置的copy函數將後面的子slice向前一位複雜完成:
要刪除slice中間的某個元素併保存原有的元素順序可以通過內置的copy函數將後面的子slice向前依次移動一位完成:
```Go
func remove(slice []int, i int) []int {
@ -82,7 +82,7 @@ func main() {
}
```
如果刪除元素後不用保原來順序的話,我們可以簡單的用最後一個元素覆蓋被刪除的元素:
如果刪除元素後不用保原來順序的話,我們可以簡單的用最後一個元素覆蓋被刪除的元素:
```Go
func remove(slice []int, i int) []int {
@ -105,4 +105,3 @@ func main() {
**練習 4.6** 編寫一個函數原地將一個UTF-8編碼的[]byte類型的slice中相鄰的空格參考unicode.IsSpace替換成一個空格返迴
**練習 4.7** 脩改reverse函數用於原地反轉UTF-8編碼的[]byte。是否可以不用分配額外的內存

View File

@ -1,8 +1,8 @@
## 4.2. Slice
Slice切片代表變長的序列序列中每個元素都有相同的類型。一個slice類型一般寫作[]T其中T代表slice中元素的類型語法和數組很像隻是沒有固定長度而已。
Slice切片代表變長的序列序列中每個元素都有相同的類型。一個slice類型一般寫作[]T其中T代表slice中元素的類型slice的語法和數組很像,隻是沒有固定長度而已。
數組和slice之間有着緊密的聯繫。一個slice是一個輕量級的數據結構提供了訪問數組子序列或者全部元素的功能因爲slice的底層確實引用一個數組對象。一個slice由三個部分構成指針、長度和容量。指針指向第一個slice元素對應的底層數組元素的地址要註意的是slice的第一個元素併不一定就是數組的第一個元素。長度對應slice中元素的數目長度不能超過容量容量一般是從slice的開始位置到底層數據的結尾位置。內置的len和cap函數分别返迴slice的長度和容量。
數組和slice之間有着緊密的聯繫。一個slice是一個輕量級的數據結構提供了訪問數組子序列或者全部元素的功能而且slice的底層確實引用一個數組對象。一個slice由三個部分構成指針、長度和容量。指針指向第一個slice元素對應的底層數組元素的地址要註意的是slice的第一個元素併不一定就是數組的第一個元素。長度對應slice中元素的數目長度不能超過容量容量一般是從slice的開始位置到底層數據的結尾位置。內置的len和cap函數分别返迴slice的長度和容量。
多個slice之間可以共享底層的數據併且引用的數組部分區間可能重疊。圖4.1顯示了表示一年中每個月份名字的字符串數組還有重疊引用了該數組的兩個slice。數組這樣定義
@ -129,4 +129,3 @@ make([]T, len, cap) // same as make([]T, cap)[:len]
{% include "./ch4-02-1.md" %}
{% include "./ch4-02-2.md" %}

View File

@ -27,7 +27,7 @@ ages["alice"] = 31
ages["charlie"] = 34
```
因此另一種創建空的map的表達式是map[string]int{}。
因此另一種創建空的map的表達式是`map[string]int{}`
Map中的元素通過key對應的下標語法訪問
@ -48,7 +48,7 @@ delete(ages, "alice") // remove element ages["alice"]
ages["bob"] = ages["bob"] + 1 // happy birthday!
```
而且x += y和x++等簡短賦值語法也可以用在map上所以上面的代碼可以改寫成
而且`x += y``x++`等簡短賦值語法也可以用在map上所以上面的代碼可以改寫成
```Go
ages["bob"] += 1
@ -176,7 +176,7 @@ func main() {
}
```
Go程序員將這種忽略value的map當作一個字符串集合併非所有map[string]bool類型value都是無關緊要的有一些則可能會同時包含tue和false的值。
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類型。
@ -292,5 +292,3 @@ func hasEdge(from, to string) bool {
**練習 4.8** 脩改charcount程序使用unicode.IsLetter等相關的函數統計字母、數字等Unicode中不同的字符類别。
**練習 4.9** 編寫一個程序wordfreq程序報告輸入文本中每個單詞出現的頻率。在第一次調用Scan前先調用input.Split(bufio.ScanWords)函數,這樣可以按單詞而不是按行輸入。