Skip to Content

入門Goプログラミング Lesson18(もっと大きなスライス)から学んだこと

もっと大きなスライス

append関数

append関数は可変個数引数関数なので、1回で複数の要素を追加できる

package main

import "fmt"

func main() {
	locations := []string{"Abashiri", "Tokoro", "Kitami"}
	locations = append(locations, "Bekkai", "Engaru")

	fmt.Println(locations)
}
[Abashiri Tokoro Kitami Bekkai Engaru]

長さと容量

なせスライスに要素を追加することが可能なのか
スライスを通して見える要素の数によって、そのスライスの長さが決まる
スライスの基底配列が、それより大きいければスライスの容量にまだ成長の余地がある

スライスの長さと容量を表示するdump関数作成し見てみる
location[1:2]によって作られるスライスは長さは1だが、5個の要素を格納できる要素を持っていることがわかる

package main

import "fmt"

func main() {
	locations := []string{"Abashiri", "Tokoro", "Kitami"}
	dump("locations", locations)
	locations = append(locations, "Bekkai", "Engaru")
	dump("locations", locations) // なんで容量が6になるのかはappend関数を研究でわかる

	dump("locations", locations[1:2])
}

func dump(label string, slice []string) {
	fmt.Printf("%v: 長さ %v, 容量 %v %v\n", label, len(slice), cap(slice), slice)
}
locations: 長さ 3, 容量 3 [Abashiri Tokoro Kitami]
locations: 長さ 5, 容量 6 [Abashiri Tokoro Kitami Bekkai Engaru]
locations: 長さ 1, 容量 5 [Tokoro]

append関数を研究

基底配列にBekkaiを追加する容量がない
appendはlovationsの2倍の容量を持つ新規に割り当てた配列にコピーする
locations2は長さが4だけど容量が6になっているのは、locations容量3*2によるもの

package main

import "fmt"

func main() {
	locations := []string{"Abashiri", "Tokoro", "Kitami"}
	dump("locations", locations)
	locations2 := append(locations, "Bekkai")
	dump("locations2", locations2)
	locations3 := append(locations2, "Engaru", "Tsubetsu", "Bihoro", "Rikubetsu")
	dump("locations3", locations3)
}

func dump(label string, slice []string) {
	fmt.Printf("%v: 長さ %v, 容量 %v %v\n", label, len(slice), cap(slice), slice)
}
locations: 長さ 3, 容量 3 [Abashiri Tokoro Kitami]
locations2: 長さ 4, 容量 6 [Abashiri Tokoro Kitami Bekkai]
locations3: 長さ 8, 容量 12 [Abashiri Tokoro Kitami Bekkai Engaru Tsubetsu Bihoro Rikubetsu]

インデックス3個のスライシング

作成されるスライスの容量を制限できる
また、要素を変更しても新たな配列が割り当てられるため、locations配列には影響がない 基底配列の要素を上書きしたくないのであれば、3個のインデックスを使ってスライスの容量を設定する方が安全

package main

import "fmt"

func main() {
	locations := []string{"Abashiri", "Tokoro", "Kitami", "Shari", "Koshimizu"}
	dump("locations", locations)

	location3 := locations[0:3:3]
	dump("location3", location3)

	location3plus1 := append(location3, "追加")
	dump("location3plus1", location3plus1)

	dump("locations", locations)
}

func dump(label string, slice []string) {
	fmt.Printf("%v: 長さ %v, 容量 %v %v\n", label, len(slice), cap(slice), slice)
}
locations: 長さ 5, 容量 5 [Abashiri Tokoro Kitami Shari Koshimizu]
location3: 長さ 3, 容量 3 [Abashiri Tokoro Kitami]
location3plus1: 長さ 4, 容量 6 [Abashiri Tokoro Kitami 追加]
location3: 長さ 3, 容量 3 [Abashiri Tokoro Kitami]
locations: 長さ 5, 容量 5 [Abashiri Tokoro Kitami Shari Koshimizu]

makeでスライスを事前に割り当てる

appendするのに容量が不足する場合、make関数で事前割り当てを行えば、
臨時に実行される割り当てとコピーを防ぐことができる

package main

import "fmt"

func main() {
	locations := make([]string, 0, 10)
	locations = append(locations, "Abashiri", "Tokoro", "Kitami")

	dump("locations", locations)
}

func dump(label string, slice []string) {
	fmt.Printf("%v: 長さ %v, 容量 %v %v\n", label, len(slice), cap(slice), slice)
}
locations: 長さ 3, 容量 10 [Abashiri Tokoro Kitami]

可変個引数関数の宣言

Printfappendは引数の個数が可変なので可変個引数関数
words ...stringが、0個以上の引数を含む、文字列のスライス

package main

import "fmt"

func main() {
	result := addNew("New", "Abashiri", "Tokoro", "Kitami")
	fmt.Println(result)
}

func addNew(prefix string, words ...string) []string {
	newWords := make([]string, len(words))

	for i := range words {
		newWords[i] = prefix + " " + words[i]
	}
	return newWords
}
[New Abashiri New Tokoro New Kitami]

複数個の引数の代りに1個のスライスを渡す場合
locations...がその書き方

package main

import "fmt"

func main() {
	locations := []string{"Abashiri", "Tokoro", "Kitami"} // スライスに変更
	result := addNew("New", locations...) // 引数の書き方変更
	fmt.Println(result)
}

func addNew(prefix string, words ...string) []string {
	newWords := make([]string, len(words))

	for i := range words {
		newWords[i] = prefix + " " + words[i]
	}
	return newWords
}
[New Abashiri New Tokoro New Kitami]

まとめ

  • スライスは長さと容量を持つ

感想

基底配列の上書きは注意したい
配列まはた、スライスをコピーする時は3個のインデックスを使った方が無難な印象