Go での予期しないスライス追加動作
Go プログラミングの領域では、要素をスライスに追加するときに異常な動作が発生しました。ループを実行し、その後ループの結果に基づいて新しいスライスを作成しようとします。興味深いことに、最後の追加操作は、前の追加で作成されたスライスをオーバーライドします。
問題の調査
次のコード スニペットを考えてみましょう:
<code class="go">func create(iterations int) []int { a := make([]int, 0) for i := 0; i < iterations; i++ { a = append(a, i) } return a }</code>
create(11) を呼び出し、要素を追加して新しいスライスを作成する場合 (つまり、 j := append(i, 100)、 g := append(i, 101)、 h := append(i, 102))、これらのスライス (j、g、および h) の最後の要素は、それぞれ 100、101、および 102 であると予想されます。しかし、驚くべきことに、この場合、最終的にはすべて 102 になります。
この動作は、次に示すように、スライス リテラルから新しいスライスを作成するときに起こることとはまったく対照的です。
<code class="go">func sliceFromLiteral() { i := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} j := append(i, 100) g := append(i, 101) h := append(i, 102) fmt.Printf("i: %v\nj: %v\ng: %v\nh:%v\n", i, j, g, h) }</code>
この場合、j、g、h は予想どおりの動作を示し、最後の要素はそれぞれ 100、101、102 です。
根本原因の詳細
この予期しない動作の背後にある謎を解明するには、追加操作が基になる配列を変更するだけでなく、新しいスライスも返すことを理解することが重要です。これは、スライス j、g、h が実際には同じ基になる配列を指していることを意味します。したがって、最後の追加 (append(i, 102)) が実行されると、基礎となる配列の最後の要素が変更され、j、g、および h の値が効果的にオーバーライドされます。
解決策
この予期しない動作を回避するには、追加を試みる前にスライスをコピーすることが不可欠です。これにより、新しい基になる配列が作成され、元のスライスが変更されないことが保証されます。次のコードは、既存のスライスに基づいて複数の新しいスライスを作成する慣用的な方法を示しています。
<code class="go">func makeFromSlice(sl []int) []int { result := make([]int, len(sl)) copy(result, sl) return result }</code>
このアプローチを採用すると、元のデータの整合性を維持しながら新しいスライスを簡単に作成できます。
スライス リテラル例外
ループから作成されたスライスで観察される独特の動作は、リテラルから初期化されたスライスには拡張されません。これは、追加操作がバッキング配列の容量を超える場合に、Go が新しい配列を割り当てるという事実に起因します。この動作は、スライスがリテラルから作成されるか変数から作成されるかには関係なく、単に Go での配列の内部処理方法の結果です。
以上がGo で新しいスライスを作成するときに、ループ内に追加されたスライスが予期しない動作を示すのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。