Повторное нарезание ломтиков в Голанге

Я недавно выбрал язык Go, и теперь меня смущает следующий код:

package main

import "fmt"

func main() {
    a := make([]int, 5)
    printSlice("a", a)
    b := make([]int, 0, 5)
    printSlice("b", b)
    c := b[:2]
    printSlice("c", c)
    d := c[2:5]
    printSlice("d", d)
}

func printSlice(s string, x []int) {
    fmt.Printf("%s len=%d cap=%d %v\n",
        s, len(x), cap(x), x)
}

И результат:

a len=5 cap=5 [0 0 0 0 0]
b len=0 cap=5 []
c len=2 cap=5 [0 0] //why the capacity of c not 2 but 5 instead
d len=3 cap=3 [0 0 0]

Ответы

Ответ 1

c - это срез, взятый из массива b. Это не копия, а просто окно над двумя первыми элементами b.

Поскольку b имеет емкость 5, c может быть расширен, чтобы занять 3 других места (на самом деле он создает новый срез, но в одном и том же месте в памяти).

Максимальная емкость среза - это емкость базового массива за вычетом позиции начала среза в массиве:

 array : [0 0 0 0 0 0 0 0 0 0 0 0]
 array :  <----   capacity   --->
 slice :     [0 0 0 0]
 slice :      <---- capacity ---> 

Возможно, эта программа сделает более понятным, что c и d - это просто окна над b:

func main() {
    b := make([]int, 0, 5)
    c := b[:2]
    d := c[1:5] // this is equivalent to d := b[1:5]
    d[0] = 1
    printSlice("c", c)
    printSlice("d", d)
}

Выход:

c len=2 cap=5 [0 1] // modifying d has modified c
d len=4 cap=4 [1 0 0 0] 

Ответ 2

Обратите внимание, что в go 1.2 (Q4 2013, теперь доступна 1.2rc1), вы можете связать с срезом емкость собственного (вместо мощности, выводимой из базового массива).

См. "трехиндексные фрагменты и проект.

Операция среза создает новый срез, описывая непрерывный раздел уже созданного массива или среза:

var array [10]int
slice := array[2:4]

Емкость среза - это максимальное количество элементов, которые может удерживать срез, даже после повторного выделения; он отражает размер базового массива.
В этом примере емкость переменной среза равна 8.

(емкость базового массива минус позиция начала среза в массиве)

array : [0 0 0 0 0 0 0 0 0 0]
 array : <---- capacity --->
 slice :    [0 0]
 slice :    <-- capacity --> 8 (10-2)

Перейти 1.2 добавляет новый синтаксис, позволяющий операции разрезания указать емкость, а также длину.
Второй двоеточие вводит значение емкости, которое должно быть меньше или равно емкости среза источника или массива, скорректированного на начало координат.

Например,

slice = array[2:4:6]

array : [0 0 0 0 0 0 0 0 0 0]
 array : <---- capacity --->   10
 slice :    [0 0]
 slice :    <- cap->           4 (6-2)

устанавливает, чтобы срез имел ту же длину, что и в предыдущем примере, но его емкость теперь составляет всего 4 элемента (6-2).
Невозможно использовать это новое значение среза для доступа к последним двум элементам исходного массива.

Основной аргумент - дать программистам больше контроля над append.

a[i : j : k]

Этот срез имеет:

  • начинающиеся с 0
  • длина равна j - i
  • емкость равна k - i

Паника оценки, если i <= j <= k <= cap(a) не соответствует true.