diff --git a/ch5/ch5-06.md b/ch5/ch5-06.md index 44c2445..80ff175 100644 --- a/ch5/ch5-06.md +++ b/ch5/ch5-06.md @@ -101,7 +101,7 @@ visitAll := func(items []string) { } ``` -在topsort中,首先對prereqs中的key排序,再調用visitAll。因爲prereqs映射的是數組而不是更複雜的map,所以數據的遍歷次序是固定的,這意味着你每次運行topsort得到的輸出都是一樣的。 topsort的輸出結果如下: +在topsort中,首先對prereqs中的key排序,再調用visitAll。因爲prereqs映射的是切片而不是更複雜的map,所以數據的遍歷次序是固定的,這意味着你每次運行topsort得到的輸出都是一樣的。 topsort的輸出結果如下: ``` 1: intro to programming @@ -119,7 +119,7 @@ visitAll := func(items []string) { 13: programming languages ``` -讓我們迴到findLinks這個例子。我們將代碼移動到了links包下,將函數重命名爲Extract,在第八章我們會再次用到這個函數。新的匿名函數被引入,用於替換原來的visit函數。該匿名函數負責將新連接添加到數組中。在Extract中,使用forEachNode遍歷HTML頁面,由於Extract隻需要在遍歷結點前操作結點,所以forEachNode的post參數被傳入nil。 +讓我們迴到findLinks這個例子。我們將代碼移動到了links包下,將函數重命名爲Extract,在第八章我們會再次用到這個函數。新的匿名函數被引入,用於替換原來的visit函數。該匿名函數負責將新連接添加到切片中。在Extract中,使用forEachNode遍歷HTML頁面,由於Extract隻需要在遍歷結點前操作結點,所以forEachNode的post參數被傳入nil。 ```Go gopl.io/ch5/links @@ -234,7 +234,7 @@ http://research.swtch.com/gotour 當所有發現的鏈接都已經被訪問或電腦的內存耗盡時,程序運行結束。 -**練習5.10:** 重寫topoSort函數,用map代替數組併移除對key的排序代碼。驗證結果的正確性(結果不唯一)。 +**練習5.10:** 重寫topoSort函數,用map代替切片併移除對key的排序代碼。驗證結果的正確性(結果不唯一)。 **練習5.11:** 現在線性代數的老師把微積分設爲了前置課程。完善topSort,使其能檢測有向圖中的環。 diff --git a/ch5/ch5-07.md b/ch5/ch5-07.md index 7b31d7e..7913323 100644 --- a/ch5/ch5-07.md +++ b/ch5/ch5-07.md @@ -1,3 +1,68 @@ ## 5.7. 可變參數 -TODO +參數數量可變的函數稱爲爲可變參數函數。典型的例子就是fmt.Printf和類似函數。Printf首先接收一個的必備參數,之後接收任意個數的後續參數。 + +在聲明可變參數函數時,需要在參數列表的最後一個參數類型之前加上省略符號“...”,這表示該函數會接收任意數量的該類型參數。 + +```Go +gopl.io/ch5/sum +func sum(vals...int) int { + total := 0 + for _, val := range vals { + total += val + } + return total +} +``` +sum函數返迴任意個int型參數的和。在函數體中,vals被看作是類型爲[] int的切片。sum可以接收任意數量的int型參數: + +```Go +fmt.Println(sum()) // "0" +fmt.Println(sum(3)) // "3" +fmt.Println(sum(1, 2, 3, 4)) // "10" +``` + +在上面的代碼中,調用者隱式的創建一個數組,併將原始參數複製到數組中,再把數組的一個切片作爲參數傳給被調函數。如果原始參數已經是切片類型,我們該如何傳遞給sum?隻需在最後一個參數後加上省略符。下面的代碼功能與上個例子中最後一條語句相同。 + +```Go +values := []int{1, 2, 3, 4} +fmt.Println(sum(values...)) // "10" +``` + +雖然在可變參數函數內部,...int 型參數的行爲看起來很像切片類型,但實際上,可變參數函數和以切片作爲參數的函數是不同的。 + +```Go +func f(...int) {} +func g([]int) {} +fmt.Printf("%T\n", f) // "func(...int)" +fmt.Printf("%T\n", g) // "func([]int)" +``` + +可變參數函數經常被用於格式化字符串。下面的errorf函數構造了一個以行號開頭的,經過格式化的錯誤信息。函數名的後綴f是一種通用的命名規范,代表該可變參數函數可以接收Printf風格的格式化字符串。 + +```Go +func errorf(linenum int, format string, args...interface{}) +{ + fmt.Fprintf(os.Stderr, "Line %d: ", linenum) + fmt.Fprintf(os.Stderr, format, args…) + fmt.Fprintln(os.Stderr) +} +linenum, name := 12, "count" +errorf(linenum, "undefined: %s", name) // "Line 12: +undefined: count" +``` + +interfac{}表示函數的最後一個參數可以接收任意類型,我們會在第7章詳細介紹。 + +**練習5.15:** 編寫類似sum的可變參數函數max和min。考慮不傳參時,max和min該如何處理,再編寫至少接收1個參數的版本。 + +**練習5.16:**編寫多參數版本的strings.Join。 + +**練習5.17:**編寫多參數版本的ElementsByTagName,函數接收一個HTML結點樹以及任意數量的標籤名,返迴與這些標籤名匹配的所有元素。下面給出了2個例子: + +```Go +func ElementsByTagName(doc *html.Node, name...string) +[]*html.Node +images := ElementsByTagName(doc, "img") +headings := ElementsByTagName(doc, "h1", "h2", "h3", "h4") +``` \ No newline at end of file