約1ヶ月かけてGoのチュートリアルA Tour of Goをひと通りやり終えた。実行環境はそのままサイト内のplaygroundのみを使った。
参考にしたもの
- WEB+DB PRESS vol.82 はじめてのGo
- WEB+DB PRESS vol.95 Goによる並行処理
以下、各課題で書いたコードとメモ。
2016/11/13 Start
記念すべきGo学習を始めた日。Packages
2016/11/23 課題:Forループ/ニュートン法
ニュートン法を扱う課題。なにこの数式、いきなりやばい。と思いながら、なんとか正しい出力結果を得たものの、forループの課題なのにforループ使わず再帰処理で無理やりグローバル変数とか使ってたりして、出題者の意図をくみ取れず。。。
package main
import (
"fmt"
"math"
)
var z = 1.0
var cnt = 0
func Sqrt(x float64) float64 {
zbuf := z
z = z - (z*z-x)/(2*z)
fmt.Println(cnt, z)
if math.Abs(zbuf-z) < 0.000001 {
return z
}
cnt++
return Sqrt(x)
}
func main() {
fmt.Println(Sqrt(3))
fmt.Println(math.Sqrt(3))
}
出力結果
0 2
1 1.75
2 1.7321428571428572
3 1.7320508100147276
4 1.7320508075688772
1.7320508075688772
1.7320508075688772
2016/11/26 課題:スライス
Exercise:Slices
これも、むずかしかったが、ごにょごにょしてたらできた。綺麗な画像が出てきたときは感動。
package main
import (
"golang.org/x/tour/pic"
)
func Pic(dx, dy int) [][]uint8 {
pic := make([][]uint8, dy)
for y := 0; y < dy; y++ {
pic[y] = make([]uint8, dx)
for x := 0; x < dx; x++ {
pic[y][x] = uint8((x+y)/2)
}
}
return pic
}
func main() {
pic.Show(Pic)
}
2016/11/26 課題:マップ
Exercise: Maps
マップの課題。APIリファレンスを見ながら書いた。なんとかできた。が、コードを見返すとなにをやっているか理解できない。
package main
import (
"strings"
"fmt"
"golang.org/x/tour/wc"
)
func WordCount(s string) map[string]int {
sf := strings.Fields(s)
fmt.Println(sf)
smap := make(map[string]int)
for _, elm := range sf {
fmt.Println(elm)
smap[elm] = smap[elm] + 1
}
return smap
}
func main() {
wc.Test(WordCount)
}
出力結果
[I am learning Go!]
I
am
learning
Go!
PASS
f("I am learning Go!") =
map[string]int{"I":1, "am":1, "learning":1, "Go!":1}
[The quick brown fox jumped over the lazy dog.]
The
quick
brown
fox
jumped
over
the
lazy
dog.
PASS
f("The quick brown fox jumped over the lazy dog.") =
map[string]int{"jumped":1, "the":1, "The":1, "quick":1, "brown":1, "fox":1, "over":1, "lazy":1, "dog.":1}
[I ate a donut. Then I ate another donut.]
I
ate
a
donut.
Then
I
ate
another
donut.
PASS
f("I ate a donut. Then I ate another donut.") =
map[string]int{"another":1, "I":2, "ate":2, "a":1, "donut.":2, "Then":1}
[A man a plan a canal panama.]
A
man
a
plan
a
canal
panama.
PASS
f("A man a plan a canal panama.") =
map[string]int{"man":1, "a":2, "plan":1, "canal":1, "panama.":1, "A":1}
2016/11/27 課題:クロージャ/フィボナッチ数列
Exercise: Fibonacci closure
これはすごく苦労した記憶があるが、なんかできた。今、コードを見返してみてもよくわからない(汗)
package main
import "fmt"
// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
var f []int
return func() int {
switch len(f) {
case 0:
f = append(f, 0)
case 1:
f = append(f, 1)
default:
f = append(f, (f[len(f)-2] + f[len(f)-1]))
}
return f[len(f)-1]
}
}
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
出力結果
0
1
1
2
3
5
8
13
21
34
2016/12/11 課題:エラー
Exercise: Errors
あまり覚えてない。一応、できてると思います。
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) {
var err ErrNegativeSqrt
if x < 0 {
err = ErrNegativeSqrt(x)
return 0, err
}
last_z, z := x, 1.0
for math.Abs(z-last_z) >= 1.0e-6 {
last_z, z = z, z-(z*z-x)/(2*z)
}
return z, nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
出力結果
1.4142135623730951 <nil>
0 cannot Sqrt negative number: -2
2016/12/11 課題:Reader
Exercise: Readers
ASCII文字 'A' の無限ストリームを出力する Reader 型を実装してください。というよくわからん課題。あまり覚えてないが、なんかできた。
package main
import "golang.org/x/tour/reader"
type MyReader struct{}
// TODO: Add a Read([]byte) (int, error) method to MyReader.
func (r MyReader) Read(b []byte) (int, error) {
for {
copy(b,"A")
return 1, nil
}
}
func main() {
reader.Validate(MyReader{})
}
出力結果
OK!
2016/12/11 課題:Readerをラップする/rot13Reader
これはギブアップ。できませんでした。
Exercise: rot13Reader
2016/12/14 課題:Imageインターフェイス
Exercise: Images
よく覚えてないが、APIリファレンスの見よう見まねで書いたらできた。
package main
import (
"golang.org/x/tour/pic"
"image"
"image/color"
)
type Image struct{}
// ColorModel returns the Image's color model.
func (img Image) ColorModel() color.Model {
return color.RGBAModel
}
// Bounds returns the domain for which At can return non-zero color.
// The bounds do not necessarily contain the point (0, 0).
func (img Image) Bounds() image.Rectangle {
return image.Rect(0, 0, 100, 100)
}
// At returns the color of the pixel at (x, y).
// At(Bounds().Min.X, Bounds().Min.Y) returns the upper-left pixel of the grid.
// At(Bounds().Max.X-1, Bounds().Max.Y-1) returns the lower-right one.
func (img Image) At(x, y int) color.Color {
return color.RGBA{0, 0, 255, 255}
}
func main() {
m := Image{}
pic.ShowImage(m)
}
2016/12/20 課題:並行処理/2分木
いよいよGoの本丸といえる並行処理についての課題。この課題、3日ぐらいかけて相当ねばったが、結局ギブアップ。かなり解まで近づいて、めちゃくちゃ悔しかったので別記事で詳細を書いている。
3日かけてA Tour of Goの練習問題に挑戦して結局解けなかった話
2016/12/23 最後の課題:並行処理/Webクローラ
簡単ではなかったが、雑誌の記事や、チュートリアルを参考にすればできた。
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) {
c := SafeCounter{v: make(map[string]int)}
ch := make(chan string)
go InnerCrawl(url, depth, fetcher, ch , &c)
fmt.Println("<-ch", <-ch)
return
}
func InnerCrawl(url string, depth int, fetcher Fetcher, ch chan string, c *SafeCounter) {
c.Inc(url)
cnt := c.Value(url)
if cnt > 1 {
fmt.Printf("duplicate cnt:%v url:%s\n",cnt, url)
ch <- url
return
}
fmt.Println("depth:",depth)
if depth <= 0 {
ch <- url
return
}
body, urls, err := fetcher.Fetch(url)
if err != nil {
fmt.Println(err)
ch <- url
return
}
fmt.Printf("found: %s %q\n", url, body)
fmt.Printf("len(urls):%v\n", len(urls))
ich := make(chan string)
for _, u := range urls {
go InnerCrawl(u, depth-1, fetcher, ich, c)
}
for i := 0; i < len(urls); i++ {
fmt.Println("<-ich", <-ich)
}
ch <- url
return
}
func main() {
Crawl("http://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{
"http://golang.org/": &fakeResult{
"The Go Programming Language",
[]string{
"http://golang.org/pkg/",
"http://golang.org/cmd/",
},
},
"http://golang.org/pkg/": &fakeResult{
"Packages",
[]string{
"http://golang.org/",
"http://golang.org/cmd/",
"http://golang.org/pkg/fmt/",
"http://golang.org/pkg/os/",
},
},
"http://golang.org/pkg/fmt/": &fakeResult{
"Package fmt",
[]string{
"http://golang.org/",
"http://golang.org/pkg/",
},
},
"http://golang.org/pkg/os/": &fakeResult{
"Package os",
[]string{
"http://golang.org/",
"http://golang.org/pkg/",
},
},
}
// SafeCounter is safe to use concurrently.
type SafeCounter struct {
v map[string]int
mux sync.Mutex
}
// Inc increments the counter for the given key.
func (c *SafeCounter) Inc(key string) {
c.mux.Lock()
// Lock so only one goroutine at a time can access the map c.v.
c.v[key]++
c.mux.Unlock()
}
// Value returns the current value of the counter for the given key.
func (c *SafeCounter) Value(key string) int {
c.mux.Lock()
// Lock so only one goroutine at a time can access the map c.v.
defer c.mux.Unlock()
return c.v[key]
}
出力結果
found: http://golang.org/ "The Go Programming Language"
not found: http://golang.org/cmd/
<-ich http://golang.org/cmd/
found: http://golang.org/pkg/ "Packages"
found: http://golang.org/pkg/os/ "Package os"
duplicate cnt:2 url:http://golang.org/pkg/
<-ich http://golang.org/pkg/
duplicate cnt:2 url:http://golang.org/
<-ich http://golang.org/
duplicate cnt:2 url:http://golang.org/cmd/
<-ich http://golang.org/cmd/
found: http://golang.org/pkg/fmt/ "Package fmt"
duplicate cnt:3 url:http://golang.org/pkg/
<-ich http://golang.org/pkg/
duplicate cnt:3 url:http://golang.org/
<-ich http://golang.org/
<-ich http://golang.org/pkg/os/
duplicate cnt:4 url:http://golang.org/
<-ich http://golang.org/
<-ich http://golang.org/pkg/fmt/
<-ich http://golang.org/pkg/
<-ch http://golang.org/
まとめ
これで終わり。変なコード書いていたら、ご指摘お願いします。
playgroundはコードが保存されて便利だが、この記事を書くにあたって過去のコードを見返してみると全然記憶がなかったりするので総括としては
コメントは残しておこう。
です。
以上、現場からでした。