はじめてのGo―シンプルな言語仕様,型システム,並行処理

第2章 基本文法―覚えやすいコンパクトな言語仕様

この記事を読むのに必要な時間:およそ 9.5 分

配列

Goの配列は固定長です。可変長配列は後述するスライスがそれにあたります。たとえば長さが4で要素の型がstringである配列は,次のように宣言します。

var arr1 [4]string

配列は,ほかの言語同様に添字でアクセスします。

var arr [4]string

arr[0] = "a"
arr[1] = "b"
arr[2] = "c"
arr[3] = "d"
fmt.Println(arr[0]) // a

宣言と同時に初期化することも可能で,その場合は[...]を用いることで,必要な配列の長さを暗黙的に指定できます。

// どちらも同じ型
arr := [4]string{"a", "b", "c", "d"}
arr := [...]string{"a", "b", "c", "d"}

配列の型は長さも情報として含むため,次のarr1arr2は,要素の型は同じstringですが,長さが違うため配列としては別の型です。関数fn[4]string型を引数にとるため,型の合わないarr2を渡すとコンパイルエラーになります。

func fn(arr [4]string) {
    fmt.Println(arr)
}

func main() {
    var arr1 [4]string
    var arr2 [5]string

    fn(arr1) // ok
    fn(arr2) // コンパイルエラー
}

また,関数に配列を渡す場合は値渡しとなり,配列のコピーが渡されます。次のfn()の中で配列に対して行った変更は,main()側には反映されません。

func fn(arr [4]string) {
    arr[0] = "x"
    fmt.Println(arr) // [x b c d]
}

func main() {
    arr := [4]string{"a", "b", "c", "d"}
    fn(arr)
    fmt.Println(arr) // [a b c d]
}

スライス

スライスは,可変長配列として扱うことができます。配列を直接使うのは,シビアなメモリ管理が必要な一部のプログラムだけなので,同じ性質のデータを束ねて扱うという用途であれば,基本的にはスライスを用います。

なお,スライスの詳細な内部構造については筆者のブログ記事を参照してください。

スライスの宣言

stringのスライスは次のように宣言します。

var s []string

このように,スライスの型には配列のように長さの情報はありません。

初期化を同時に行う場合は,配列と同じように書くことができます。またスライスも,配列同様に添字でアクセスできます。

s := []string{"a", "b", "c", "d"}
fmt.Println(s[0]) // "a"

append()

スライスの末尾に値を追加する場合はappend()を使用します。append()は,スライスの末尾に値を追加し,その結果を返す組込み関数です。複数の値を追加することもできます。

var s []string
s = append(s, "a") // 追加した結果を返す
s = append(s, "b")
s = append(s, "c", "d")
fmt.Println(s) // [a b c d]

次のように指定すれば,スライスに別のスライスの中身を展開して追加することもできます。

s1 := []string{"a", "b"}
s2 := []string{"c", "d"}
s1 = append(s1, s2...) // s1にs2を追加
fmt.Println(s1)        // [a b c d]

range

配列やスライスに格納された値を,先頭から順番に処理するような場合は,添字によるアクセスの代わりにrangeを使用できます。

for文の中でrangeを用いると,添字と値の両方が取得できます。

var arr [4]string

arr[0] = "a"
arr[1] = "b"
arr[2] = "c"
arr[3] = "d"

for i, s := range arr {
    // i = 添字, s = 値
    fmt.Println(i, s)
}

実行結果は次のようになります。

$ go run range.go
0 a
1 b
2 c
3 d

rangeは配列やスライスのほかに,string,マップ,チャネルに対しても使用できます。マップについては本章で,チャネルについては5章で解説します。

値の切り出し

string,配列,スライスから,値を部分的に切り出すことができます。次のように始点と終点をコロンで挟んで指定すると,その範囲の値を切り出すことができます。始点,終点を省略した場合,それぞれ先頭,末尾になります。

s := []int{0, 1, 2, 3, 4, 5}
fmt.Println(s[2:4])      // [2 3]
fmt.Println(s[0:len(s)]) // [0 1 2 3 4 5]
fmt.Println(s[:3])       // [0 1 2]
fmt.Println(s[3:])       // [3 4 5]
fmt.Println(s[:])        // [0 1 2 3 4 5]

可変長引数

関数において引数を次のように指定すると,可変長引数として,任意の数の引数をその型のスライスとして受け取ることができます。

func sum(nums ...int) (result int) {
    // numsは[]int型
    for _, n := range nums {
        result += n
    }
    return
}

func main() {
    fmt.Println(sum(1, 2, 3, 4))  // 10
}

著者プロフィール