diff --git a/ch4/ch4-03.md b/ch4/ch4-03.md index 23c6a8d..11f28b5 100644 --- a/ch4/ch4-03.md +++ b/ch4/ch4-03.md @@ -191,4 +191,106 @@ func Add(list []string) { m[k(list)]++ } func Count(list []string) int { return m[k(list)] } ``` -TODO +使用同樣的技術可以處理任何不可比較的key類型,而不僅僅是slice類型。它對於想使用自定義key比較函數的時候也很有用,例如在比較字符串的時候忽略大小寫。同時,輔助函數k(x)也不一定是字符串類型,它可以返迴任何可比較的類型,例如整數、數組或結構體等。 + +這是map的另一個例子,下面的程序用於統計輸入中每個Unicode碼點出現的次數。雖然Unicode全部碼點的數量鉅大,但是出現在特點文檔中的字符併沒有多少,使用map可以用比較自然的方式來跟蹤那些見過字符次數。 + +```Go +gopl.io/ch4/charcount + +// Charcount computes counts of Unicode characters. +package main + +import ( + "bufio" + "fmt" + "io" + "os" + "unicode" + "unicode/utf8" +) + +func main() { + counts := make(map[rune]int) // counts of Unicode characters + var utflen [utf8.UTFMax + 1]int // count of lengths of UTF-8 encodings + invalid := 0 // count of invalid UTF-8 characters + + in := bufio.NewReader(os.Stdin) + for { + r, n, err := in.ReadRune() // returns rune, nbytes, error + if err == io.EOF { + break + } + if err != nil { + fmt.Fprintf(os.Stderr, "charcount: %v\n", err) + os.Exit(1) + } + if r == unicode.ReplacementChar && n == 1 { + invalid++ + continue + } + counts[r]++ + utflen[n]++ + } + fmt.Printf("rune\tcount\n") + for c, n := range counts { + fmt.Printf("%q\t%d\n", c, n) + } + fmt.Print("\nlen\tcount\n") + for i, n := range utflen { + if i > 0 { + fmt.Printf("%d\t%d\n", i, n) + } + } + if invalid > 0 { + fmt.Printf("\n%d invalid UTF-8 characters\n", invalid) + } +} +``` + +ReadRune方法執行UTF-8解碼併返迴三個值:解碼的rune字符的值,字符UTF-8編碼後的長度,和一個錯誤值。我們可預期的錯誤值隻有對應文件結尾的io.EOF。如果輸入的是無效的UTF-8編碼的字符,返迴的將是unicode.ReplacementChar無效字符,併且編碼長度是1。 + +charcount程序同時打印不同UTF-8編碼長度的字符數目。對此,map併不是一個合適的數據結構;因爲UTF-8編碼的長度總是從1到utf8.UTFMax(最大是4個字節),使用數組將更有效。 + +作爲一個實驗,我們用charcount程序對本身的字符進行了統計。雖然大部分是英語,但是也有一些非ASCII字符。下面是排名前10的非ASCII字符: + +![](../images/ch4-xx-01.png) + +下面是不同UTF-8編碼長度的字符的數目: + +``` +len count +1 765391 +2 60 +3 70 +4 0 +``` + +Map的value類型也可以是一個聚合類型,比如是一個map或slice。在下面的代碼中,圖graph的key類型是一個字符串,value類型map[string]bool代表一個字符串集合。從概念上將,graph將一個字符串類型的key映射到一組相關的字符串集合,它們指向新的graph的key。 + +```Go +gopl.io/ch4/graph + +var graph = make(map[string]map[string]bool) + +func addEdge(from, to string) { + edges := graph[from] + if edges == nil { + edges = make(map[string]bool) + graph[from] = edges + } + edges[to] = true +} + +func hasEdge(from, to string) bool { + return graph[from][to] +} +``` + +其中addEdge函數惰性初始化map是一個慣用方式,也就是説在每個值首次作爲key是才初始化。addEdge函數顯示了如何讓map的零值也能正常工作;卽使from到to的邊不存在,graph[from][to]依然可以返迴一個有意義的結果。 + +**練習 4.8:** 脩改charcount程序,使用unicode.IsLetter等相關的函數,統計字母、數字等Unicode中不同的字符類别。 + +**練習 4.9:** 編寫一個程序wordfreq程序,報告輸入文本中每個單詞出現的頻率。在第一次調用Scan前先調用input.Split(bufio.ScanWords)函數,這樣可以按單詞而不是按行輸入。 + +