Go程序设计语言课后习题答案-第五章(函数)
第五章
- 函数声明
- 递归
- 多返回值
- 错误
- 函数变量
- 匿名函数
- 变长函数
- 延迟函数调用
- 宕机
- 恢复
练习5.1
改变findlinks程序,使用递归调用visit(而不是循环)遍历n.FirstChild链表
- 可怕的递归ヽ(*。>Д<)o゜可怕
- 循环还挺好理解的,可是改成递归真。。。不好理解
- 本题就修改一句就好了
1 | return visit(visit(links, n.FirstChild), n.NextSibling) |
练习5.2
写一个函数,用于统计HTML文档树内所有的元素个数,如p,div,span等
- 将传入的slice修改为map,其他不变
1 | package main |
练习5.3
写一个函数,用于输出HTML文档树种所有文本节点的内容.但不包括<script>
或<style>
元素,因为这些内容在web浏览器中是不可见的
- 还是差不多,递归读取到每一个节点,选择性输出
1 | package main |
练习5.4:
扩展visit函数,使之能够获得到其他种类的链接地址,比如图片、脚本或样式表的链接
用一个字典匹配所有的类型,先匹配n.Data,在遍历属性得到需要的地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22var 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
- ElementNode、TextNode、ComentNode、DoctypeNode、ErrorNode
- 判断为TextNode的时候统计字符个数,n.Data为img的时候图片数目累加
1 | func countWordsAndImages(n *html.Node) (words, images int) { |
练习5.6:
修改gopl.io/ch3/surface中的函数corner,以使用命名的结果以及裸返回语句
- 最简单的概念题了,返回参数规范化
- return的时候忽略后面的语句,不过我想我写代码的时候可能不会选择忽略
1 | func corner(i, j int) (sx, sy float64) { |
练习5.7:
开发startElement和endElement函数并应用到一个普通的HTML输出代码中.输出注释节点、文本节点和所有元素属性(<a href='...'>)
.当一个元素没有子节点时,使用简短的形式,比如<img/>
而不是<img></img>
.写一个测试程序保证输出可以正确解析
- 抱歉,我感觉挺复杂
练习5.8:
修改forEachNode使得pre和post函数返回一个布尔型的结果来确定遍历是否继续下去.使用它写一个函数ElementByID,该函数使用下面的函数签名并且找到第一个符合id属性的HTML元素.函数在找到符合条件的元素时应该尽快停止遍历.func 使用它写一个函数ElementByID(doc *html.Node, id string) *html.Node
- 终止递归,每次执行pre、post的时候选择判断返回值,如果为真表示找到直接进行return
- 因为传递给forEachNode的是html.Node的指针,当return的时候即为找到的第一个id,如果遍历完了还没有,最终返回值为n.FirstChild会为nil
1 | func ElementByID(doc *html.Node, id string) *html.Node { |
练习5.9:
写一个函数expand(s string,f func(string)string)string,该函数替换参数s中每一个子字符串”$foo”为f(“foo”)的返回值
- 感觉应该是类似于
${foo}
这样,否则$后面会有很长的字符串,不利于模板进行分割
1 | package main |
练习5.10:
重写topSort以使用map代替slice并去掉开头的排序.结果不是唯一的,验证这个结果是合法的拓扑排序
- 跳过┑( ̄Д  ̄)┍
练习5.11:
现在有”线性代数”这门课程,它的先决课程是”微积分”.拓展topSort以函数输出结果
- 跳过┑( ̄Д  ̄)┍
练习5.12:
5.5节(gopl.io/ch5/outline2)的startElement和endElement函数共享一个全局变量depth.把它们变为匿名函数以共享outline函数的一个局部变量
- 跳过┑( ̄Д  ̄)┍
练习5.13:
修改crawl函数保存找到的页面,根据需要创建目录.不要保存不同域名下的页面.比如本来的页面来自golang.org,那么就把它们保存下来但是不要保存vimeo.com下的页面
- 跳过┑( ̄Д  ̄)┍
练习5.14:
使用广度优先遍历搜索一个不同的拓扑结构.比如,你可以借鉴拓扑排序的例子里的课程依赖关系,计算机文件系统的分层结构,或者从当前城市的官网上下载公共汽车或者地铁的路线图.
- 跳过┑( ̄Д  ̄)┍
练习5.15:
模仿sum写两个变长函数max和min.当不带任何参数调用这些函数的时候应该怎么应对?编写类似函数的变种,要求至少需要一个参数
- 当缺少参数的时候直接panic
- 比较的时候直接使用math模块的最大值最小值使用
1 | package main |
练习5.16:
写一个变长版本的strings.Join函数
- 可以直接使用strings.Join返回
- 选择使用bytes.Buffer模块
1 | package main |
练习5.17:
写一个变长函数ElementsByTagname,已知一个HTML节点树和零个或多个名字,返回所有符合给出名字的元素
- 衔接前面的题目,遍历判断一下即可
1 | package main |
练习5.18:
不改变原本的行为,重写fetch函数以使用defer语句关闭打开的可写的文件
- 基本不变,直接将原有的写法放置到嵌套的defer函数内部,这样关闭失败的时候同样的逻辑会写入err
1 | func fetch(url string) (filename string, n int64, err error) { |
练习5.19:
使用panic和recover写一个函数,它没有return语句,但是能够返回一个非零的值
- 利用defer语句的作用域和函数同级,在defer语句中执行recover再设置函数返回值
1 | package main |