こんにちは、ナナオです。

前回の続きでチュートリアルをやっていこうと思います。

map

package main

import "fmt"

func main() {
	// このmapはnilのため、make関数で初期化する必要がある
	var m map[string]int
	// make関数でmapを作成する
	m = make(map[string]int)
	// このマップは初期化済み
	m = map[string]int{
		"A": 1,
		"B": 2,
	}
	
	
	// キーの追加
	m["Answer"] = 42
	fmt.Println("The value:", m["Answer"])

	// キーの変更
	m["Answer"] = 48
	fmt.Println("The value:", m["Answer"])

	// キーの削除
	delete(m, "Answer")
	fmt.Println("The value:", m["Answer"])

	// キーの取得と存在確認
	v, ok := m["Answer"]
	fmt.Println("The value:", v, "Present?", ok)
}

関数

関数も変数として定義することができますし、引数にもできます。

package main

import (
	"fmt"
	"math"
)

func compute(fn func(float64, float64) float64) float64 {
	return fn(3, 4)
}

func main() {
	hypot := func(x, y float64) float64 {
		return math.Sqrt(x*x + y*y)
	}
	fmt.Println(hypot(5, 12))

	fmt.Println(compute(hypot))
	fmt.Println(compute(math.Pow))
}

メソッド

メソッドはレシーバ引数を実装することで可能です。

package main

import (
	"fmt"
	"math"
)

type Vertex struct {
	X, Y float64
}

func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
	v := Vertex{3, 4}
	fmt.Println(v.Abs())
}

インターフェース

インターフェースは以下のように実装します。

インターフェースのレシーバ引数のポインタはメソッドと違って厳密に判定されます。

例えば以下の例ではI型のインターフェースを実装したMメソッドのレシーバ引数を*Tとするとエラーになります。

その場合エラーを解消するにはレシーバ引数をTに戻すか、もしくは呼び出し方を&T{"hello"}に変更すればエラーは解消します。

package main

import "fmt"

type I interface {
	M()
}

type T struct {
	S string
}

// This method means type T implements the interface I,
// but we don't need to explicitly declare that it does so.
func (t T) M() {
	fmt.Println(t.S)
}

func main() {
	var i I = T{"hello"}
	i.M()
}

また、インターフェースに定義したメソッドのレシーバ引数はnilである場合があります。(これは結構引っ掛かりそうな仕様。。)

package main

import "fmt"

type I interface {
	M()
}

type T struct {
	S string
}

func (t *T) M() {
	if t == nil {
		fmt.Println("<nil>")
		return
	}
	fmt.Println(t.S)
}

func main() {
	var i I

	var t *T
	i = t
	describe(i)
	// 出力は<nil>
	i.M()
}

更にgoには空のインターフェースというのが存在します。

(これはPythonのAny型とかTypeScriptのany型みたいなこと…?)

package main

import "fmt"

func main() {
	var i interface{}
	describe(i)

	i = 42
	describe(i)

	i = "hello"
	describe(i)
}

func describe(i interface{}) {
	fmt.Printf("(%v, %T)\n", i, i)
}

空インターフェースを利用して型switchなどの仕組みを実現できます。

package main

import "fmt"

func do(i interface{}) {
	switch v := i.(type) {
	case int:
		fmt.Printf("Twice %v is %v\n", v, v*2)
	case string:
		fmt.Printf("%q is %v bytes long\n", v, len(v))
	default:
		fmt.Printf("I don't know about type %T!\n", v)
	}
}

func main() {
	do(21)
	do("hello")
	do(true)
}

まとめ

今日はここまでにします。

go、奥深い。