コンテンツにスキップ

Go/append

出典: フリー教科書『ウィキブックス(Wikibooks)』
< Go

appendとは

[編集]

appendはGoの事前宣言された組み込み関数で、スライスに要素を追加するために使用されます。

func append(slice []Type, elems ...Type) []Type

基本的な使い方

[編集]
s := []int{1, 2, 3}
s = append(s, 4)        // [1, 2, 3, 4]
s = append(s, 5, 6, 7)  // [1, 2, 3, 4, 5, 6, 7]

他のキーワード・識別子との組み合わせ

[編集]

1. makeとの組み合わせ

[編集]
// 事前にキャパシティを指定してスライスを作成
s := make([]int, 0, 10)
s = append(s, 1, 2, 3)

// 長さとキャパシティを指定
s := make([]int, 5)     // [0, 0, 0, 0, 0]
s = append(s, 6, 7)     // [0, 0, 0, 0, 0, 6, 7]

2. ...演算子との組み合わせ

[編集]
// スライスを展開して追加
s1 := []int{1, 2, 3}
s2 := []int{4, 5, 6}
s1 = append(s1, s2...)  // [1, 2, 3, 4, 5, 6]

// 文字列を[]byteに変換して追加
s := []byte("Hello")
s = append(s, " World"...)  // []byte("Hello World")

3. caplenとの組み合わせ

[編集]
s := make([]int, 0, 5)
fmt.Printf("len=%d cap=%d\n", len(s), cap(s))  // len=0 cap=5

s = append(s, 1, 2, 3)
fmt.Printf("len=%d cap=%d\n", len(s), cap(s))  // len=3 cap=5

s = append(s, 4, 5, 6)  // キャパシティを超える
fmt.Printf("len=%d cap=%d\n", len(s), cap(s))  // len=6 cap=10(自動拡張)

4. copyとの組み合わせ

[編集]
// appendとcopyを組み合わせた効率的なスライス操作
func insertAtIndex(slice []int, index int, value int) []int {
    slice = append(slice, 0)
    copy(slice[index+1:], slice[index:])
    slice[index] = value
    return slice
}

s := []int{1, 2, 4, 5}
s = insertAtIndex(s, 2, 3)  // [1, 2, 3, 4, 5]

5. nilスライスとの組み合わせ

[編集]
var s []int         // nil スライス
s = append(s, 1)    // [1] - nilスライスでも安全に使用可能

// nilチェックは不要
if s == nil {
    s = append(s, 1)  // これは安全
}

6. deferとの組み合わせ

[編集]
func processItems() []string {
    var results []string
    
    defer func() {
        results = append(results, "cleanup completed")
    }()
    
    results = append(results, "processing...")
    return results
}

7. rangeとの組み合わせ

[編集]
// rangeでスライスを反復しながらappend
source := []int{1, 2, 3, 4, 5}
var evens []int

for _, v := range source {
    if v%2 == 0 {
        evens = append(evens, v)
    }
}

8. [[../interface{}|interface{}]]との組み合わせ

[編集]
// 異なる型の値を格納
var mixed []interface{}
mixed = append(mixed, 1, "hello", true, 3.14)

// 型アサーションと組み合わせ
for _, v := range mixed {
    switch val := v.(type) {
    case int:
        fmt.Printf("Integer: %d\n", val)
    case string:
        fmt.Printf("String: %s\n", val)
    }
}

実用的なユースケース

[編集]

1. 動的配列としての使用

[編集]
func readLines() []string {
    var lines []string
    scanner := bufio.NewScanner(os.Stdin)
    
    for scanner.Scan() {
        lines = append(lines, scanner.Text())
    }
    return lines
}

2. フィルタリング

[編集]
func filter(slice []int, predicate func(int) bool) []int {
    var result []int
    for _, v := range slice {
        if predicate(v) {
            result = append(result, v)
        }
    }
    return result
}

numbers := []int{1, 2, 3, 4, 5, 6}
evens := filter(numbers, func(n int) bool { return n%2 == 0 })

3. スライスのマージ

[編集]
func merge(slices ...[]int) []int {
    var result []int
    for _, slice := range slices {
        result = append(result, slice...)
    }
    return result
}

s1 := []int{1, 2}
s2 := []int{3, 4}
s3 := []int{5, 6}
merged := merge(s1, s2, s3)  // [1, 2, 3, 4, 5, 6]

4. キューの実装

[編集]
type Queue struct {
    items []int
}

func (q *Queue) Enqueue(item int) {
    q.items = append(q.items, item)
}

func (q *Queue) Dequeue() int {
    if len(q.items) == 0 {
        return 0
    }
    item := q.items[0]
    q.items = q.items[1:]
    return item
}

5. 重複除去

[編集]
func unique(slice []int) []int {
    seen := make(map[int]bool)
    var result []int
    
    for _, v := range slice {
        if !seen[v] {
            seen[v] = true
            result = append(result, v)
        }
    }
    return result
}

6. バッファとしての使用

[編集]
type Buffer struct {
    data []byte
}

func (b *Buffer) Write(p []byte) (n int, err error) {
    b.data = append(b.data, p...)
    return len(p), nil
}

func (b *Buffer) String() string {
    return string(b.data)
}

パフォーマンスの考慮事項

[編集]
// 悪い例:キャパシティを考慮しない
func badAppend() []int {
    var s []int
    for i := 0; i < 1000; i++ {
        s = append(s, i)  // 再割り当てが頻繁に発生
    }
    return s
}

// 良い例:事前にキャパシティを設定
func goodAppend() []int {
    s := make([]int, 0, 1000)  // キャパシティを事前設定
    for i := 0; i < 1000; i++ {
        s = append(s, i)
    }
    return s
}

注意点

[編集]
  1. 戻り値の代入: appendは新しいスライスを返すため、結果を元の変数に代入する必要があります
  2. キャパシティの自動拡張: キャパシティが不足すると自動的に拡張されますが、パフォーマンスに影響します
  3. ポインタの共有: 容量を超えない限り、元のスライスと底層配列を共有する可能性があります

appendはGoでスライスを扱う際の中核となる関数で、動的配列やバッファ、データ処理パイプラインなど様々な場面で活用されます。