正确解答A Tour of Go的练习问题
首先
我认为《Go之旅》是一本很好的入门教材,但是在练习问题中,有些问题初次看到时感到很难。有些问题即使查了也很难找到能直接运行的答案,所以我想留下答案供日后参考。
-
- そのままコピーして正しく動く
-
- 必要ないパッケージを使わない
- なるべくシンプルに書く
我们将采取以下解答策略。
以下是其他找到的答案链接:
– https://qiita.com/kroton/items/8622d5e9e38ff822070c
– https://gist.github.com/zyxar/2317744
– https://github.com/golang/tour/tree/master/solutions
– http://takatoshiono.hatenablog.com/entry/2016/09/08/001823
– http://blog.yuuk.io/entry/2014/02/16/183206
练习:循环和函数
作为使用函数和循环的简单练习,让我们尝试使用牛顿法来计算平方根。…问题链接:https://go-tour-jp.appspot.com/flowcontrol/8
只重复10次
请最初将该计算公式循环进行10次,检查对于各个不同的x值(如1、2、3等)结果与正确值的接近程度。
package main
import (
"fmt"
)
func Sqrt(x float64) float64 {
z := 1.0
for i := 0; i < 10; i++ {
z -= (z*z - x) / (2 * z)
}
return z
}
func main() {
fmt.Println(Sqrt(2))
}
如果与前一刻的差距变小就停下来。
请尝试修改循环,以便在循环之前计算出的变量z的值不再改变(或者变化非常小)时停止循环。
package main
import (
"fmt"
)
func Sqrt(x float64) float64 {
z := 1.0
for d := 1.0; d*d > 1e-10; z -= d {
d = (z*z - x) / (2 * z)
}
return z
}
func main() {
fmt.Println(Sqrt(2))
}
Exercise: 切片 (Qiē
我们来实现Pic函数。当我们执行这个程序时,应该会在下方显示生成的图像。这个函数需要实现将一个长度为dy的切片分配给一个长度为dx的切片,其中每个元素是8位无符号整数类型。图像将被解释为灰度图像(实际上是蓝黑图像)…问题详情请参考https://go-tour-jp.appspot.com/moretypes/18。
package main
import "golang.org/x/tour/pic"
func Pic(dx, dy int) [][]uint8 {
pic := make([][]uint8, dy)
for y := range pic {
pic[y] = make([]uint8, dx)
for x := range pic[y] {
pic[y][x] = uint8((x + y) / 2)
}
}
return pic
}
func main() {
pic.Show(Pic)
}
练习:地图
让我们来实现一个 WordCount 函数吧。该函数需要接收一个字符串 s 作为参数,并返回一个记录每个单词出现次数的映射。wc.Test 函数会执行测试套件,检查传入的函数是否成功,并将结果显示为成功或失败。…
问题链接:https://go-tour-jp.appspot.com/moretypes/23
package main
import (
"golang.org/x/tour/wc"
"strings"
)
func WordCount(s string) map[string]int {
m := make(map[string]int)
for _, w := range strings.Fields(s) {
m[w]++
}
return m
}
func main() {
wc.Test(WordCount)
}
练习:斐波那契闭包
让我们实现斐波那契函数。该函数返回一个返回连续斐波那契数(0、1、1、2、3、5、…)的函数(闭包)。
问题链接:https://go-tour-jp.appspot.com/moretypes/26
package main
import "fmt"
// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
a, b := 1, 0
return func() int {
a, b = b, a+b
return a
}
}
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
练习:紧绳
让我们实现一个IPAddr类型,以点分四段的形式输出IP地址。请实现fmt.Stringer接口…
问题:https://go-tour-jp.appspot.com/methods/18
package main
import "fmt"
type IPAddr [4]byte
func (ip IPAddr) String() string {
return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3])
}
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)
}
}
Exercise: Mistakes
练习:错误
请从以前的练习中复制Sqrt函数,并修复它以返回错误值。…
问题链接https://go-tour-jp.appspot.com/methods/20
package main
import (
"fmt"
)
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 {
return 0, ErrNegativeSqrt(x)
}
z := 1.0
for i := 0; i < 10; i++ {
z -= (z*z - x) / (2 * z)
}
return z, nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
锻炼:读者们
请实现一个 Reader 类型,用于输出无限流的 ASCII 字符 ‘A’。问题链接:https://go-tour-jp.appspot.com/methods/22
package main
import "golang.org/x/tour/reader"
type MyReader struct{}
func (r MyReader) Read(b []byte) (int, error) {
for i := range b {
b[i] = 'A'
}
return len(b), nil
}
func main() {
reader.Validate(MyReader{})
}
练习:rot13阅读器
请实现一个 rot13Reader,它实现了 io.Reader 接口,并将 ROT13 換字式暗号(置换密码)应用于所有字母进行解密。你可以在此链接找到该问题的详细说明:https://go-tour-jp.appspot.com/methods/23
package main
import (
"io"
"os"
"strings"
)
type rot13Reader struct {
r io.Reader
}
func (rot13 rot13Reader) Read(b []byte) (int, error) {
n, err := rot13.r.Read(b)
for i, v := range b {
switch {
case v >= 'A' && v < 'N', v >= 'a' && v < 'n':
b[i] += 13
case v >= 'N' && v <= 'Z', v >= 'n' && v <= 'z':
b[i] -= 13
}
}
return n, err
}
func main() {
s := strings.NewReader("Lbh penpxrq gur pbqr!")
r := rot13Reader{s}
io.Copy(os.Stdout, &r)
}
健身:图像
你还记得之前解决的图像生成器吗?这次,我们尝试返回实现了 image.Image 接口而不是数据切片的图像。…问题链接:https://go-tour-jp.appspot.com/methods/25
package main
import (
"golang.org/x/tour/pic"
"image"
"image/color"
)
type Image struct{}
func (i Image) ColorModel() color.Model {
return color.RGBAModel
}
func (i Image) Bounds() image.Rectangle {
return image.Rect(0, 0, 256, 256)
}
func (i Image) At(x, y int) color.Color {
return color.RGBA{uint8(x), uint8(y), 255, 255}
}
func main() {
m := Image{}
pic.ShowImage(m)
}
练习:等价的二叉树
在许多语言中,检查两个二叉树是否保持相同的顺序是相当复杂的。为了描述一种简单的解决方法,我们将利用Go的并发性和通道。…
问题链接:https://go-tour-jp.appspot.com/concurrency/7
继续问题链接:https://go-tour-jp.appspot.com/concurrency/8
package main
import (
"fmt"
"golang.org/x/tour/tree"
)
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
walk(t, ch)
close(ch)
}
func walk(t *tree.Tree, ch chan int) {
if t == nil {
return
}
walk(t.Left, ch)
ch <- t.Value
walk(t.Right, ch)
}
// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
c1, c2 := make(chan int), make(chan int)
go Walk(t1, c1)
go Walk(t2, c2)
for {
v1, ok1 := <-c1
v2, ok2 := <-c2
switch {
case !ok1, !ok2:
return ok1 == ok2
case v1 != v2:
return false
}
}
}
func main() {
ch := make(chan int)
go Walk(tree.New(1), ch)
for i := range ch {
fmt.Println(i)
}
fmt.Println(Same(tree.New(1), tree.New(1)))
fmt.Println(Same(tree.New(1), tree.New(2)))
}
运动:网络爬虫
在这个练习中,我们将使用Go语言的并发特性来并行化网络爬虫(web crawler)。
问题链接:https://go-tour-jp.appspot.com/concurrency/10
尽管本文中未提及sync.WaitGroup,但基于以下原因,我们使用它。
Mutexを使うのでどうせsyncパッケージはインポートすることになる
channelを使って頑張るよりもシンプルで分かりやすく書ける
WaitGroupのドキュメントのExampleがこの問題と同じような設定
此外,由于这个过程会在一瞬间结束,因此很难确认是否可以同时执行。为了更加清楚明了,我们可以导入time包,并在Fetch方法的第一行加入time.Sleep(1 * time.Second),以便等待1秒钟。如果一切正确,应该在3秒钟内结束。
package main
import (
"fmt"
"sync"
)
type Fetcher interface {
// Fetch returns the body of URL and
// a slice of URLs found on that page.
Fetch(url string) (body string, urls []string, err error)
}
// Crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func Crawl(url string, depth int, fetcher Fetcher) {
cache := struct {
visited map[string]bool
sync.Mutex
}{
visited: make(map[string]bool),
}
var wg sync.WaitGroup
var crawl func(string, int)
crawl = func(url string, depth int) {
if depth <= 0 {
return
}
cache.Lock()
if cache.visited[url] {
cache.Unlock()
return
}
cache.visited[url] = true
cache.Unlock()
body, urls, err := fetcher.Fetch(url)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("found: %s %q\n", url, body)
wg.Add(len(urls))
for _, u := range urls {
go func(u string) {
crawl(u, depth-1)
wg.Done()
}(u)
}
}
crawl(url, depth)
wg.Wait()
}
func main() {
Crawl("https://golang.org/", 4, fetcher)
}
// fakeFetcher is Fetcher that returns canned results.
type fakeFetcher map[string]*fakeResult
type fakeResult struct {
body string
urls []string
}
func (f fakeFetcher) Fetch(url string) (string, []string, error) {
if res, ok := f[url]; ok {
return res.body, res.urls, nil
}
return "", nil, fmt.Errorf("not found: %s", url)
}
// fetcher is a populated fakeFetcher.
var fetcher = fakeFetcher{
"https://golang.org/": &fakeResult{
"The Go Programming Language",
[]string{
"https://golang.org/pkg/",
"https://golang.org/cmd/",
},
},
"https://golang.org/pkg/": &fakeResult{
"Packages",
[]string{
"https://golang.org/",
"https://golang.org/cmd/",
"https://golang.org/pkg/fmt/",
"https://golang.org/pkg/os/",
},
},
"https://golang.org/pkg/fmt/": &fakeResult{
"Package fmt",
[]string{
"https://golang.org/",
"https://golang.org/pkg/",
},
},
"https://golang.org/pkg/os/": &fakeResult{
"Package os",
[]string{
"https://golang.org/",
"https://golang.org/pkg/",
},
},
}