2015/11/18: 社内勉強会用に解説を作成。動作環境はGo 1.5.1 (darwin/amd64)。
ニュートン法の問題。
package main
import (
"fmt"
)
func Sqrt(x float64) float64 {
}
func main() {
fmt.Println(Sqrt(2))
}
とりあえず10回ループするとこんな感じ。
package main
import (
"fmt"
)
func Sqrt(x float64) float64 {
z := 1.0
for i := 0; i < 10; i++ {
z = z - (z*z-x)/2*z
}
return z
}
func main() {
fmt.Println(Sqrt(2))
// 1.3351578377717581
}
適当にしきい値をもうけてやる方式だとこんな感じ
package main
import (
"fmt"
"math"
)
func Sqrt(x float64) float64 {
z := 1.0
delta := 0.001
for {
next := z - (z*z-x)/2*z
if math.Abs(next-z) < delta {
break
}
z = next
}
return z
}
func main() {
fmt.Println(Sqrt(2))
// 1.4137132974342435
fmt.Println(math.Sqrt(2))
// 1.4142135623730951
}
https://tour.golang.org/moretypes/15
画像バイナリを生成する問題
package main
import "golang.org/x/tour/pic"
func Pic(dx, dy int) [][]uint8 {
}
func main() {
pic.Show(Pic)
}
x軸y軸の各座標の濃さを指定するとブルースケールで表示される。(まあグレースケールのブルー版)
package main
import "golang.org/x/tour/pic"
func Pic(dx, dy int) [][]uint8 {
m := make([][]uint8, dy)
for i := 0; i < dy; i++ {
m[i] = make([]uint8, dx)
for j := 0; j < dx; j++ {
m[i][j] = uint8(i * j)
}
}
return m
}
func main() {
pic.Show(Pic)
}
ちなみに pic.Show()
実装はこうなってる tour/pic.go at master · golang/tour
https://tour.golang.org/moretypes/20
それぞれの単語をカウントできるようにしてね、という問題
package main
import (
"golang.org/x/tour/wc"
)
func WordCount(s string) map[string]int {
return map[string]int{"x": 1}
}
func main() {
wc.Test(WordCount)
}
このまま実行するとエラーがでる
FAIL
f("I am learning Go!") =
map[string]int{"x":1}
want:
map[string]int{"Go!":1, "I":1, "am":1, "learning":1}
strings.Fields
が便利だよ、って書いてあるのでみてみましょう。
-> % go doc strings.Fields
func Fields(s string) []string
Fields splits the string s around each instance of one or more consecutive
white space characters, as defined by unicode.IsSpace, returning an array of
substrings of s or an empty list if s contains only white space.
ソースからみてもいいですね。
// Fields splits the string s around each instance of one or more consecutive white space
// characters, as defined by unicode.IsSpace, returning an array of substrings of s or an
// empty list if s contains only white space.
func Fields(s string) []string {
return FieldsFunc(s, unicode.IsSpace)
}
こんな感じでよさそうですね。
package main
import (
"golang.org/x/tour/wc"
"strings"
)
func WordCount(s string) map[string]int {
m := make(map[string]int)
for _, f := range strings.Fields(s) {
m[f] += 1
}
return m
}
func main() {
wc.Test(WordCount)
}
ちゃんと通りました。
PASS
f("I am learning Go!") =
map[string]int{"I":1, "am":1, "learning":1, "Go!":1}
PASS
f("The quick brown fox jumped over the lazy dog.") =
map[string]int{"quick":1, "fox":1, "lazy":1, "dog.":1, "The":1, "brown":1, "jumped":1, "over":1, "the":1}
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}
PASS
f("A man a plan a canal panama.") =
map[string]int{"panama.":1, "A":1, "man":1, "a":2, "plan":1, "canal":1}
みんな大好きフィボナッチです。無名関数で返しましょう。
package main
import "fmt"
// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
}
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
ちなみに func() int
が fibonacci()
の戻りの型に指定されています。 int
を戻す手続きを返せば良いので、例えば以下のような形式を期待します。
func fibonacci() func() int {
return func() int { return 0 }
}
buildは正常に通ります。まあもちろんこれは結果として0を返してしまいます。では実装します。
package main
import "fmt"
// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
x := 0
y := 1
return func() int {
x, y = y, x+y
return x
}
}
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
ということでクロージャと局所変数をうまく使えばよいですね。
package main
import "fmt"
type IPAddr [4]byte
// TODO: Add a "String() string" method to IPAddr.
func main() {
addrs := map[string]IPAddr{
"loopback": {127, 0, 0, 1},
"googleDNS": {8, 8, 8, 8},
}
for n, a := range addrs {
fmt.Printf("%v: %v\n", n, a)
}
}
普通に実行すると下のようになります。
loopback: [127 0 0 1]
googleDNS: [8 8 8 8]
IPアドレスv4なんで、 127.0.0.1
と表示したいですね。
-> % go doc fmt.Stringer
type Stringer interface {
String() string
}
Stringer is implemented by any value that has a String method, which defines
the ``native'' format for that value. The String method is used to print
values passed as an operand to any format that accepts a string or to an
unformatted printer such as Print.
Stringer
というのは String() string
をもつ型に対するinterfaceになっています。つまり String() string
を IPAddr
について実装すれば、 fmt.Printf
などで String() string
をつかってくれるわけです。あとで内部実装もみてみましょう。
package main
import "fmt"
type IPAddr [4]byte
func (i IPAddr) String() string {
return fmt.Sprintf("%d.%d.%d.%d", i[0], i[1], i[2], i[3])
}
func main() {
addrs := map[string]IPAddr{
"loopback": {127, 0, 0, 1},
"googleDNS": {8, 8, 8, 8},
}
for n, a := range addrs {
fmt.Printf("%v: %v\n", n, a)
}
}
これで以下のように出力できます。 fmt.Sprintf
便利ですね。
loopback: 127.0.0.1
googleDNS: 8.8.8.8
package main
import (
"fmt"
)
func Sqrt(x float64) (float64, error) {
return 0, nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
平方根だすときに負数わたされたらerror返すようにしましょう。(複素数はサポートしない、ということにします。)
package main
import (
"fmt"
)
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
return fmt.Sprintf("cannot Sqrt negative number: %g", e)
}
func Sqrt(x float64) (float64, error) {
if x < 0 {
return 0, ErrNegativeSqrt(x)
}
return 0, nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
Sqrt本体の実装は割愛します。Error() string
を持っているtypeは error
型とみなされます。出力は以下のとおり。
0 <nil>
0 cannot Sqrt negative number: -2
package main
import "golang.org/x/tour/reader"
type MyReader struct{}
// TODO: Add a Read([]byte) (int, error) method to MyReader.
func main() {
reader.Validate(MyReader{})
}
io.Reader
をつかいます。 io.Reader
のインタフェースを実装すると、Goの全てのIOについてReadの機能を提供できます。
package main
import "golang.org/x/tour/reader"
type MyReader struct{}
func (r MyReader) Read(b []byte) (int, error) {
for i := 0; i < len(b); i++ {
b[i] = 'A'
}
return len(b), nil
}
func main() {
reader.Validate(MyReader{})
}
package main
import (
"io"
"os"
"strings"
)
type rot13Reader struct {
r io.Reader
}
func main() {
s := strings.NewReader("Lbh penpxrq gur pbqr!")
r := rot13Reader{s}
io.Copy(os.Stdout, &r)
}
この問題はさっきのio.Readerのところの応用です。渡ってきた文字列をいい感じに変換して、rot13になってるものを普通に読めるようにしよう、ということですね。
rot13というのは a->n, b->o, c->p のような規則で文字を変換するという簡単な暗号です。
package main
import (
"io"
"os"
"strings"
)
type rot13Reader struct {
r io.Reader
}
func (rot *rot13Reader) Read(b []byte) (int, error) {
n, err := rot.r.Read(b)
for i := 0; i < len(b); i++ {
if ('A' <= b[i] && b[i] <= 'M') || ('a' <= b[i] && b[i] <= 'm') {
b[i] = b[i] + 13
} else if ('N' <= b[i] && b[i] <= 'Z') || ('n' <= b[i] && b[i] <= 'z') {
b[i] = 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!
、となります。ちなみに '
でくくると rune
になります。runeというのはstringではなく、文字のことをしめします。つまり、 string
は文字列であり、 rune
は文字ですから、内部的には文字の配列がstring、となります。
Strings, bytes, runes and characters in Go - The Go Blog
ちなみにこのエクササイズでrot13Readerを書きましたが、rot13Writerも同じ要領でかけますね。
https://tour.golang.org/methods/14
package main
import (
"log"
"net/http"
)
func main() {
// your http.Handle calls here
log.Fatal(http.ListenAndServe("localhost:4000", nil))
}
struct
つくって ServeHTTP
を実装しようという問題です。これは手元の環境で走らせるとよいでしょう。
package main
import (
"fmt"
"log"
"net/http"
)
type String string
type Struct struct {
Greeting string
Punct string
Who string
}
func (s String) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, s)
}
func (s Struct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%s%s%s", s.Greeting, s.Punct, s.Who)
}
func main() {
http.Handle("/string", String("I'm a frayed knot."))
http.Handle("/struct", &Struct{"Hello", ":", "Gophers!"})
// your http.Handle calls here
log.Fatal(http.ListenAndServe("localhost:4000", nil))
}
以下のようにテストできます。
-> % curl http://localhost:4000/string
I'm a frayed knot.
-> % curl http://localhost:4000/struct
Hello:Gophers!
https://tour.golang.org/methods/16
package main
import "golang.org/x/tour/pic"
type Image struct{}
func main() {
m := Image{}
pic.ShowImage(m)
}
これは image.Image
のインタフェースにしたがって自分で Image
を実装しましょう、という問題ですね。
ちなみに image
パッケージでは image.RGBA
なんかがこの image.Image
を実装しています。
-> % go doc image RGBA
type RGBA struct {
// Pix holds the image's pixels, in R, G, B, A order. The pixel at
// (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*4].
Pix []uint8
// Stride is the Pix stride (in bytes) between vertically adjacent pixels.
Stride int
// Rect is the image's bounds.
Rect Rectangle
}
RGBA is an in-memory image whose At method returns color.RGBA values.
func NewRGBA(r Rectangle) *RGBA
func (p *RGBA) At(x, y int) color.Color
func (p *RGBA) Bounds() Rectangle
func (p *RGBA) ColorModel() color.Model
func (p *RGBA) Opaque() bool
func (p *RGBA) PixOffset(x, y int) int
func (p *RGBA) RGBAAt(x, y int) color.RGBA
func (p *RGBA) Set(x, y int, c color.Color)
func (p *RGBA) SetRGBA(x, y int, c color.RGBA)
func (p *RGBA) SubImage(r Rectangle) Image
ということで問題の指定通りstructをつくっていきましょう。
package main
import (
"golang.org/x/tour/pic"
"image"
"image/color"
)
type Image struct {
width int
height int
}
func (i Image) ColorModel() color.Model {
return color.RGBAModel
}
func (i Image) Bounds() image.Rectangle {
return image.Rect(0, 0, i.width, i.height)
}
func (i Image) At(x, y int) color.Color {
return color.RGBA{255 - uint8(x), 255 - uint8(y), 255, 255}
}
func main() {
m := Image{100, 100}
pic.ShowImage(m)
}
出力される画像のグラデーションパターンは At()
の実装によって変わることになります。
https://tour.golang.org/concurrency/8
package main
import "code.google.com/p/go-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)
// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool
func main() {
}
ここからconcurrencyの課題です。並行(parallel)ではなく並列です。
Binary Treeは左側のノードのほうが小さい値、右側のノードの方が大きい値をとるようになっています。なので、ツリーをたどっていくときには左側を優先してたどると小さい順に値をとることができる、ということになります。
package main
import (
"code.google.com/p/go-tour/tree"
"fmt"
)
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
walkIter(t, ch)
close(ch)
}
func walkIter(t *tree.Tree, ch chan int) {
if t != nil {
walkIter(t.Left, ch)
ch <- t.Value
walkIter(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 i := range ch1 {
if i != <-ch2 {
return false
}
}
return true
}
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))) // -> true
fmt.Println(Same(tree.New(1), tree.New(2))) // -> false
}
package main
import (
"fmt"
)
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) {
// TODO: Fetch URLs in parallel.
// TODO: Don't fetch the same URL twice.
// This implementation doesn't do either:
if depth <= 0 {
return
}
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 {
Crawl(u, depth-1, fetcher)
}
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/",
},
},
}
これまでのexerciseのまとめのような問題。
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)
}
var crawled map[string]bool
func childCrawl(url string, depth int, fetcher Fetcher, result chan string, wg *sync.WaitGroup) {
defer wg.Done()
if depth <= 0 {
return
}
if e := crawled[url]; e {
return
}
body, urls, err := fetcher.Fetch(url)
if err != nil {
result <- err.Error()
return
}
crawled[url] = true
result <- fmt.Sprintf("found: %s %q", url, body)
for _, u := range urls {
wg.Add(1)
go childCrawl(u, depth-1, fetcher, result, wg)
}
}
// Crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func Crawl(url string, depth int, fetcher Fetcher) {
result := make(chan string)
var wg sync.WaitGroup
wg.Add(1)
go childCrawl(url, depth, fetcher, result, &wg)
go func() {
wg.Wait()
close(result)
}()
for r := range result {
fmt.Println(r)
}
}
func main() {
crawled = make(map[string]bool)
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/",
},
},
}
- tour.golang exercise solutions
- 色んな人の回答