37
官方教程 A Tour of Go Excercises 我的答案总结及讲解
source link: https://www.tuicool.com/articles/BJzqQzj
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
这两天学完了 A Tour of Go 官方的语法教学,里面有很多的 Excercise(训练题)。希望对大家有用,如果有其他人也写过,并觉得我写的不对的,求教!:heart:
Exercise: Loops and Functions
题目
- 给一个 number
x
,我们通过 loop 和 function 来找到其平方根z
,即z² = x
- tour.golang.org/flowcontrol…
解答
package main import ( "fmt" "math" ) func Sqrt(x float64) float64 { z := x/2 for i:= 0; math.Abs(z*z - x) > 0.0000000001; i++ { z -= (z*z - x) / (2*z) fmt.Println(i, "z:", z, "z^2 -x:", z*z - x) } return z } func main() { fmt.Println(Sqrt(1000)) } 复制代码
-
z := x/2
这个是猜测的初始值(也可以像是题目里的 hint 写的设置成 1) -
math.Abs(z*z - x) > 0.0000000001
用最优解的逻辑就是给了一个 tolerance0.0000000001
,即我们用计算公式算出来的z²
与x
的差值已经足够小,我们认定预估的z
算是一个近似准确值。
Exercise: Slices
题目
- 写一个
Pic
函数来生成一个[][]uint8
的 2D 图片(即可说是 Array of Array)。它的大小由参数(dx, dy int)
决定,这个有dy
个数组,每个数组里又有一个长度为dx
的数组。而相关的位置上pic[y][x]
是这个图片的 bluescale(只有蓝色)数值,格式为uint8
。 - tour.golang.org/moretypes/1…
解答
package main import "golang.org/x/tour/pic" func Pic(dx, dy int) [][]uint8 { pic := make([][]uint8, dy) for i := range pic { pic[i] = make([]uint8, dx) for j := range pic[i] { pic[i][j] = uint8(i*j + j*j) } } return pic } func main() { pic.Show(Pic) } 复制代码
-
pic := make([][]uint8, dy)
先建一个数组,长度是dy
,数组里每个元素的内容是一个数组[]uint8
-
pic[i] = make([]uint8, dx)
在数组里的第i
个元素里,我们再创造一个[]uint8
数组,长度为dx
-
pic[i][j] = uint8(i*j + j*j)
表示我们设计的 bluesacle 计算公式里,pic[i][j]
位置的数值是uint8(i*j + j*j)
(这里你可以随意改几个,能看到很多不同的效果哦!)
Exercise: Maps
题目
- 实现一个函数
WordCount
,它可以回复一个map
里面包含输入字符串中出现的单词 word 及相应出现的次数。 - 例如:"I love you you you you",返回
map[string]int{"I":1, "love":1, "you":3}
- tour.golang.org/moretypes/2…
解答
package main import ( "golang.org/x/tour/wc" "strings" ) func WordCount(s string) map[string]int { m := make(map[string]int) words := strings.Fields(s) for _, word := range words { m[word] = m[word] + 1 } return m } func main() { wc.Test(WordCount) } 复制代码
-
strings.Files(s)
这个函数会自动切分一个字符串到一个数组,每个数组里是一个 word - 建立
map
然后在数组里每当某一个word
出现,就相应的增加 1
Exercise: Fibonacci closure
题目
- 实现一个
fibonacci
函数,使其返回一个函数 - 这个函数会连续的输出斐波那契数列
- tour.golang.org/moretypes/2…
解答
package main import "fmt" // fibonacci is a function that returns // a function that returns an int. func fibonacci() func() int { a, b := 0, 1 return func() int { c := a a, b = b, a+b return c } } func main() { f := fibonacci() for i := 0; i < 10; i++ { fmt.Println(f()) } } 复制代码
- closure 也叫闭包,意思是一个函数中需要使用的某一个变量是在此函数外定义的
- 在上面的
func fibonacci() func() int
中,返回的是一个函数func() int
,而这个函数每次运行返回的是一个int
- 在
fibonacci()
中,变量a
和b
定义在函数fibonacci()
里,并被此函数返回的return func() int { ... }
函数引用到,也就是说在返回的函数里a
和b
两个变量一直存储在内存中,且数值会一直变化 -
f := fibonacci()
中f
是fibonacci()
返回的函数,在初始情况中,此时的a, b := 0, 1
- 以第一次
f()
调用为例:-
c := a
:c
赋值为a
即 0 -
a, b = b, a+b
:a
赋值为b
即 1,b
赋值为a+b
即 1 -
return c
,返回 0,也就是斐波那契数列的 第一个值
-
- 第二次
f()
调用,注意此时a
是 1,b
是 1:-
c := a
:c
赋值为a
即 1 -
a, b = b, a+b
:a
赋值为b
即 1,b
赋值为a+b
即 2 -
return c
,返回 1,也就是斐波那契数列的 第二个值
-
- 以此类推,循环中
f
函数被调用了 10 次,输出了斐波那契数列前 10 个值
Exercise: Stringers
题目
- 为
type IPAddr [4]byte
增加 Stringer Interface 函数来输出字符串,即IPAddr{1, 2, 3, 4}
print 为1.2.3.4
- tour.golang.org/methods/18
解答
package main import ( "fmt" "strings" "strconv" ) type IPAddr [4]byte // TODO: Add a "String() string" method to IPAddr. func (ip IPAddr) String() string { s := make([]string, len(ip)) for i, val := range ip { s[i] = strconv.Itoa(int(val)) } return fmt.Sprintf(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) } } 复制代码
- 引入
strconv.Itoa
,int to string -
IPAddr
是一个大小为 4 的[]byte
,我们生成一个[4]string
数组s
,每一个元素是 IP 地址中的一位,并且我们用strconv.Itoa(int(val))
把它转换为字符串 -
strings.Join(s, ".")
将字符串数组用"."
连起来 - 这样在使用
fmt.Printf("%v: %v\n", name, ip)
时,会对type IPAddr
默认调用其Stringer interface
下定义的String()
函数来输出
Exercise: Errors
题目
- 优化 Exercise: Loops and Functions 里写的
sqrt
函数,当参数是一个负数时,添加type ErrNegativeSqrt float64
,并通过定义func (e ErrNegativeSqrt) Error() string
从而使其为error
- tour.golang.org/methods/20
解答
package main import ( "fmt" "math" ) type ErrNegativeSqrt float64 func (e ErrNegativeSqrt) Error() string { return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e)) } func Sqrt(x float64) (float64, error) { if (x > 0) { z:= x/2 for i:= 0; math.Abs(z*z - x) > 0.0000000001; i++ { z -= (z*z - x) / (2*z) fmt.Println(i, "z:", z, "z^2 -x:", z*z - x) } return z, nil } else { return 0, ErrNegativeSqrt(x) } } 复制代码
-
error
类型是一个 built-in interface,需要定义Error() string
函数,测试一个error
type 是否是nil
是定义函数返回是否出错的方法。例如:i, err := strconv.Atoi("42")
返回值中i
代表函数返回数值,而err
如果不是nil
的话,则表示有错误发生 -
func (e ErrNegativeSqrt) Error() string
定义了ErrNegativeSqrt
属于error
的Error()
函数,也就简洁说明ErrNegativeSqrt
是一个error
-
func Sqrt(x float64) (float64, error)
函数返回两个数值,前者为参数的平方根,后者为error
,当后者不是nil
的时候,在Println
中会自动调用Error()
输出相应的错误信息字符串。
Exercise: Readers
题目
- 实现一个
Reader
type,以输出一个无限个'A'
的字符流 - tour.golang.org/methods/22
解答
package main import ( "fmt" "golang.org/x/tour/reader" ) type MyReader struct{} type ErrEmptyBuffer []byte func (b ErrEmptyBuffer) Error() string { return fmt.Sprintf("cannot read an empty buffer: %v", b) } // TODO: Add a Read([]byte) (int, error) method to MyReader. func (reader MyReader) Read(b []byte) (int, error) { bLength := len(b) if (bLength == 0) { return 0, ErrEmptyBuffer(b) } for i := range b { b[i] = 'A' } return bLength, nil } func main() { reader.Validate(MyReader{}) } 复制代码
- 因为
MyReader
会输出无限个'A'
,因此只要输入参数b []byte
不是一个空的 Buffer,就会写满 - 当
bLength == 0
也就是 Bufferb
是空时,返回ErrEmptyBuffer(b)
错误 - 只要不是空,就都填满
'A'
并返回bLength, nil
Exercise: rot13Reader
题目
- 实现一个
rot13Reader
type 使其包含一个io.Reader
使得其在执行Read
函数时,会自动根据rot13
来转化相应的字母字符。 - tour.golang.org/methods/23
解答
package main import ( "io" "os" "strings" ) type rot13Reader struct { r io.Reader } func rot13(c byte) byte { switch { case (c >= 'A' && c <= 'M') || (c >= 'a' && c <= 'm'): c += 13 case (c >= 'N' && c <= 'Z') || (c >= 'n' && c <= 'z'): c -= 13 } return c } func (reader *rot13Reader) Read(b []byte) (n int, err error) { n, err := reader.r.Read(b) for i := range b { b[i] = rot13(b[i]) } return n, err } func main() { s := strings.NewReader("Lbh penpxrq gur pbqr!") r := rot13Reader{s} io.Copy(os.Stdout, &r) } 复制代码
- 先根据
rot13
规则实现函数func rot13(c byte) byte
,也就是A-M
与N-Z
的兑换,以及a-m
与n-z
的兑换 - 定义
rot13Reader
的Read
函数,首先使用期包含的r
Reader 来读取数据,然后每一个数据都通过rot13
转换,最终返回相应的数字结果
Exercise: Images
题目
- 优化 Exercise: Slices 里实现的图片函数,这次实现一个
image.Image
图像而不仅仅是一个二维数组数据 - 定义一个
Image
type,并定义相应的image.Image
interface,其中-
ColorModel
使用color.RGBAModel
-
Bounds
使用image.Rectangle
type,并用image.Rect(0, 0, w, h)
定义 -
At
会返回具体图片像素点上的颜色,最终用color.RGBA{v, v, 255, 255}
定义
-
- tour.golang.org/methods/25
解答
package main import ( "golang.org/x/tour/pic" "image" "image/color" ) type Image struct{ W int H int } func (i Image) ColorModel() color.Model { return color.RGBAModel } func (i Image) Bounds() image.Rectangle { return image.Rect(0, 0, i.W, i.H) } func (i Image) At(x, y int) color.Color { v := uint8(x*y + y*y) return color.RGBA{v, v, 255, 255} } func main() { m := Image{200, 200} pic.ShowImage(m) } 复制代码
- 因为要用到
image.Rect
,color.RGBAModel
,color.RGBA
因此,我们引入"image"
和"image/color"
packages - 定义相关函数,即可得到结果,这里
At
函数我沿用了之前的图片颜色计算公式v := uint8(x*y + y*y)
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK