W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
原文鏈接:https://gopl-zh.github.io/ch5/ch5-03.html
在Go中,一個(gè)函數(shù)可以返回多個(gè)值。我們已經(jīng)在之前例子中看到,許多標(biāo)準(zhǔn)庫中的函數(shù)返回2個(gè)值,一個(gè)是期望得到的返回值,另一個(gè)是函數(shù)出錯(cuò)時(shí)的錯(cuò)誤信息。下面的例子會(huì)展示如何編寫多返回值的函數(shù)。
下面的程序是findlinks的改進(jìn)版本。修改后的findlinks可以自己發(fā)起HTTP請(qǐng)求,這樣我們就不必再運(yùn)行fetch。因?yàn)镠TTP請(qǐng)求和解析操作可能會(huì)失敗,因此findlinks聲明了2個(gè)返回值:鏈接列表和錯(cuò)誤信息。一般而言,HTML的解析器可以處理HTML頁面的錯(cuò)誤結(jié)點(diǎn),構(gòu)造出HTML頁面結(jié)構(gòu),所以解析HTML很少失敗。這意味著如果findlinks函數(shù)失敗了,很可能是由于I/O的錯(cuò)誤導(dǎo)致的。
gopl.io/ch5/findlinks2
func main() {
for _, url := range os.Args[1:] {
links, err := findLinks(url)
if err != nil {
fmt.Fprintf(os.Stderr, "findlinks2: %v\n", err)
continue
}
for _, link := range links {
fmt.Println(link)
}
}
}
// findLinks performs an HTTP GET request for url, parses the
// response as HTML, and extracts and returns the links.
func findLinks(url string) ([]string, error) {
resp, err := http.Get(url)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
return nil, fmt.Errorf("getting %s: %s", url, resp.Status)
}
doc, err := html.Parse(resp.Body)
resp.Body.Close()
if err != nil {
return nil, fmt.Errorf("parsing %s as HTML: %v", url, err)
}
return visit(nil, doc), nil
}
在findlinks中,有4處return語句,每一處return都返回了一組值。前三處return,將http和html包中的錯(cuò)誤信息傳遞給findlinks的調(diào)用者。第一處return直接返回錯(cuò)誤信息,其他兩處通過fmt.Errorf(§7.8)輸出詳細(xì)的錯(cuò)誤信息。如果findlinks成功結(jié)束,最后的return語句將一組解析獲得的連接返回給用戶。
在findlinks中,我們必須確保resp.Body被關(guān)閉,釋放網(wǎng)絡(luò)資源。雖然Go的垃圾回收機(jī)制會(huì)回收不被使用的內(nèi)存,但是這不包括操作系統(tǒng)層面的資源,比如打開的文件、網(wǎng)絡(luò)連接。因此我們必須顯式的釋放這些資源。
調(diào)用多返回值函數(shù)時(shí),返回給調(diào)用者的是一組值,調(diào)用者必須顯式的將這些值分配給變量:
links, err := findLinks(url)
如果某個(gè)值不被使用,可以將其分配給blank identifier:
links, _ := findLinks(url) // errors ignored
一個(gè)函數(shù)內(nèi)部可以將另一個(gè)有多返回值的函數(shù)調(diào)用作為返回值,下面的例子展示了與findLinks有相同功能的函數(shù),兩者的區(qū)別在于下面的例子先輸出參數(shù):
func findLinksLog(url string) ([]string, error) {
log.Printf("findLinks %s", url)
return findLinks(url)
}
當(dāng)你調(diào)用接受多參數(shù)的函數(shù)時(shí),可以將一個(gè)返回多參數(shù)的函數(shù)調(diào)用作為該函數(shù)的參數(shù)。雖然這很少出現(xiàn)在實(shí)際生產(chǎn)代碼中,但這個(gè)特性在debug時(shí)很方便,我們只需要一條語句就可以輸出所有的返回值。下面的代碼是等價(jià)的:
log.Println(findLinks(url))
links, err := findLinks(url)
log.Println(links, err)
準(zhǔn)確的變量名可以傳達(dá)函數(shù)返回值的含義。尤其在返回值的類型都相同時(shí),就像下面這樣:
func Size(rect image.Rectangle) (width, height int)
func Split(path string) (dir, file string)
func HourMinSec(t time.Time) (hour, minute, second int)
雖然良好的命名很重要,但你也不必為每一個(gè)返回值都取一個(gè)適當(dāng)?shù)拿?。比如,按照慣例,函數(shù)的最后一個(gè)bool類型的返回值表示函數(shù)是否運(yùn)行成功,error類型的返回值代表函數(shù)的錯(cuò)誤信息,對(duì)于這些類似的慣例,我們不必思考合適的命名,它們都無需解釋。
如果一個(gè)函數(shù)所有的返回值都有顯式的變量名,那么該函數(shù)的return語句可以省略操作數(shù)。這稱之為bare return。
// CountWordsAndImages does an HTTP GET request for the HTML
// document url and returns the number of words and images in it.
func CountWordsAndImages(url string) (words, images int, err error) {
resp, err := http.Get(url)
if err != nil {
return
}
doc, err := html.Parse(resp.Body)
resp.Body.Close()
if err != nil {
err = fmt.Errorf("parsing HTML: %s", err)
return
}
words, images = countWordsAndImages(doc)
return
}
func countWordsAndImages(n *html.Node) (words, images int) { /* ... */ }
按照返回值列表的次序,返回所有的返回值,在上面的例子中,每一個(gè)return語句等價(jià)于:
return words, images, err
當(dāng)一個(gè)函數(shù)有多處return語句以及許多返回值時(shí),bare return 可以減少代碼的重復(fù),但是使得代碼難以被理解。舉個(gè)例子,如果你沒有仔細(xì)的審查代碼,很難發(fā)現(xiàn)前2處return等價(jià)于 return 0,0,err(Go會(huì)將返回值 words和images在函數(shù)體的開始處,根據(jù)它們的類型,將其初始化為0),最后一處return等價(jià)于 return words, image, nil?;谝陨显?,不宜過度使用bare return。
練習(xí) 5.5: 實(shí)現(xiàn)countWordsAndImages。(參考練習(xí)4.9如何分詞)
練習(xí) 5.6: 修改gopl.io/ch3/surface(§3.2)中的corner函數(shù),將返回值命名,并使用bare return。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: