ゆるりのこと。

文系営業マンから新米データサイエンティストしています。

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

参考

medium.com

/* */