A Tour of Goを完走した
1周目はよくわからなかったけど、よく読んで2周目やってみたら意外と解けました。
ということで、一通りやってみたコードのまとめ。
もっとスマートにできたりすると思う。
Loops and Functions
関数とループを使った簡単な練習として、平方根の計算を実装してみましょう: 数値 x が与えられたときに z² が最も x に近い数値 z を求めたいと思います。
func Sqrt(x float64) float64 { z := 1.0 for i := 0; i < 10; i++ { z -= (z*z - z) / (2 * z) } return z }
次に値が変化しなくなった (もしくはごくわずかな変化しかしなくなった) 場合にループを停止させます。 それが 10 回よりも多いか少ないかを確認してください。
この文章が本当に理解できず、悩みました。
「ごくわずかな変化ってなんやねん」って感じで・・・。
下の方にニュートン法のことが触れられていて、そっちを読むとなんとなく理解し始めました。
func Sqrt(x float64) float64 { z := 1.0 var prev float64 for i := 0; i < 10; i++ { z -= (z*z - z) / (2 * z) if math.Abs(prev-z) < 1e-10 { fmt.Printf("%d回目\n" , i+1) break } prev = z } return z }
どんなもんなのかを課題解いたよ!って記事のコードいくつか読んでみて噛み砕いてからやりました。
ただ、1e-10
のところが実装者によって様々で、1e-10
、1e-6
、0.0001
とかいろいろあって、理解していないときは「なんで?」ってなりました。
ここが誤差の許容範囲みたいな感じで理解してます。
Slices
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[x][y] = uint8(x*y) } } return pic }
uint8(xxx)のところは、
生成する画像は、好きに選んでください。例えば、面白い関数に、 (x+y)/2 、 x*y 、 xy などがあります。
の部分なので、好きな計算式入れてあげると良いです。
画像変わって面白いですよ。なんならここに示されている計算式以外でも変な画像できます。
Maps
WordCount 関数を実装してみましょう。string s で渡される文章の、各単語の出現回数のmapを返す必要があります。 wc.Test 関数は、引数に渡した関数に対しテストスイートを実行し、成功か失敗かを結果に表示します。
一番分かりやすかった気がするのは、普段JSで大体なんでもオブジェクトでKey:Valueにしてなんかしちゃうのが原因?
func WordCount(s string) map[string]int { wordList := strings.Fields(s) m := make(map[string]int) for _, word := range wordList { if _, exist := m[word]; exist { m[word]++ } else { m[word] = 1 } } return m }
Fibonacci closure
fibonacci (フィボナッチ)関数を実装しましょう。この関数は、連続するフィボナッチ数(0, 1, 1, 2, 3, 5, ...)を返す関数(クロージャ)を返します。
クロージャの問題。
フィボナッチ数列自体は分かってたのですが、最初の0,1は固定なんですね・・・。
func fibonacci() func() int { fib := 0 result := [2]int{0,0} return func() int { if fib == 1 { result[1] = 1 } else { w := result[1] result[1] = result[0] + result[1] result[0] = w } fib++ return result[1] } }
さすがにもっとスマートに書けるんじゃないかなこれ。
Stringers
IPAddr 型を実装してみましょう IPアドレスをドットで4つに区切った( dotted quad )表現で出力するため、 fmt.Stringer インタフェースを実装してください。 例えば、 IPAddr{1, 2, 3, 4} は、 "1.2.3.4" として出力するようにします。
func (ip IPAddr) String() string { return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]) }
Errors
Sqrt 関数を 以前の演習 からコピーし、 error の値を返すように修正してみてください。 Sqrt は、複素数をサポートしていないので、負の値が与えられたとき、nil以外のエラー値を返す必要があります。
type ErrNegativeSqrt float64 func (x ErrNegativeSqrt) Error() string { return fmt.Sprintf("cannot Sqrt negative number %f", float64(x)) } func Sqrt(x float64) (float64, error) { if x < 0 { return 0, ErrNegativeSqrt(x) } z := 1.0 var prev float64 for i := 0; i < 10; i++ { z -= (z*z - z) / (2 * z) if math.Abs(prev-z) < 1e-10 { fmt.Printf("%d回目\n" , i+1) break } prev = z } return z, nil }
Readers
ASCII文字 'A' の無限ストリームを出力する Reader 型を実装してください。
無限ストリームがイマイチピンとこなくて調べました。 ほぼそのままの意味でした()
b[i] = "A"
としていてtype errorになって、ここではじめて'A'
であることに気付いた。
学び。
func (r MyReader) Read(b []byte) (int, error) { for i := range b { b[i] = 'A' } return len(b), nil }
rot13Reader
io.Reader を実装し、 io.Reader でROT13 換字式暗号( substitution cipher )をすべてのアルファベットの文字に適用して読み出すように rot13Reader を実装してみてください。
type rot13Reader struct { r io.Reader } func (r rot13Reader) Read(b []byte) (n int, err error) { n, err = r.r.Read(b) if err != nil { return } for i, c := range b[:n] { if 'A' <= c && c <= 'Z' { b[i] = (c-'A'+13)%26 + 'A' } else if 'a' <= c && c <= 'z' { b[i] = (c-'a'+13)%26 + 'a' } } return }
問題文中にあるgzip.NewReader
へのリンクがあったので、コード読んでると、r.r.Read
のヒントが得られました。
読んだら書けました。
Image
前に解いた、画像ジェネレーターを覚えていますか? 今回は、データのスライスの代わりに image.Image インタフェースの実装を返すようにしてみましょう。 自分の Image 型を定義し、 インタフェースを満たすのに必要なメソッド を実装し、 pic.ShowImage を呼び出してみてください。
ドキュメント読んだらそれっぽくできました。
とりあえず、w, hだけ持たせて、呼び出しで大きさ決めれるようにはしておきました。
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 int, y int) color.Color { return color.RGBA{0, 0, uint8(x ^ y), uint8(x ^ y)} } func main() { m := Image{180, 180} pic.ShowImage(m) }
Exercise: Equivalent Binary Trees
https://go-tour-jp.appspot.com/concurrency/7
問題文長いので↑で確認してください。。。
Tree.Left, Tree.Rightが*Treeであることに気付けたら、あとは早かったです。
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) { recursiveWalk(t, ch) close(ch) } func recursiveWalk(t *tree.Tree, ch chan int) { if t.Left != nil { recursiveWalk(t.Left, ch) } ch <- t.Value if t.Right != nil { recursiveWalk(t.Right, ch) } } // Same determines whether the trees // t1 and t2 contain the same values. func Same(t1, t2 *tree.Tree) bool { ch1 := make(chan int) ch2 := make(chan int) go Walk(t1, ch1) go Walk(t2, ch2) for { c1, ok1 := <-ch1 c2, ok2 := <-ch2 switch { case !ok1, !ok2: return ok1 == ok2 case c1 != c2: return false } } } func main() { ch := make(chan int) go Walk(tree.New(1), ch) for c := range ch { fmt.Println(c) } fmt.Println(Same(tree.New(1), tree.New(1))) fmt.Println(Same(tree.New(1), tree.New(2))) }
Exercise: Web Crawler
package main import ( "fmt" "sync" "time" ) 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) { cacheUrl := make(map[string]int) var mux sync.Mutex var crawl func(string, int) crawl = func(url string, depth int) { if depth <= 0 { return } if _, ok := cacheUrl[url]; ok { return } mux.Lock() cacheUrl[url]++ mux.Unlock() body, urls, err := fetcher.Fetch(url) if err != nil { fmt.Println(err) return } fmt.Printf("found: %s %q\n", url, body) for _, u := range urls { go crawl(u, depth-1) } } go crawl(url, depth) time.Sleep(time.Second) fmt.Println(cacheUrl) return } 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/", }, }, }
合ってるかわからん!
それっぽいはず・・・。
一通り終えて
コードこれで本当に合っているのかはわからないけど、1回目に書いたものということで残しておきます。
これで、みんなのGo言語を読み進めるスタート地点に立てた気がします。。。 本にもあったけど、また慣れてからやれば、もっと違う感じになると思うので、それはまた追々。
とりあえず、終わったよ報告でした。