Повторная нарезка слайсов в Golang

Я недавно взялся за язык 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]

person Coder    schedule 07.10.2012    source источник
comment
Идут слайсы со своей емкостью (идут 1.2). См. мой ответ ниже   -  person VonC    schedule 20.09.2013


Ответы (2)


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] 
person Denys Séguret    schedule 07.10.2012
comment
Спасибо за полезный ответ. - person Coder; 07.10.2012
comment
Таким образом, b также будет затронут? - person Coder; 07.10.2012
comment
Да. Но вы этого не увидите, потому что длина равна 0. - person Denys Séguret; 07.10.2012

Обратите внимание, что в версии go 1.2 (4 квартал 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)

В Go 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) не соответствует действительности.

person VonC    schedule 20.09.2013