はじめに
今回はGoの基本文法である「参照型」についてです。
目次
参照型 Slices : スライス(可変長配列)
スライスは可変長配列を指します。
スライスの宣言方法は以下です。
配列(Arrays)と違い、スライスは [ ] の中に大きさを指定しません。
① var 変数名 []型
② var 変数名 []型 = []型{初期値1, ..., 初期値n}
③ 変数名 := 配列[start:end]
⇨ 配列(またはスライス)のstartから(end - 1)を取り出す事でスライスを生成
④ 変数名 := 配列[len(要素数):end] ⇨ startをlen(要素数)で指定
⑤ make で要素数を決めた後、一部の要素へデータを格納
package main
import "fmt"
func main() {
// ①
var s1 []string
fmt.Println(s1) // => []
// ②
s2 := []int{1, 2, 3, 4, 5}
fmt.Println(s2) // => "[1 2 3 4 5]"
// ③
s3 := s2[0:3]
fmt.Println(s3) // => "[1 2 3]"
// ④
s4 := s2[len(s2)-2 : 5]
fmt.Println(s4) // => "[4 5]"
// ⑤
s5 := make([]int, 5)
s5[0] = 3
fmt.Println(s5) // => "[3, 0, 0, 0, 0]"
}
[start:end]の指定パターンは以下のとおりです。
| パターン | 意味 |
|---|---|
| Slice[start:end] | start から end - 1 まで |
| Slice[start:] | start から最後尾まで |
| Slice[:end] | 先頭から end - 1 まで |
| Slice[:] | 先頭から最後尾まで |
ちなみに、スライスとスライス元となった配列の要素は共有されます。
なので、スライスの要素を変更するとスライス元の配列要素に対しても変更がかかリます。
func main() {
slice_target := [...]string{"Hello", "Go"}
slice := slice_target[0:2] // スライスの生成
slice[1] = "PHP" // slice[1]の要素を変更
fmt.Println(slice_target) // => [Hello PHP] // slice_target の要素も変更されている
}
スライスへの要素追加は組み込み関数append()を使用することで可能です。
new_slice = append(slice, 追加要素)
func main() {
slice_target := []string{"Hello", "Go"}
new_slice := append(slice_target, "PHP") // slice_target に "PHP" を追加
fmt.Println(new_slice) // => [Hello Go PHP]
fmt.Println(slice_target) // => [Hello Go] // 元の値は変わっていない。
}
ここで、スライスで使われるlength(長さ)とcapacity(容量)について解説です。
・length : length を指定したスライスの要素数
・capacity : capacity を指定したスライスの生成元の要素数
length と capacity はlen()とcap()という式を使います。
func main() {
slice_target := [...]string{"Hello", "Go"}
s1 := slice_target[0:1]
fmt.Println(s1) // => Hello
fmt.Println(len(s1)) // => 1 // s1 の要素数は1つなので1を返す。
fmt.Println(cap(s1)) // => 2 // s1 の生成元の要素数は2つなので2を返す。
}
また、スライスには完全スライス型というものがあります。
low、high、maxの3つのパラメータを使ってスライスを生成します。
slice[low:high:max]
// この時のlenとcapは各々以下で算出される
// len() : high - low
// cap() : max - low
func main() {
slice_targat := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
s1 := slice_targat[2:4:4] // == "[3, 4]"
fmt.Println(len(s1)) // == 2
fmt.Println(cap(s1)) // == 2
s2 := slice_targat[2:4:6] // == "[3, 4]"
fmt.Println(len(s2)) // == 2
fmt.Println(cap(s2)) // == 4
}
スライスは型が一致している場合、他のスライスへ代入することができます。
func main() {
slice_target := []string{"Hello", "Go"}
var s1 []string // slice_target と同型である s1 を作成
s1 = slice_target // slice_target を s1 へ代入
fmt.Println(s1) // => [Hello Go]
}
スライスのゼロ値はnilです。
nilスライスは、0 の length と capacity を持っており、元となる配列は持っていません。
func main() {
var slice_target []int
fmt.Println(slice_target, len(slice_target), cap(slice_target)) // => [] 0 0
// 条件式では nil と比較
if slice_target == nil {
fmt.Println("nil!") // => nil!
// slice_target の値が nil の場合に nil! を表示
}
}
スライスは組み込み関数のmake()を使って定義することもできます。
make([]型, len, cap)
func main() {
slice := make([]int, 3, 3) // スライス生成
slice[1] = 1 // => slice[1] へ1を代入
fmt.Println(slice) // => [0 0 0]
}
参照型 Maps : マップ(連想配列)
マップは連想配列を指します。要素と値がセットになっています。
マップの生成方法は以下の通りです。
var マップ名 map[要素の型]値の型
マップ名 := make(map[要素の型]値の型)
色々と書いてみました。特徴・記法については以下の通りです。
package main
import "fmt"
func main() {
// マップ生成 : ※出力値は五十音順にソートされる
m := map[string]int{"japan": 100, "usa": 200, "canada": 300}
fmt.Println(m) // => map[canada:300 japan:100 usa:200]
// 空のマップ生成、値を代入
m2 := make(map[string]int)
m2["apple"] = 1
fmt.Println(m2) // => map[apple:1]
// 値の参照
fmt.Println(m["japan"]) // => 100
// 値の追加・更新
m["italy"] = 500
fmt.Println(m) // => map[canada:300 italy:500 japan:100 usa:200]
m["japan"] = 150
fmt.Println(m) // => map[canada:300 italy:500 japan:150 usa:200]
// 値の存在チェック
v1, check := m["korean"]
fmt.Println(v1, check) // => 0 false
v2, check := m["japan"]
fmt.Println(v2, check) // => 100 true
// 値の消去
delete(m, "italy")
fmt.Println(m) // => map[canada:300 japan:150 usa:200]
// マップから要素のみを抽出
fmt.Println(extract_keys(m)) // => [japan usa canada]
// マップから値のみを抽出
fmt.Println(extract_values(m)) // => [150 200 300]
// マップを多次元配列へ変換
fmt.Println(convert_ma(m)) // => [[japan 150] [usa 200] [canada 300]]
// 特定要素の値のみを抽出
keys := []string{"usa", "canada"}
fmt.Println(extract_pinpoint_values(m, keys)) // => [200 300]
// マップ同士をマージ(結合)
merge1 := map[string]string{"key1": "val1", "key2": "val2"}
merge2 := map[string]string{"key3": "val3"}
fmt.Println(merge(merge1, merge2)) // => map[key1:val1 key2:val2 key3:val3]
}
// マップから要素のみを抽出
func extract_keys(m map[string]int) []string {
ks := []string{}
for k, _ := range m {
ks = append(ks, k)
}
return ks
}
// マップから値のみを抽出
func extract_values(m map[string]int) []int {
vs := []int{}
for _, v := range m {
vs = append(vs, v)
}
return vs
}
// マップを多次元配列へ変換
func convert_ma(m map[string]int) []interface{} {
a := []interface{}{}
for k, v := range m {
a = append(a, []interface{}{k, v})
}
return a
}
// 特定要素の値のみを抽出
func extract_pinpoint_values(m map[string]int, keys []string) []int {
vs := []int{}
for _, k := range keys {
vs = append(vs, m[k])
}
return vs
}
// マップ同士をマージ(結合)
func merge(m1, m2 map[string]string) map[string]string {
ans := map[string]string{}
for k, v := range m1 {
ans[k] = v
}
for k, v := range m2 {
ans[k] = v
}
return (ans)
}
参照型 Range : レンジ(反復処理)
for ループに利用する range は、スライスやマップを反復処理するために使います。
range の生成方法は以下の通りです。
for 要素, 値 := range スライスまたはマップ名 {}
for と range の扱い方は主に3種類です。
①要素と値を取り出す
②要素だけを取り出す(値を```_```で出力しないようにする)
③値だけを取り出す(要素を```_```で出力しないようにする)
以下はスライスを for と range で取り出しています。
package main
import "fmt"
func main() {
// ①
var country = []string{"japan", "usa", "canada", "italy"}
for index, value := range country {
fmt.Println(index, value) // => 0 japan 1 usa 2 canada 3 italy
}
// ②
var country = []string{"japan", "usa", "canada", "italy"}
for index, _ := range country {
fmt.Println(index) // => 0 1 2 3
}
// ③
var country = []string{"japan", "usa", "canada", "italy"}
for _, value := range country {
fmt.Println(value) // => japan usa canada italy
}
}
_は省略可能です。
for index := range country
マップは以下のように取り出します。
func main() {
m := map[string]int{"japan":100, "usa":200, "canada": 300}
for index, value := range m {
fmt.Println(index, value) // => japan 100 usa 200 canada 300
}
}
参考文献
A Tour of Go
Go言語(golang) スライスと配列の使い方
逆引きGolang (マップ)
Go 言語のマップ (map)
【Go入門】forとrangeを使った取り出し方について