ゼロから学ぶGo言語プログラミング(12) A Tour of Go 55~60

f:id:belbo:20140616112930j:plain

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 Languagecolor - 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)
}

型を作り、インターフェースに基いてメソッドを実装し、それを投げればあとはよしなに…、という感覚が少し進んだ気がする。

でもこのコード、メソッドの実装を全部値渡しでしてるけど、負荷を考えるとポインタ渡しにすべきなんじゃないだろうか。 単純にポインタレシーバに書き換えただけでは動かないのは、何かを見落としているか、勘違いをしているんだろうなあ。