#Java/C#風に書いたら駄目でした
型付きだから、もうPerlの世界じゃないと思い、Java/C#風に書いたら駄目でした
func arr1(){
a1 := [2]int{1, 2}
a2 := [3]int{1, 2, 3}
a1 = a2 //ここは焦点
fmt.Println(a1, a2)
}
// mainからの呼び出し部分は省略。下同。
$ go run array_slice.go
./array_slice.go:9:5: cannot use a2 (type [3]int) as type [2]int in assignment
型違いと言われているので、下記とおり修正したら、うまくいきました:
func arr2(){
a1 := [3]int{1, 2} //a1の長さも3に変更
a2 := [3]int{1, 2, 3}
a1 = a2 //ここは焦点
fmt.Println(a1, a2)
}
// output: [1 2 3] [1 2 3]
どっかで見かけたようでそこまで気にしなかったこと: 配列の長さも、その型定義の一部なので、要素型の同じ配列でも 長さが違うと別型扱いとなります。
これは、コンパイルすれば教えてくれるものなので、まだまだそこまで鬱陶しいものではないかもしれません。問題なのは後述のこっそりさられる系⬇
v := [長さn]Type{要素数m}
の新発見
上記コンパイルエラー対応するため、慌ててa1
の長さを2から3に変更することで通させたんですが、ソースコードa1 := [3]int{1, 2}
をよく見てみると、長さの指定3
と中身の記述{1, 2}
個数がずれています。興味持って、いくつかパタン試し:
func arr3(){
a1 := [2]int{} // output: [0 0]
a2 := [2]int{1, 2} // output: [1 2]
a3 := [2]int{1, 2, 3} // array index 2 out of bounds [0:2]
fmt.Println(a1, a2, a3)
}
(実は言うまでもないかもしれない)新発見: v := [長さn]Type{要素数m}
に対して
- n < m の場合、コンパイルエラー(out of bounds)
- n >= m の場合、コンパイルOK(
m+1 ~ n 個目要素の値は、その要素型の0値となる
と言われてます)
#新発見由来の苦悩
でも、a2 := [2]int{1, 2}
のような記述を見ると、なんとなく違和感があるように思えます。
そもそも、{}
に要素記述するれば、配列長さは自動的に検知できるものでしょう、なのにわざわざn
を指定するのは、例のDRY原則と相違するじゃないか。
そう思って、n無し版a2 := []int{1, 2}
試してみたら、無事コンパイルできました! じゃぁ、今後はもうn指定なんかやめる!(このとき自分は既にarrayからsliceへと別物作っていることとは知らなかった)
でも、さらにいろいろ試してみたら、予想通りに動かないところがありました:
func arr_or_slice_1() {
v1 := [1]string{"元の値"} // n指定
v2 := v1
v2[0] = "新しい値"
fmt.Printf("v1[0]:%v\n", v1[0]) // output: v1[0]:元の値
}
func arr_or_slice_2() {
v1 := []string{"元の値"} // n指定なし
v2 := v1
v2[0] = "新しい値"
fmt.Printf("v1[0]:%v\n", v1[0]) // output: v1[0]:新しい値
}
n指定有りなしで、結果が変わってます。これは考えもしなかった結果です。 今までのコーディング経験のベースのところと衝突しているくらいなので、しばらくは目を疑い、更にパソコン壊れているかを疑いました。自分ひとりではどうしても抜き出せないところでしたので、色んな方にこの悩みを聞くことにしました。 当時は、確かだれかからsliceになっているから
と言われた記憶がありますが、sliceだからarrayと違う
という前提知識は頭になかったから、理解できませんし聞き逃しました。
#配列は値型、スライスは参照型
しばらくして、またgoのことを勉強・やってみようと思い、ある記事を呼んでみたら、配列は値型、スライスは参照型
との記述に目を留め、実際に手を動かして見たら、この前の苦悩は、一気に解消しました:
func arr_and_slice() {
//n指定の場合は 配列
a1 := [1]int{1}
a2 := a1
a2[0] = 2
fmt.Println(a1, a2) // output: [1] [2]
//n指定無しの場合は slice
s1 := []int{1}
s2 := s1
s2[0] = 2
fmt.Println(s1, s2) // output: [2] [2]
}
ということで,以下2のことが分かったことで苦悩解消:
- n指定の場合は 配列,指定なしの場合はslice
- 配列は値型、スライスは参照型
上記書いた悩みなどは、自分が勉強不足で発生したもので、まだまだ知っていれば大丈夫
レベルです。
それより、sliceのappendとcapとの間に纏わる知っていても、気が付かないとやられること
も書きたかったですが、記事書くことは本当に予想以上に時間・頭かかることで、取り敢えずここまでとすることにしました。
今後も是非記事書くことに頑張っていきたいと思います。