mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-12-17 11:14:20 +08:00
fmt
This commit is contained in:
100
ch5/ch5-02.md
100
ch5/ch5-02.md
@@ -11,25 +11,25 @@ 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)
|
||||
@@ -43,21 +43,21 @@ gopl.io/ch5/findlinks1
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
"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)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -66,17 +66,17 @@ visit函數遍歷HTML的節點樹,從每一個anchor元素的href屬性獲得l
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
@@ -109,21 +109,21 @@ http://www.google.com/intl/en/policies/privacy/
|
||||
```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)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -153,10 +153,10 @@ $ ./fetch https://golang.org | ./outline
|
||||
|
||||
大部分編程語言使用固定大小的函數調用棧,常見的大小從64KB到2MB不等。固定大小棧會限製遞歸的深度,當你用遞歸處理大量數據時,需要避免棧溢出;除此之外,還會導致安全性問題。與相反,Go語言使用可變棧,棧的大小按需增加(初始時很小)。這使得我們使用遞歸時不必考慮溢出和安全問題。
|
||||
|
||||
練習**5.1** :脩改findlinks代碼中遍歷n.FirstChild鏈表的部分,將循環調用visit,改成遞歸調用。
|
||||
**練習 5.1:** 脩改findlinks代碼中遍歷n.FirstChild鏈表的部分,將循環調用visit,改成遞歸調用。
|
||||
|
||||
練習**5.2** : 編寫函數,記録在HTML樹中出現的同名元素的次數。
|
||||
**練習 5.2:** 編寫函數,記録在HTML樹中出現的同名元素的次數。
|
||||
|
||||
練習**5.3** : 編寫函數輸出所有text結點的內容。註意不要訪問`<script>`和`<style>`元素,因爲這些元素對瀏覽者是不可見的。
|
||||
**練習 5.3:** 編寫函數輸出所有text結點的內容。註意不要訪問`<script>`和`<style>`元素,因爲這些元素對瀏覽者是不可見的。
|
||||
|
||||
練習**5.4** : 擴展vist函數,使其能夠處理其他類型的結點,如images、scripts和style sheets。
|
||||
**練習 5.4:** 擴展vist函數,使其能夠處理其他類型的結點,如images、scripts和style sheets。
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
```Go
|
||||
var f func(int) int
|
||||
f(3) // 此處f的值爲nil,會引起panic錯誤
|
||||
f(3) // 此處f的值爲nil, 會引起panic錯誤
|
||||
```
|
||||
|
||||
函數值可以與nil比較:
|
||||
|
||||
@@ -120,7 +120,7 @@ func trace(msg string) func() {
|
||||
|
||||
每一次bigSlowOperation被調用,程序都會記録函數的進入,退出,持續時間。(我們用time.Sleep模擬一個耗時的操作)
|
||||
|
||||
```powershell
|
||||
```
|
||||
$ go build gopl.io/ch5/trace
|
||||
$ ./trace
|
||||
2015/11/18 09:53:26 enter bigSlowOperation
|
||||
|
||||
@@ -4,7 +4,7 @@ Go的類型繫統會在編譯時捕獲很多錯誤,但有些錯誤隻能在運
|
||||
|
||||
一般而言,當panic異常發生時,程序會中斷運行,併立卽執行在該goroutine(可以先理解成線程,在第8章會詳細介紹)中被延遲的函數(defer 機製)。隨後,程序崩潰併輸出日誌信息。日誌信息包括panic value和函數調用的堆棧跟蹤信息。panic value通常是某種錯誤信息。對於每個goroutine,日誌信息中都會有與之相對的,發生panic時的函數調用堆棧跟蹤信息。通常,我們不需要再次運行程序去定位問題,日誌信息已經提供了足夠的診斷依據。因此,在我們填寫問題報告時,一般會將panic異常和日誌信息一併記録。
|
||||
|
||||
不是所有的panic異常都來自運行時,直接調用內置的panic函數也會引發panic異常;panic函數接受任何值作爲參數。 當某些不應該發生的場景發生時,我們就應該調用panic。比如,當程序到達了某條邏輯上不可能到達的路徑:
|
||||
不是所有的panic異常都來自運行時,直接調用內置的panic函數也會引發panic異常;panic函數接受任何值作爲參數。當某些不應該發生的場景發生時,我們就應該調用panic。比如,當程序到達了某條邏輯上不可能到達的路徑:
|
||||
|
||||
```Go
|
||||
switch s := suit(drawCard()); s {
|
||||
@@ -67,7 +67,7 @@ func f(x int) {
|
||||
|
||||
上例中的運行輸出如下:
|
||||
|
||||
```bash
|
||||
```
|
||||
f(3)
|
||||
f(2)
|
||||
f(1)
|
||||
@@ -78,7 +78,7 @@ defer 3
|
||||
|
||||
當f(0)被調用時,發生panic異常,之前被延遲執行的的3個fmt.Printf被調用。程序中斷執行後,panic信息和堆棧信息會被輸出(下面是簡化的輸出):
|
||||
|
||||
```powershell
|
||||
```
|
||||
panic: runtime error: integer divide by zero
|
||||
main.f(0)
|
||||
src/gopl.io/ch5/defer1/defer.go:14
|
||||
@@ -111,7 +111,7 @@ func printStack() {
|
||||
|
||||
printStack的簡化輸出如下(下面隻是printStack的輸出,不包括panic的日誌信息):
|
||||
|
||||
```bash
|
||||
```
|
||||
goroutine 1 [running]:
|
||||
main.printStack()
|
||||
src/gopl.io/ch5/defer2/defer.go:20
|
||||
|
||||
Reference in New Issue
Block a user