当前位置: 首页 > news >正文

GO语言圣经 第五章习题

练习5.1

修改findlinks代码中遍历n.FirstChild链表的部分,将循环调用visit,改成递归调用。

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

练习5.2

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

package mainimport ("fmt""os""golang.org/x/net/html"
)type NodeCount map[string]intfunc main() {doc, err := html.Parse(os.Stdin)if err != nil {fmt.Fprintf(os.Stderr, "findlinks1: %v\n", err)os.Exit(1)}nodeCount := NodeCount{}fill(&nodeCount, doc)fmt.Printf("%v", nodeCount)
}func fill(nc *NodeCount, cn *html.Node) {if cn.Type == html.ElementNode {(*nc)[cn.Data]++}for next := cn.FirstChild; next != nil; next = next.NextSibling {fill(nc, next)}
}

练习5.3

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

func getText(texts []string, n *html.Node) []string {if n.Type == html.TextNode {texts = append(texts, n.Data)}for c := n.FirstChild; c != nil; c = c.NextSibling {if c.Data == "script" || c.Data == "style" {continue}texts = getText(texts, c)}return texts
}

练习5.4

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

func visit(links []string, n *html.Node) []string {if n.Type == html.ElementNode && (n.Data == "a" || n.Data == "img" || n.Data == "link" || n.Data == "scripts") {for _, a := range n.Attr {if a.Key == "href" {// fmt.Println(n.Data)links = append(links, a.Val)}}}for c := n.FirstChild; c != nil; c = c.NextSibling {links = visit(links, c)}return links
}

练习5.5

实现countWordsAndImages。(参考练习4.9如何分词)

func countWordsAndImages(n *html.Node) (words, images int) {texts, images := visit(nil, 0, n)for _, v := range texts {words += len(strings.Split(v, " "))v = strings.Trim(strings.TrimSpace(v), "\r\n")if v == "" {continue}words += len(strings.Split(v, " "))}return
}func visit(texts []string, imgs int, n *html.Node) ([]string, int) {if n.Type == html.TextNode {texts = append(texts, n.Data)}if n.Type == html.ElementNode && (n.Data == "img") {imgs++}for c := n.FirstChild; c != nil; c = c.NextSibling {if c.Data == "script" || c.Data == "style" {continue}texts, imgs = visit(texts, imgs, c)}return texts, imgs
}

练习5.6

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

func corner(i, j int) (sx float64, 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*xyscalesy := height/2 + (x+y)*sin30*xyscale - z*zscalereturn
}

练习5.7

完善startElement和endElement函数,使其成为通用的HTML输出器。要求:输出注释结点,文本结点以及每个元素的属性(< a href=‘…’>)。使用简略格式输出没有孩子结点的元素(即用<img/>代替<img></img>)。编写测试,验证程序输出的格式正确。(详见11章)

func startElement(n *html.Node) {if n.Type == html.ElementNode {attr := ""for _, a := range n.Attr {attr += " " + a.Key + "=" + "\"" + a.Val + "\" "}fmt.Printf("%*s<%s%s", depth*2, "", n.Data, attr)depth++}if n.Type == html.ElementNode && n.FirstChild == nil && n.Data != "script" {fmt.Printf("/>\n")} else if n.Type == html.ElementNode {fmt.Printf(">\n")}if n.Type == html.TextNode {fmt.Printf("%*s %s\n", depth*2, "", n.Data)}
}
func endElement(n *html.Node) {if n.Type == html.ElementNode && n.FirstChild == nil && n.Data != "script" {depth--fmt.Printf("\n")return}if n.Type == html.ElementNode {depth--fmt.Printf("%*s</%s>\n", depth*2, "", n.Data)}
}

练习5.8

修改pre和post函数,使其返回布尔类型的返回值。返回false时,中止forEachNoded的遍历。使用修改后的代码编写ElementByID函数,根据用户输入的id查找第一个拥有该id元素的HTML元素,查找成功后,停止遍历。

func ElementByID(n *html.Node, id string) *html.Node {if n.Type == html.ElementNode {for _, a := range n.Attr {if a.Key == "id" && a.Val == id {return n}}}for c := n.FirstChild; c != nil; c = c.NextSibling {ElementByID(c, id)}return n
}

练习5.9

编写函数expand,将s中的"foo"替换为f(“foo”)的返回值。

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

练习5.10

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

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

练习5.11

博客

练习5.12

gopl.io/ch5/outline2(5.5节)的startElement和endElement共用了全局变量depth,将它们修改为匿名函数,使其共享outline中的局部变量。

func outline(url string) (string, error) {resp, err := http.Get(url)if err != nil {return "", err}doc, _ := html.Parse(resp.Body)//使用匿名函数实现var depth intvar startElement func(n *html.Node)var endElement func(n *html.Node)startElement = func (n *html.Node) {if n.Type == html.ElementNode {fmt.Printf("%*s<%s>\n", depth*2, "", n.Data)depth++}}endElement = func (n *html.Node) {if n.Type == html.ElementNode {depth--fmt.Printf("%*s</%s>\n", depth*2, "", n.Data)}}forEachNode(doc, startElement, endElement)resp.Body.Close()return "", nil
}

练习5.15

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

func max(vals ...int) (int, error) {var m int = math.NaN()if len(vals) == 0 { return m, fmt.Errorf("max: %s", "至少传递一个参数")}   for _, v := range vals {if m < v { m = v }   }   return m, nil 
}

练习5.16

编写多参数版本的strings.Join。

func join(sep string, strs ...string) string {var res stringfor i, v := range strs {if i == (len(strs) - 1) {res += v} else {res += v + sep}}return res
}

练习5.17

编写多参数版本的ElementsByTagName,函数接收一个HTML结点树以及任意数量的标签名,返回与这些标签名匹配的所有元素。

var nodes []*html.Node
func ElementsByTagName(n *html.Node, names ...string) []*html.Node {for _, name := range names {if n.Type == html.ElementNode && n.Data == name {nodes = append(nodes, n)}}for c := n.FirstChild; c != nil; c = c.NextSibling {ElementsByTagName(c, names...)}return nodes
}

练习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() {closeErr := f.close()if err == nil {err = closeErr}}()n, err = io.Copy(f, resp.Body)return local, n, err
}
http://www.lryc.cn/news/151426.html

相关文章:

  • 用kotlin 开发一个简单的多页面跳转
  • 记录我的tensorrt 部署yolov8
  • 什么是用户界面? 优漫动游
  • 基于 Docker 的 MySQL 主从复制搭建(Mac M1版本)
  • 【Locomotor运动模块】瞬移
  • 【负载均衡】常见的负载均衡策略有哪些?
  • ChatGPT如何应对紧急救援和医疗应急?
  • vue3 ref reactive响应式数据 赋值的问题
  • 【美团秋招】20230922小美的彩虹糖
  • 论文阅读_扩散模型_DM
  • 【每日运维】RockyLinux8.6升级OpenSSH9.4p1
  • libdrm全解析三十八 —— 源码全解析(35)
  • jar包和war包的区别
  • CloudCompare 二次开发(10)——点云投影到平面
  • 如何制作并运行 jar 程序
  • Hadoop MapReduce 调优参数
  • springboot 与 Redis整合
  • 如何高效地设计测试用例并评审
  • 基于python+Django知识图谱的医疗问答系统设计与实现
  • cuda编程常见问题
  • QTday3
  • docker镜像是如何导入的?
  • 四川大学874考研真题00-23
  • openGauss学习笔记-58 openGauss 高级特性-资源池化
  • centos升级cmake之相关问题解决
  • vcs仿真教程(查看断言)
  • 2023开学礼新疆石河子大学图书馆藏八一新书《乡村振兴战略下传统村落文化旅游设计》许少辉新财经理工
  • javaee spring aop 切入点表达式
  • js 获得元素的offsetLeft
  • 【Spring面试题】IOC控制反转和DI依赖注入(详解)