Go指南/习题解答
2017-12-18 小文字背景
Go Tour
练习题,解答。
Go指南/解答
练习:Stringers
让 IPAddr 类型实现 fmt.Stringer 以便用点分格式输出地址。
例如,IPAddr{1, 2, 3, 4} 应当输出 "1.2.3.4"。
- 解答
package main
import "fmt"
import "strconv"
type IPAddr [4]byte
// TODO: Add a "String() string" method to IPAddr.
func (addr IPAddr) String() string {
readableip := ""
last := len(addr) - 1
for i, c := range addr {
readableip += strconv.Itoa(int(c))
if i != last {
readableip += "."
}
}
return readableip
}
func main() {
addrs := map[string]IPAddr{
"loopback": {127, 0, 0, 1},
"googleDNS": {8, 8, 8, 8},
}
for n, a := range addrs {
fmt.Printf("%v: %v\n", n, a.String())
}
}
练习:slice
实现 Pic 。它返回一个长度为 dy 的 slice,其中每个元素是一个长度为 dx 且元素类型为8位无符号整数的 slice。当你运行这个程序时, 它会将每个整数作为对应像素的灰度值(好吧,其实是蓝度)并显示这个 slice 所对应的图像。
计算每个像素的灰度值的方法由你决定;几个有意思的选择包括 (x+y)/2、x*y 和 x^y 。
(需要使用循环来分配 [][]uint8 中的每个 []uint8 。)
(使用 uint8(intValue) 来在类型之间进行转换。)
- 解答
package main
import "golang.org/x/tour/pic"
func Pic(dx, dy int) [][]uint8 {
var p = make([][]uint8,dy,dy)
for i,_:=range p {
p[i]=make([]uint8,dx,dx)
}
return p
}
func main() {
pic.Show(Pic)
}
练习:map
实现 WordCount。它应当返回一个含有 s 中每个 “词” 个数的 map。函数 wc.Test 针对这个函数执行一个测试用例,并输出成功还是失败。
你会发现 strings.Fields 很有帮助。
- 解答
package main
import (
"golang.org/x/tour/wc"
"strings"
"fmt"
)
func WordCount(s string) map[string]int {
q:=strings.Fields(s)
m:=make(map[string]int)
for _,c:=range q {
m[c]+=1
}
fmt.Println("%v", m)
return m
}
func main() {
wc.Test(WordCount)
}
练习:斐波纳契闭包
现在来通过函数做些有趣的事情。
实现一个 fibonacci 函数,返回一个函数(一个闭包)可以返回连续的斐波纳契数。
< 23/24 > exercise-fibonacci-closure.go 语法高亮
- 解答
package main
import "fmt"
// fibonacci 函数会返回一个返回 int 的函数。
func fibonacci() func() int {
first:=0
second:=1
index:=0
return func() int {
if index == 0 {
index++
return 0
}
if index == 1 {
index++
return 1
}
cur:=first+second
first=second
second=cur
return cur
}
}
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
练习:错误
从先前的练习中复制 Sqrt 函数,并修改使其返回 error 值。
由于不支持复数,当 Sqrt 接收到一个负数时,应当返回一个非 nil 的错误值。
创建一个新类型
type ErrNegativeSqrt float64
为其实现
func (e ErrNegativeSqrt) Error() string
使其成为一个 error, 该方法就可以让 ErrNegativeSqrt(-2).Error() 返回 `"cannot Sqrt negative number: -2"`。
*注意:* 在 Error 方法内调用 fmt.Sprint(e) 将会让程序陷入死循环。可以通过先转换 e 来避免这个问题:fmt.Sprint(float64(e))。请思考这是为什么呢?
修改 Sqrt 函数,使其接受一个负数时,返回 ErrNegativeSqrt 值。
- 解答
package main
import (
"fmt"
)
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
return "cannot Sqrt negtive number: "+fmt.Sprint(float64(e))
}
func Sqrt(x float64) (float64, error) {
if x<0 {
return x, ErrNegativeSqrt(x)
}
z:=1.0
for i:=0;i<10;i++{
z=z-(z*z-x)/2/z
}
return z, nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
练习:Reader
实现一个 Reader 类型,它不断生成 ASCII 字符 'A' 的流。
- 解答
package main
import "golang.org/x/tour/reader"
type MyReader struct{}
// TODO: Add a Read(b []byte) (int, error) method to MyReader.
func (r MyReader) Read(b []byte) (int, error) {
b[0]='A'
return 1, nil
}
func main() {
reader.Validate(MyReader{})
}
练习:rot13Reader
一个常见模式是 io.Reader 包裹另一个 io.Reader,然后通过某种形式修改数据流。
例如,gzip.NewReader 函数接受 io.Reader(压缩的数据流)并且返回同样实现了 io.Reader 的 *gzip.Reader(解压缩后的数据流)。
编写一个实现了 io.Reader 的 rot13Reader, 并从一个 io.Reader 读取, 利用 rot13 代换密码对数据流进行修改。
已经帮你构造了 rot13Reader 类型。 通过实现 Read 方法使其匹配 io.Reader。
- 解答
package main
import (
"io"
"os"
"strings"
)
type rot13Reader struct {
r io.Reader
}
func (r rot13Reader) Read(b []byte) (int, error) {
n, e := r.r.Read(b)
for i := 0; i < n; i++ {
item := b[i]
if (item <= 'Z' && item > 'M') || (item <= 'z' && item > 'm') {
b[i] = item - 13
} else if (item >= 'A' && item <= 'M') || (item >= 'a' && item <= 'm') {
b[i] = item + 13
} else {
b[i] = item
}
}
return n, e
}
func main() {
s := strings.NewReader("Lbh penpxrq gur pbqr!")
r := rot13Reader{s}
io.Copy(os.Stdout, &r)
}
练习:等价二叉树
1. 实现 Walk 函数。
2. 测试 Walk 函数。
函数 tree.New(k) 构造了一个随机结构的二叉树,保存了值 k,2k,3k,...,10k。 创建一个新的 channel ch 并且对其进行步进:
go Walk(tree.New(1), ch)
然后从 channel 中读取并且打印 10 个值。应当是值 1,2,3,...,10。
3. 用 Walk 实现 Same 函数来检测是否 t1 和 t2 存储了相同的值。
4. 测试 Same 函数。
Same(tree.New(1), tree.New(1)) 应当返回 true,而 Same(tree.New(1), tree.New(2)) 应当返回 false。
- 解答
package main
import "golang.org/x/tour/tree"
import "fmt"
// Walk 步进 tree t 将所有的值从 tree 发送到 channel ch。
func Walk(t *tree.Tree, ch chan int) {
ch <- t.Value
if t.Left != nil {
Walk(t.Left, ch)
}
if t.Right != nil {
Walk(t.Right, ch)
}
}
// Same 检测树 t1 和 t2 是否含有相同的值。
func Same(t1, t2 *tree.Tree) bool {
ch1 := make(chan int)
ch2 := make(chan int)
go Walk(t1, ch1)
go Walk(t2, ch2)
result := make(map[int]int)
for i := 0; i < 10; i++ {
value1 := <-ch1
value2 := <-ch2
result[value1]++
result[value2]--
}
for _, e := range result {
if e == 0 {
continue
}
return false
}
return true
}
func main() {
fmt.Println("tree.New(1) =tree.New(1):", Same(tree.New(1), tree.New(1)))
fmt.Println("tree.New(1) =tree.New(2):", Same(tree.New(1), tree.New(2)))
}