A Tour of Goのざっくりまとめ⑤ <More types: Range, Maps, 関数編>
前回までの続きです。
mattsun-plapla.hatenablog.com
mattsun-plapla.hatenablog.com
mattsun-plapla.hatenablog.com
mattsun-plapla.hatenablog.com
Range
配列やスライスなどを単純にfor-loopする場合、ちょっと面倒。
Rangeを使えば簡単に反復処理ができる。
import "fmt" func main() { a := []int{1, 2, 3, 4, 5} for _, value := range a { fmt.Println(value) // 1 2 3 4 5 }
スライスをrangeで繰り返す場合、rangeは反復毎に2つの変数を返します。 1つ目の変数はインデックス( index )で、2つ目はインデックスの場所の要素のコピーです。
https://go-tour-jp.appspot.com/moretypes/16
インデックスを無視したい場合は上記の例のようにアンダーバーを書く。
インデックスだけほしい場合は返す変数を1つにする。(, valueを書かない)
Maps
マップはキーと値とを関連付けする。
ゼロ値はnilで、キーをもっていない。追加もできない。
Pythonの辞書型のイメージと近い気がする。
func main() { v := map[int]string{1: "Mike", 2: "Tony", 3: "Kary"} fmt.Println(v) // map[1:Mike 2:Tony 3:Kary] fmt.Println(v[2]) // Tony
上述の例だとint型のインデックス(キー)にstring型の値なのでスライスでもできるが、
mapsならインデックスにint型以外も指定できる。
mapsのいろんな操作
func main() { v := map[int]string{1: "Mike", 2: "Tony", 3: "Kary"} /* 要素の追加 */ v[4] = "Bob" fmt.Println(v) // map[1:Mike 2:Tony 3:Kary 4:Bob] /* 要素が存在するかチェック */ element, ok := v[2] fmt.Println(element, ok) // Tony true element2, ok2 := v[5] fmt.Println(element2, ok2) // false /* 要素の削除 */ delete(v, 1) fmt.Println(v) // map[2:Tony 3:Kary 4:Bob] }
空のmapを作る方法
空のmapはnilを返す。(ゼロ値=nil)
空のmapに要素を追加しようとすると、エラーが発生する。
スライスと同様の理由で、map自体はどんなデータも保持していなく参照しかしていない、またnilで何も存在していない状態だから、とのこと。
func main() { var m map[string]int fmt.Println(m == nil) // true m["add"] = 200 fmt.Println(m) // ERROR: panic: assignment to entry in nil map }
では、空のmapをどう作るかというと、make関数をつかう。
func main() { m := make(map[string]bool) m["ok"] = true fmt.Println(m) // map[ok:true] }
この場合、変数mはstring型のキーとbool型の値を保持する空のmapである。
また、スライスと同様に、map自体と比較できるのはnilである。
関数
func add(x, y int) int { return x + y } func add_two(x, y int) (int, int) { return x + y, x - y } func add_name(x, y int) (result int) { result = x * y // := を使うとエラー return // Nakid return } func main() { r := add(30, 30) fmt.Println(r) // 60 r2, r3 := add_two(30, 10) fmt.Println(r2, r3) // 40 20 r4 := add_name(50, 50) fmt.Println(r4) // 2500 }
関数内に関数をつくる
芸がない事例ですが、こんな感じ。
func main() { f := func(x int) { fmt.Println("Function is called", x) } f(100) // Function is called 100 /* 他の変数に一旦置かずに、関数を書き、続けて引数を入力こともできる*/ func(x int) { fmt.Println("Function is called", x) }(2000) // Function is called 2000 }
クロージャー
クロージャは、それ自身の外部から変数を参照する関数値です。 この関数は、参照された変数へアクセスして変えることができ、その意味では、その関数は変数へ"バインド"( bind )されています。
https://go-tour-jp.appspot.com/moretypes/25
簡単に言うと、関数を返す関数みたいな感じでしょうか。
/* int型を返す関数funcを返すincrementGenerator関数 */ func incrementGenerator() func() int { x := 0 return func() int { x++ return x } } func main() { increment := incrementGenerator() fmt.Println(increment()) // 1 fmt.Println(increment()) // 2 fmt.Println(increment()) // 3 }
上記の例だと、毎回incrementGeneratorを呼ぶごとに、それまでに読んだ回数分だけxが既に加算されている。
円の面積を求めるクロージャー
func circleCalc(pi float64) func(radius float64) float64 { return func(radius float64) float64 { return pi * radius * radius } } func main() { circle1 := circleCalc(3) fmt.Println(circle1(2)) // 12 circle2 := circleCalc(3.14) fmt.Println(circle2(2)) // 12.56 }
フィボナッチ数列の実装
最後にあるエクササイズの一例。
// fibonacci is a function that returns // a function that returns an int. func fibonacci() func() int { a := 0 b := 1 return func() int { a, b = b, a+b return a } } func main() { f := fibonacci() for i := 0; i < 10; i++ { fmt.Println(f()) } }