Go言語|Basics
Go言語の基礎メモ。
関数
func add(x int, y int) int { return x + y }
引数の型が同じ場合は、最後の引数の型だけ書いてもOK。
func add(x, y int) int { return x + y }
関数の中では、 var 宣言の代わりに :=
の代入文を使って暗黙的な型宣言ができる。
func main() { var i, j int = 1, 2 k := 3 c, python, java := true, false, "no!" fmt.Println(i, j, k, c, python, java) } // 1 2 3 true false no!
For文
func main() { sum := 0 for i := 0; i < 10; i++ { sum += i } fmt.Println(sum) } // 45
Defer
defer
に渡した関数の実行を、呼び出し元の関数の終わり(returnする)まで遅延させることができる。
func main() { defer fmt.Println("world") fmt.Println("hello") } // hello // world
ポインタ
&
オペレータは、そのオペランド(operand)へのポインタを引き出す。
*
オペレータは、ポインタの指す先の変数を示す。
i := 42 p = &i fmt.Println(*p) // ポインタpを通してiから値を読みだす -> 42 *p = 21 // ポインタpを通してiへ値を代入する fmt.Println(i) // 21
Goでは関数呼び出しの時に、引数や戻り値のコピーを行う。
このとき変数をポインタにしていた場合は、そのポインタが指すアドレス値をコピーするため、コピーコストを抑えられる。
構造体
構造体内で宣言された変数をフィールドと呼ぶ。 頭文字が大文字だとpublicに、小文字だとprivateになる。
// public type Vertex struct { X int Y int } // private type vertex struct { X int Y int }
配列
intの10個の配列。
配列の長さは型の一部分なので、配列のサイズを変えることはできない。
var a [10]int
スライス
スライスは配列への参照のようなもの。
スライスはどんなデータも格納しておらず、単に元の配列の部分列を指し示す。
primes := [6]int{2, 3, 5, 7, 11, 13} var s []int = primes[1:4] // primesの1から4を含むスライスを作成 fmt.Println(s) // [3 5 7]
配列の作成とそれを参照するスライスを同時に作成できる。
array := [3]bool{true, true, false} // これは配列 slice := []bool{true, true, false} // 上記と同じ配列を作成し、それを参照するスライスを作成 fmt.Println(array) fmt.Println(slice) // [true true false] // [true true false]
これは上記と同様の配列を作成し、それを参照するスライスを作成
Map
Key と Value。
m := make(map[string]int) m["Answer"] = 42 fmt.Println(m["Answer"]) // 42
メソッド
Goにはクラスの仕組みが無い。
構造体(Vertex)にメソッド(Abs)を定義できる。
データと処理を強く結びつけたいときに多用される。
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()) }
Absメソッドの引数 v
を、レシーバと呼ぶ。
レシーバにポインタを使うこともできる。
ポインタレシーバを使う理由は二つ。
- ポインタレシーバが指す先の変数を、メソッド内で変更するため。
- メソッドの呼び出し毎に変数のコピーを避けるため。レシーバが大きな構造体である場合に効率的。
type Vertex struct { X, Y float64 } func (v *Vertex) Scale(f float64) { v.X = v.X * f v.Y = v.Y * f } func (v *Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } func main() { v := &Vertex{3, 4} fmt.Printf(v.Abs()) v.Scale(5) fmt.Printf(v.Abs()) } // 5 // 25
Goroutine
ゴルーチン。Goのランタイムに管理される軽量なスレッド。
go f(x, y, z)
と書くと、新しいgoroutineが実行される。
func say(s string) { for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func main() { go say("world") // 別のゴルーチンで実行される say("hello") // go say の完了を待たずに実行される } // hello // world // hello // world // world // hello // hello // world // hello
チャネル
チャネルを使うと、複数のゴルーチン間で値の送受信ができる。
あるゴルーチンから別のゴルーチンへ値を渡したいときは、チャネルへその値を送信する。
ch := make(chan int) ch <- 100
あるゴルーチンから渡された値を受け取るときは、チャネルを利用して受信する。
n := <- ch