From 60f822294de5ea53007deb01434da9f90e401293 Mon Sep 17 00:00:00 2001 From: chai2010 Date: Sat, 2 Jan 2016 21:34:50 +0800 Subject: [PATCH] ch5-2: fmt code --- ch5/ch5-02.md | 126 +++++++++++++++++++++++++++----------------------- 1 file changed, 69 insertions(+), 57 deletions(-) diff --git a/ch5/ch5-02.md b/ch5/ch5-02.md index 29af3f8..0c0f77c 100644 --- a/ch5/ch5-02.md +++ b/ch5/ch5-02.md @@ -6,73 +6,80 @@ 例子中調用golang.org/x/net/html的部分api如下所示。html.Parse函數讀入一組bytes.解析後,返迴html.node類型的HTML頁面樹狀結構根節點。HTML擁有很多類型的結點如text(文本),commnets(註釋)類型,在下面的例子中,我們 隻關註< name key='value' >形式的結點。 -``` +```Go golang.org/x/net/html package html + type Node struct { -Type NodeType -Data string -Attr []Attribute -FirstChild, NextSibling *Node + Type NodeType + Data string + Attr []Attribute + FirstChild, NextSibling *Node } + type NodeType int32 const ( -ErrorNode NodeType = iota -TextNode -DocumentNode -ElementNode -CommentNode -DoctypeNode + ErrorNode NodeType = iota + TextNode + DocumentNode + ElementNode + CommentNode + DoctypeNode ) type Attribute struct { -Key, Val string + Key, Val string } + func Parse(r io.Reader) (*Node, error) ``` + main函數解析HTML標準輸入,通過遞歸函數visit獲得links(鏈接),併打印出這些links: -``` +```Go gopl.io/ch5/findlinks1 -// Findlinks1 prints the links in an HTML document read -from standard input. +// Findlinks1 prints the links in an HTML document read from standard input. package main + import ( -"fmt" -"os" -"golang.org/x/net/html" + "fmt" + "os" + + "golang.org/x/net/html" ) + func main() { -doc, err := html.Parse(os.Stdin) -if err != nil { -fmt.Fprintf(os.Stderr, "findlinks1: %v\n", err) -os.Exit(1) -} -for _, link := range visit(nil, doc) { -fmt.Println(link) -} + doc, err := html.Parse(os.Stdin) + if err != nil { + fmt.Fprintf(os.Stderr, "findlinks1: %v\n", err) + os.Exit(1) + } + for _, link := range visit(nil, doc) { + fmt.Println(link) + } } ``` + visit函數遍歷HTML的節點樹,從每一個anchor元素的href屬性獲得link,將這些links存入字符串數組中,併返迴這個字符串數組。 -``` -// visit appends to links each link found in n and returns -the result. +```Go +// visit appends to links each link found in n and returns the result. func visit(links []string, n *html.Node) []string { -if n.Type == html.ElementNode && n.Data == "a" { -for _, a := range n.Attr { -if a.Key == "href" { -links = append(links, a.Val) -} -} -} -for c := n.FirstChild; c != nil; c = c.NextSibling { -links = visit(links, c) -} -return links + if n.Type == html.ElementNode && n.Data == "a" { + for _, a := range n.Attr { + if a.Key == "href" { + links = append(links, a.Val) + } + } + } + for c := n.FirstChild; c != nil; c = c.NextSibling { + links = visit(links, c) + } + return links } ``` + 爲了遍歷結點n的所有後代結點,每次遇到n的孩子結點時,visit遞歸的調用自身。這些孩子結點存放在FirstChild鏈表中。 讓我們以Go的主頁(golang.org)作爲目標,運行findlinks。我們以fetch(1.5章)的輸出作爲findlinks的輸入。下面的輸出做了簡化處理。 @@ -94,34 +101,35 @@ https://golang.org/dl/ /doc/tos.html http://www.google.com/intl/en/policies/privacy/ ``` -註意在頁面中出現的鏈接格式,在之後我們會介紹如何將這些鏈接,根據根路徑(https://golang.org)生成可以直接訪問的url。 + +註意在頁面中出現的鏈接格式,在之後我們會介紹如何將這些鏈接,根據根路徑( https://golang.org )生成可以直接訪問的url。 在函數outline中,我們通過遞歸的方式遍歷整個HTML結點樹,併輸出樹的結構。在outline內部,每遇到一個HTML元素標籤,就將其入棧,併輸出。 -``` +```Go gopl.io/ch5/outline func main() { -doc, err := html.Parse(os.Stdin) -if err != nil { -fmt.Fprintf(os.Stderr, "outline: %v\n", err) -os.Exit(1) -} -outline(nil, doc) + doc, err := html.Parse(os.Stdin) + if err != nil { + fmt.Fprintf(os.Stderr, "outline: %v\n", err) + os.Exit(1) + } + outline(nil, doc) } func outline(stack []string, n *html.Node) { -if n.Type == html.ElementNode { -stack = append(stack, n.Data) // push tag -fmt.Println(stack) -} -for c := n.FirstChild; c != nil; c = c.NextSibling { -outline(stack, c) -} + if n.Type == html.ElementNode { + stack = append(stack, n.Data) // push tag + fmt.Println(stack) + } + for c := n.FirstChild; c != nil; c = c.NextSibling { + outline(stack, c) + } } ``` 有一點值得註意:outline有入棧操作,但沒有相對應的出棧操作。當outline調用自身時,被調用者接收的是stack的拷貝。被調用者的入棧操作,脩改的是stack的拷貝,而不是調用者的stack,因對當函數返迴時,調用者的stack併未被脩改。 -下面是https://golang.org頁面的簡要結構: +下面是 https://golang.org 頁面的簡要結構: ``` $ go build gopl.io/ch5/outline @@ -140,11 +148,15 @@ $ ./fetch https://golang.org | ./outline [html body div div form div a] ... ``` + 正如你在上面實驗中所見,大部分HTML頁面隻需幾層遞歸就能被處理,但仍然有些頁面需要深層次的遞歸。 大部分編程語言使用固定大小的函數調用棧,常見的大小從64KB到2MB不等。固定大小棧會限製遞歸的深度,當你用遞歸處理大量數據時,需要避免棧溢出;除此之外,還會導致安全性問題。與相反,Go語言使用可變棧,棧的大小按需增加(初始時很小)。這使得我們使用遞歸時不必考慮溢出和安全問題。 練習**5.1** :脩改findlinks代碼中遍歷n.FirstChild鏈表的部分,將循環調用visit,改成遞歸調用。 + 練習**5.2** : 編寫函數,記録在HTML樹中出現的同名元素的次數。 -練習**5.3** : 編寫函數輸出所有text結點的內容。註意不要訪問< script >和< style >元素,因爲這些元素對瀏覽者是不可見的。 + +練習**5.3** : 編寫函數輸出所有text結點的內容。註意不要訪問`