ゼロから学ぶGo言語プログラミング(12) A Tour of Go 55~60
A Tour of Goの続き。 メソッドから。
55.Errors
なんとなく見てきたコードでも登場していましたが、Goではエラーの通知に複数の戻り値を活用しているようです。 こんな感じのコードでしょうか。
f, err := foo() if err != nil { fmt.Println(err) }
これは errorインターフェースが文字列を返すよう定義されているから、と説明してますが、ここだけでは「へー、そうなのか」という感じ。 次で、自分でエラーを実装。
56.Exercise: Errors
エラーを扱う課題。 が、複素数は概念自体が身についていない。 仕方ないのでもっと単純な文字列の判定を課題代わりにしよう。
性別を表す文字列"M"または"F"のみを受け、maleやfemaleという文字列を返す関数を実装してみる。
package main import ( "fmt" ) type ErrMonthNum uint func (err ErrMonthNum) Error() string { return fmt.Sprintf("Need 1...12 uint. You gave %d.", uint(err)) } func MonthName(m uint) (string, error) { if m < 1 || m > 12 { return "", ErrMonthNum(m) } ms := []string{ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", } return ms[m-1], nil } func main() { fmt.Println(MonthName(6)) fmt.Println(MonthName(21)) }
これで一応期待通りにエラーが出力されるけど、これで合っているのかどうか…。 エラーを返す際に、自分でエラー用の型を作って、それにErrorメソッドを実装する、という辺りまでは理解できた。 まあ、色んなコード見て勉強しよう。
57.Web servers
やっと身近な話題が。 と思ったら、これはさすがにPlaygroundでは動かせない。 取り敢えずで作っておいたGoの環境を使って、ローカルで動かすことに。
ガイド通りコピーしてローカルで go run すると、localhost:4000 できちんと"Hello!"。 やはり恐ろしく手軽。
コードの方はというと、Hello型のstructを定義し、そこに ServeHTTPメソッドを実装してる。 ServerHTTPメソッドは、net/httpパッケージを使ったResponseWriterと、ポインタ渡しのRequestが引数。 処理はFprintのみ。
サーバーの実行はhttp.ListenAndServe()で、引数に"localhost:4000"を与えてポートを指定してる。 ポート込みのホスト名を指定って面白いな。 そしてHello型の変数も渡す。
多分http.ListenAndServe()は、ServHTTPメソッドを実装した構造体を受け取って、指定したポートで待ち受けるもの。 go runすると、ローカルの4000番で待ち続ける。
58.Exercise: HTTP Handlers
HTTPな課題。 任意の型にServeHTTPメソッドを実装し、任意のパスをそれらで受け取る、というもの。
型にServeHTTPメソッドを実装するのは、一つ前の解説通り。 問題はパスの扱いだが、課題の初期コードにそのまま「// your http.Handle calls here」とコメントがあり、解説中のハンドラ例をそのまま転記で問題なく動く。
http.ListenAndServe()の第2引数は、特定の型に結びつけたくない場合nilを渡す、っていうのはちょっとわかりにくいかな。
package main import ( "fmt" "net/http" ) type String string func (s String) ServeHTTP( w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, s) } type Struct struct { Greeting string Punct string Who string } func (s Struct) ServeHTTP( w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, s) } func main() { http.Handle("/string", String("I'm a String!")) http.Handle("/struct", &Struct{"Hello", ":", "Gophers!"}) http.ListenAndServe("localhost:4000", nil) }
物凄く手軽。 でもこれ、Goの特徴っていうより、net/httpが手軽なんでないだろうか。 もちろんGo自体の言語設計がこういうパッケージを書きやすくはしてるんだろうけど、これだけの課題では、便利な組み込みパッケージがあるんだなー、程度。
59.Images
画像の取り扱い。
imageパッケージがGoでの画像の基本らしい。
そしてimageパッケージにはImageインターフェースが定義されていて、カラーモデルやカラー値の取得、矩形での画像サイズを満たす必要がある。 しかし通常は用意されているRGBAなどが使えるので、独自実装する必要はない。 また、image.NewRGBA(image.Rect(0, 0, 100, 100)) のような手軽な生成用関数も用意されている。
60.Exercise: Images
画像取り扱いの課題。
自分でImage型を実装し、必要なメソッドを用意する。 必要なメソッドは以下。
- 画像のカラーモデル:color.Modelを返すColorModel()
- 画像のサイズ:Rectangleを返すBounds()
- 任意の座標のカラー:color.Colorを返すAt(x, y int)
image - The Go Programming Languageやcolor - The Go Programming Languageを見つつ、課題のコメントや解説を参考に完成。
package main import ( "code.google.com/p/go-tour/pic" "image" "image/color" ) type Image struct{ W int H int color uint8 } func (m Image) ColorModel() color.Model { model := color.RGBAModel return model } func (m Image) Bounds() image.Rectangle { return image.Rect(0, 0, m.W, m.H) } func (m Image) At(x,y int) color.Color { return color.RGBA{uint8(x), uint8(y^x), uint8(x^y), uint8(255)} } func main() { m := Image{255,255,100} pic.ShowImage(m) }
型を作り、インターフェースに基いてメソッドを実装し、それを投げればあとはよしなに…、という感覚が少し進んだ気がする。
でもこのコード、メソッドの実装を全部値渡しでしてるけど、負荷を考えるとポインタ渡しにすべきなんじゃないだろうか。 単純にポインタレシーバに書き換えただけでは動かないのは、何かを見落としているか、勘違いをしているんだろうなあ。