携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第14天,点击查看活动详情
前言
之前在学习 Go 的官网教程时,发现它里面的练习好像没有答案,所以在这里分享一下自己写的解答,给新入门的 Gopher 提供一个对照,如果有疑问或者有更好的方法,欢迎大家在评论区里一起讨论。
英文版:A Tour of Go
中文版:Go 语言之旅
方法和接口模块
Stringer
英文版:golang.google.cn/tour/method…
题目简述
通过让 IPAddr 类型实现 fmt.Stringer 来打印点号分隔的地址。
例如,IPAddr{1, 2, 3, 4} 应当打印为 "1.2.3.4"。
解答
package main
import "fmt"
import "strings"
type IPAddr [4]byte
func (ip IPAddr) String() string {
s := make([]string, 0, len(ip))
for _, v := range ip {
s = append(s, fmt.Sprintf("%d", v))
}
return strings.Join(s, ".")
}
func main() {
hosts := map[string]IPAddr{
"loopback": {127, 0, 0, 1},
"googleDNS": {8, 8, 8, 8},
}
for name, ip := range hosts {
fmt.Printf("%v: %v\n", name, ip)
}
}
先创建一个和 ip 一样长(容量)的字符串切片,注意初始长度要为0,不然后面追加的时候就从初始长度后面开始加了。然后遍历 ip 只取值,通过 Sprintf 将字节转为字符串,注意这里不可以直接用类型转换。最后用 strings.Join 方法添加 . 即可。
我这里写的答案会更加泛用一点,如果仅对于这道题来说,你也可以直接:
return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3])
错误
英文版:golang.google.cn/tour/method…
题目简述
从之前的练习中复制 Sqrt 函数并修改它,使其接受一个负数时,返回 ErrNegativeSqrt (自定义的错误类型)值,打印错误值得到如 "cannot Sqrt negative number: -2" 的提示。
你需要创建一个新的类型:
type ErrNegativeSqrt float64
并为其实现:
func (e ErrNegativeSqrt) Error() string
以满足 error 接口, 与 fmt.Stringer 类似,fmt 包在打印值时会寻找 error 接口。
解答
package main
import (
"fmt"
"math"
)
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
return fmt.Sprint("cannot Sqrt negative number: ", float64(e))
}
func Sqrt(x float64) (float64, error) {
if x < 0 {
return x, ErrNegativeSqrt(x)
}
if x == 0 {
return 0, nil
}
z := x / 2
d := 1.0
for math.Abs(d) > 1e-15 {
d = (z*z - x) / (2 * z)
z -= d
}
return z, nil
}
func main() {
if v, err := Sqrt(-0.1); err != nil {
fmt.Println(err)
} else {
fmt.Println(v)
}
}
要注意一点是,在 Sprint 里打印 ErrNegativeSqrt 类型值时,会递归调用 Error 方法,所以应该先对其进行类型转换。
或者也可以使用 Sprintf 方法:
return fmt.Sprintf("cannot Sqrt negative number: %g", e)
Reader
英文版:golang.google.cn/tour/method…
题目简述
实现一个 Reader 类型,它产生一个 ASCII 字符 'A' 的无限流。
io.Reader 接口需要一个 Read 方法:
func (T) Read(b []byte) (n int, err error)
解答
package main
import "golang.org/x/tour/reader"
type MyReader struct{}
func (MyReader) Read(b []byte) (int, error) {
b[0] = 'A'
return 1, nil
}
func main() {
reader.Validate(MyReader{})
}
直接令 b[0]='A' , 返回填充的字节数为 1 比较省事,也符合题意。但如果切片大小为零可能会出现错误,读取效率也不高,所以一般来讲应该像下面这样写:
func (MyReader) Read(b []byte) (int, error) {
for i := range b {
b[i] = 'A'
}
return len(b), nil
}
rot13Reader
英文版:golang.google.cn/tour/method…
题目简述
有种常见的模式是一个 io.Reader 包装另一个 io.Reader,然后通过某种方式修改其数据流。
自定义一个 rot13Reader 类型,包含一个 Read 方法来实现 io.Reader 接口,该方法从另一个 io.Reader 中读取数据,通过应用 rot13 代换密码对数据流进行修改。
rot13 是一种简单的替换密码,它将字母表中前13个字母和后13个字母一一对应进行相互替换。编码和解码是相同的操作。
wikipedia ROT13
解答
package main
import (
"io"
"os"
"strings"
"unicode"
)
type rot13Reader struct {
r io.Reader
}
func (rot *rot13Reader) Read(b []byte) (int, error) {
n, err := rot.r.Read(b)
for i := range b {
if unicode.IsLetter(rune(b[i])) {
if b[i] >= 'A' && b[i] < 'N' || b[i] >= 'a' && b[i] < 'n' {
b[i] += 13
} else {
b[i] -= 13
}
}
}
return n, err
}
func main() {
s := strings.NewReader("Lbh penpxrq gur pbqr!")
r := rot13Reader{s}
io.Copy(os.Stdout, &r)
}
输出结果是: You cracked the code!
图像
英文版:golang.google.cn/tour/method…
题目简述
还记得之前编写的图片生成器 吗?我们再来编写另外一个,不过这次它将会返回一个 image.Image 的实现而非一个数据切片。
定义你自己的 Image 类型,实现必要的方法:
type Image interface {
ColorModel() color.Model
Bounds() Rectangle
At(x, y int) color.Color
}
并调用 pic.ShowImage。
Bounds 应当返回一个 image.Rectangle ,例如 image.Rect(0, 0, w, h)。
ColorModel 应当返回 color.RGBAModel。
At 应当返回一个颜色。上一个图片生成器的值 v 对应于此次的 color.RGBA{v, v, 255, 255}。
解答
package main
import (
"golang.org/x/tour/pic"
"image"
"image/color"
)
type Image struct {
w, h int
}
func (img Image) Bounds() image.Rectangle {
return image.Rect(0, 0, img.w, img.h)
}
func (Image) ColorModel() color.Model {
return color.RGBAModel
}
func (Image) At(x, y int) color.Color {
v := (x + y) / 2
return color.RGBA{uint8(v), uint8(v), 255, 255}
}
func main() {
m := Image{256, 256}
pic.ShowImage(m)
}
At 方法定义了每一个位置的像素是什么颜色。你可以像上一个图片生成器一样,通过更改 v 的公式来生成不同的图案;或者你也可以更改 color.RGBA 的值来进行更自由的创作。
总结
本篇文章是对方法和接口模块的习题解答,包括 fmt.Stringer, builtin.error, io.Reader, image.Image 这四个接口的实现。如果大家有更好的答案,欢迎在评论区留言。
最后,如果本篇文章对你有所帮助,求 点赞、收藏、评论,感谢支持 ✧(≖ ◡ ≖✿