変数の省略定義
// 型は自動で推測される
name := "John"
// 以下と同じ
var name string
name = "John"
名前付き戻り値
戻り値を変数として定義しておくことで、return のみで返却できる
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
func main() {
a, b := swap(10, 7)
fmt.Println(a, b)
}
Defer
defer へ渡した関数の実行を、呼び出し元の関数の終わり(returnする)まで遅延させる。
defer へ渡した関数が複数ある場合、その呼び出しはスタック( stack )されます。 呼び出し元の関数がreturnするとき、 defer へ渡した関数は LIFO(last-in-first-out) の順番で実行されます。
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
// 結果
// hello
// world
func main() {
fmt.Println("counting")
for i := 0; i < 10; i++ {
defer fmt.Println(i)
}
fmt.Println("done")
}
// 結果
// counting
// done
// 9```
// 8
// .
// .
// 0
スライス
スライスは配列への参照のようなものです。
スライスはどんなデータも格納しておらず、単に元の配列の部分列を指し示しています。
スライスの要素を変更すると、その元となる配列の対応する要素が変更されます。
同じ元となる配列を共有している他のスライスは、それらの変更が反映されます。
func main() {
names := [4]string{
"John",
"Paul",
"George",
"Ringo",
}
fmt.Println(names)
a := names[0:2]
b := names[1:3]
fmt.Println(a, b)
b[0] = "XXX"
fmt.Println(a, b)
fmt.Println(names)
}
// 結果
// [John Paul George Ringo]
// [John Paul] [Paul George]
// [John XXX] [XXX George]
// [John XXX George Ringo]
注意点
配列 (スライス) を部分的に参照する際、終了インデックスに指定する値は [実際のインデックス+1]
s = []int{10, 20, 30, 40, 50, 60}
// start_index >= 2 ~ end_index < 4 までの要素
s1 := s[1:4] //=> [20, ]
Make関数
// Make関数
// 第1引数:スライスの型
// 第2引数:スライスの長さ
// 第3引数:スライスの容量
// 長さ5 要素int型のスライス作成
a := make([]int, 5)
==> [0, 0, 0, 0, 0] //結果 要素は全てゼロ値(int型の場合は0)で初期化される
// 長さ0 容量5 要素int型のスライス作成
// 将来的に最大で5つの要素を格納できる
b := make([]int, 0, 5)
==> [] //長さ0なので要素なし
ループ
スライスをrangeで繰り返す場合、rangeは反復毎に2つの変数を返します。 1つ目の変数はインデックス( index )で、2つ目はインデックスの場所の要素のコピーです。
func main() {
for i, v := range pow {
fmt.Printf("2**%d = %d\n", i, v)
}
}
Map
// key:int value:文字列 とするマップを作成
e := make(map[int]string)
e[1] = "1st" // 追加
e[2] = "2nd"
e[3] = "3rd"
fmt.Println(e)
delete(e, 2) // 削除
fmt.Println(e)
// 存在確認
elem, ok := e[1]
fmt.Println("The value:", elem, "Present?", ok)
// 結果
// map[1:1st 2:2nd 3:3rd]
// map[1:1st 3:3rd]
// The value: Present? false
クロージャ
クロージャは、外部のスコープにある変数を参照し、その変数を保持して利用する関数です。Golangでは、無名関数
(関数リテラル)を使ってクロージャを作成します。無名関数は func() ... という形式で表現され、その関数型の戻り値として返すことでクロージャとして機能します。
package main
import "fmt"
// クロージャを返す関数
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
// count変数の値を取得する関数
func getCount() int {
return counter()()
}
func main() {
increment := counter()
fmt.Println(increment()) // クロージャを呼び出して countの値を更新。1が出力される
fmt.Println(increment()) // クロージャを呼び出して、countの値を更新。2が出力される
fmt.Println(getCount()) // countの現在の値を取得する(クロージャの再生成はしない)2が出力される
}
このコードでは、counter()関数がクロージャを返しています。counter()関数内部でcount変数が定義されており、この変数はクロージャに含まれています。クロージャはcount変数にアクセスして、その値を更新しています。
increment変数はcounter()関数を呼び出してその結果として返されるクロージャを保持しています。このクロージャを呼び出すたびに、count変数が更新され、新しい値が返されます。