《go语言圣经》练习答案--第五章

练习 5.2: 编写函数,记录在HTML树中出现的同名元素的次数。

package main

import (
	"fmt"
	"golang.org/x/net/html"
	"os"
)

func main() {
	doc, err := html.Parse(os.Stdin)
	if err != nil {
		fmt.Fprintf(os.Stderr, "findlinks1: %v\n", err)
		os.Exit(1)
	}
	elements := make(map[string]int)
	visit(elements, doc)
	for e, c := range elements {
		fmt.Printf("%s\t%d\n", e, c)
	}
}
func visit(elements map[string]int, n *html.Node) {
	if n.Type == html.ElementNode {
		elements[n.Data]++
	}
	for i := n.FirstChild; i != nil; i = i.NextSibling {
		visit(elements, i)
	}
}

 输出

script  11
path    4
code    1
h2      4
link    3
a       134
aside   1

 练习 5.3: 编写函数输出所有text结点的内容。注意不要访问<script><style>元素,因为这些元素对浏览者是不可见的。

package main

import (
	"fmt"
	"golang.org/x/net/html"
	"os"
)

func main() {
	doc, err := html.Parse(os.Stdin)
	if err != nil {
		fmt.Fprintf(os.Stderr, "findlinks1: %v\n", err)
		os.Exit(1)
	}
	visit(doc)
}
func visit(n *html.Node) {
	if n != nil && n.Type == html.ElementNode {
		if n.Data == "script" || n.Data == "style" {
			return
		}
	}
	if n.Type == html.TextNode {
		fmt.Println(n.Data)
	}
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		visit(c)
	}
}

练习 5.4: 扩展visit函数,使其能够处理其他类型的结点,如images、scripts和style sheets。

 var filter = map[string]string{
	"a":      "href",
	"img":    "src",
	"script": "src",
}

func visit(links []string, n *html.Node) []string {
	for k, v := range filter {
		if n.Type == html.ElementNode && n.Data == k {
			for _, a := range n.Attr {
				if a.Key == v {
					links = append(links, a.Val)
				}
			}
		}
	}
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		links = visit(links, c)
	}
	return links
}
 

练习 5.5: 实现countWordsAndImages。

func countWordsAndImages(n *html.Node) (words, images int) {
	if n.Type == html.TextNode {
		scanner := bufio.NewScanner(strings.NewReader(n.Data))
		scanner.Split(bufio.ScanWords)
		for scanner.Scan() {
			words++
		}
	}
	if n.Type == html.ElementNode && n.Data == "img" {
		images++
	}
	return words, images
}

练习 5.6: 修改gopl.io/ch3/surface (§3.2) 中的corner函数,将返回值命名,并使用bare return。

func corner(i, j int) (sx, sy float64) {
	// Find point (x,y) at corner of cell (i,j).
	x := xyrange * (float64(i)/cells - 0.5)
	y := xyrange * (float64(j)/cells - 0.5)

	// Compute surface height z.
	z := f(x, y)

	// Project (x,y,z) isometrically onto 2-D SVG canvas (sx,sy).
	sx = width/2 + (x-y)*cos30*xyscale
	sy = height/2 + (x+y)*sin30*xyscale - z*zscale
	return
}

练习 5.9: 编写函数expand,将s中的"foo"替换为f("foo")的返回值。

func expand(s string, f func(string) string) string {
	str := strings.Replace(s, "foo", f("foo"), -1)
	return str
}

练习5.10: 重写topoSort函数,用map代替切片并移除对key的排序代码。验证结果的正确性(结果不唯一)。

package main

import (
	"fmt"
)

var prereqs = map[string][]string{
	"算法":  {"数据结构"},
	"微积分": {"线性代数"},
	"编译器": {
		"数据结构",
		"形式语言",
		"计算机组成",
	},
	"数据结构":  {"离散数学"},
	"数据库":   {"数据结构"},
	"离散数学":  {"编程入门"},
	"形式语言":  {"离散数学"},
	"计算机网络": {"操作系统"},
	"操作系统":  {"数据结构", "计算机组成"},
	"编程语言":  {"数据结构", "计算机组成"},
}

func main() {
	for k, v := range topoSort(prereqs) {
		fmt.Printf("%d:\t%s\n", k, v)
	}
}

func topoSort(m map[string][]string) map[int]string {

	order := make(map[int]string)
	seen := make(map[string]bool)
	index := 1
	var visitAll func(items []string)
	visitAll = func(items []string) {
		for _, item := range items {
			if !seen[item] {
				seen[item] = true
				visitAll(m[item])
				//order = append(order, item)
				order[index] = item
				index++
			}
		}
	}
	var keys []string
	for key := range m {
		keys = append(keys, key)
	}
	visitAll(keys)
	return order
}

练习5.15: 编写类似sum的可变参数函数max和min。考虑不传参时,max和min该如何处理,再编写至少接收1个参数的版本。

func max(vals ...int) (m int) {
	if len(vals) == 0 {
		return 0
	}
	if len(vals) == 1 {
		return vals[0]
	}
	m = vals[0]
	for i := 1; i < len(vals); i++ {
		if m > vals[i] {
			m = vals[i]
		}
	}
	return m
}
func min(vals ...int) (m int) {
	if len(vals) == 0 {
		return 0
	}
	if len(vals) == 1 {
		return vals[0]
	}
	m = vals[0]
	for i := 1; i < len(vals); i++ {
		if m < vals[i] {
			m = vals[i]
		}
	}
	return m
}

练习5.18:不修改fetch的行为,重写fetch函数,要求使用defer机制关闭文件。

func fetch(url string) (filename string, n int64, err error) {
	resp, err := http.Get(url)
	if err != nil {
		return "", 0, err
	}
	defer resp.Body.Close()
	local := path.Base(resp.Request.URL.Path)
	if local == "/" {
		local = "index.html"
	}
	f, err := os.Create(local)
	if err != nil {
		return "", 0, err
	}

	defer func() {
		if closeErr := f.Close(); err == nil {
			err = closeErr
		}
	}()
	// Close file, but prefer error from Copy, if any.
	n, err = io.Copy(f, resp.Body)
	return local, n, err
}

 练习5.19: 使用panic和recover编写一个不包含return语句但能返回一个非零值的函数。

func rzero(a int) (r int) {
	defer func() {
		if p := recover(); p != nil {
			r = a
		}
	}()
	panic(a)
}

 

posted @ 2022-03-22 18:15  随风而逝的白色相簿  阅读(273)  评论(0编辑  收藏  举报