ゼロから学ぶGo言語プログラミング(9) A Tour of Go 34~39

f:id:belbo:20140616112930j:plain

A Tour of Goの続き。 rangeとmap。

34.Range

これもpythonっぽいrange機能。 mapはまだツアーに登場してないけど、辞書みたいなものかな。

for i, v := range foo {
}

これでfooが持つインデックスがiに、値がvに渡される。

ただ、"range フォーム"という表現が引っかかる。 翻訳の揺れなどではなく、元の英文でも"range form"となってる。

35.Range continued

インデックスや値を " _ "(アンダーバー) へ代入することで破棄することができます。

破棄することで、受け取って使わないよりも不要なリソースを確保せず済むんでしょうか。

for _, _ := range foo {
}

こうすると、fooの長さ分処理ができると思ったら、":="での型推論がひっかかった。 コロンを外せば動いたので、アンダースコアも減らす。

for _ = range foo {
}

さてこれが、len()でカウント取ってのループとどっちが効率良いのか。 range使った方が好みだけど、"_"を省略できないのはちょっと残念。

36.Exercise: Slices

バイト列を使った画像の生成の課題。 色々わかりにくい。

まず、"code.google.com/p/go-tour/pic"からのimport。 恐らく受け取ったバイト列を画像として出力するためのパッケージ。 pic.show()がそれに当たるらしい。

pic.show()はPic関数を引数として受け取るようだけど、課題の開始時点でPicには引数がない。 と思ったら、pic.show()にはPicを関数として渡していた。 つまり、pic.show()側で渡されたPicに、生成するサイズとなるdxやdyが渡されるということらしい。

次に分からないのが、"uint8"というPicの戻り値の指定。 まだまだ目がなれないので一見でわからないけれど、sliceはの後に、そのsliceが保持する中身の指定を行うようなので、Picの戻り値はuint8が連なったslice、ということになるようだ。 と思ったのだけど、それだとsliceを用意するmakeの書き方がわからない。 結局試行錯誤して、コードは次のようになった。

package main

import "code.google.com/p/go-tour/pic"

func Pic(dx, dy int) [][]uint8 {
    ret := make([][]uint8, dx, dy)
    for i := 0; i < dy; i++ {
        row := make([]uint8, dx)
        for k := 0; k < dx; k++ {
            row[k] = uint8(i ^ k)
        }
        ret[i] = row
    }
    return ret
}

func main() {
    pic.Show(Pic)
}

"uint8"のように、""を連ねると、多次元のsliceを示すようで、make()での長さ指定は引数を増やして対応するらしい。 多次元配列を"[]uint8"という風に初期化しないといけないのは、動的に次元の増減は難しいということだろうか。

ついでに、呼び出しているpic.Show()のコードも見てみました。

やはり、int2つを引数に取る関数がShow()の引数でしたが、引数として関数を与える場合、

func Show(f func(int, int) [][]uint8) {
}

というように、引数の場所に関数名、引数となる関数の引数、そして引数となる関数の戻り値まで記述するようです。 こういう風に関数を引数に取れるのは、Goがモダンなところなんでしょうか。

Sho()の結果は、imageパッケージを使ってRGBA画像としておき、それを更にShowImage()でbase64として返しています。 最終的な出力時に"IMAGE:"という文字列を先頭に付加していますが、これはplaygroundが画像としてbase64のデータを出力するためのフラグのようです。

sliceによる多次元配列の扱いは、まだ全然慣れませんが、使いこなせると強力そうです。

37.Maps

mapはやはり辞書のようです。

mapの初期化はsliceと同じmake()で、キーへのアクセスは"m["キー"]"です。 mapもsliceと同じく、保持する値の型を、make時に指定する必要があるようです。

38.Map literals

リテラルとしてのmapは"キー: 値"というペアでダイレクトに指定でき、make()で準備する必要がないようです。 stringのmapであれば、こんな風に定義できました。

var m2 = map[string]string{
    "a": "b",
    "c": "d",
    "e": "f",
}

39.Map literals continued

これ、38で試したコードではすでにstringを個々のmap要素に書いてませんが、Vertexの場合と同じように考えていいんでしょうか。

"トップレベル"とは、Vertexがmainパッケージの直下に指定されている事と同義と捉えていいのかどうか…。