Выражения среза в Go

В Golang есть много отличных знакомств с ломтиками:

Эта история посвящена исключительно выражениям срезов. Они могут создавать два типа значений:

  • подстрока из строкового операнда
  • фрагмент из массива, указатель на массив или сам фрагмент

Спецификация языка определяет два типа выражений среза - простые и полные ...

Простые выражения среза

Наиболее известная форма выражения среза:

вход [низкий: высокий]

Индексы low и high должны быть целыми числами. Они указывают, какие элементы операнда (input) помещаются внутри результирующего фрагмента или строки. Результат содержит элементы операнда, начиная с low (включительно) до high (исключительно). Операнд - это строка, массив, указатель на массив или срез (площадка):

fmt.Println("foobar"[1:3]) // "oo"
numbers := [5]int{1, 2, 3, 4, 5}
fmt.Println(numbers[1:3]) // [2, 3]

Длина результата high — low

Применение выражения среза к указателю массива является сокращением для первого разыменования такого указателя, а затем регулярного применения выражения среза (площадка):

numbers := [5]int{1, 2, 3, 4, 5}
fmt.Println((&numbers)[1:3]) // [2, 3]

Индексы low или high можно не указывать. Затем используются значения по умолчанию. Для low это 0, а для high это длина операнда (площадка):

fmt.Println("foo"[:2]) // "fo"
fmt.Println("foo"[1:]) // "oo"
fmt.Println("foo"[:]) // "foo"

Индексы не могут быть произвольными числами (площадка):

  • отрицательные числа не допускаются
  • низкийвысокий
  • highlen (ввод)
//fmt.Println("foo"[-1:]) // invalid slice index -1 (index must be non-negative)
//fmt.Println("foo"[:4]) // invalid slice index 4 (out of bounds for 3-byte string)
fmt.Println("foo"[2:2]) // ""(blank)
//fmt.Println("foo"[2:1]) // invalid slice index: 2 > 1

Если индексы вне диапазона не могут быть обнаружены при компиляции, возникает паника во время выполнения (игровая площадка):

func low() int {
 return 4
}
func main() {
    fmt.Println("foo"[low():])
}
panic: runtime error: slice bounds out of range

goroutine 1 [running]:
panic(0x102280, 0x1040a018)
	/usr/local/go/src/runtime/panic.go:500 +0x720
main.main()
	/tmp/sandbox685025974/main.go:12 +0x120

Полные выражения среза

Применяется только к массиву, указателю на массив или фрагменту (строки исключаются). Он позволяет контролировать емкость возвращаемого среза. С помощью простого выражения среза емкость возвращаемого среза - это максимально возможная емкость, начиная с low, то есть cap(input) — low (площадка):

numbers := [10]int{0,1,2,3,4,5,6,7,8,9}
s := numbers[1:4]
fmt.Println(s) // [1, 2, 3]
fmt.Println(cap(s)) // 9

Для массива a cap(a) == len(a)

В приведенном выше фрагменте емкость s равна 9, поскольку срез начинается с индекса 1, а в базовом массиве есть еще 8 элементов (2–9). Выражения полного среза позволяют изменить это поведение по умолчанию (игровая площадка):

numbers := [10]int{0,1,2,3,4,5,6,7,8,9}
s := numbers[1:4:5]
fmt.Println(s) // [1, 2, 3]
fmt.Println(cap(s)) // 4

Выражение полного среза имеет следующий синтаксис:

вход [низкий: высокий: макс]

Индексы low и high работают так же, как и с простыми выражениями среза. Единственное отличие - max, при котором размер результата устанавливается равным max — low (площадка):

numbers := [10]int{0,1,2,3,4,5,6,7,8,9}
s := numbers[2:4:6]
fmt.Println(s) // [2, 3]
fmt.Println(cap(s)) // 4

При нарезке операнда, который является срезом, емкость зависит от операнда, а не от базового массива (площадка):

numbers := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(cap(numbers)) // 10
s1 := numbers[1:4]
fmt.Println(s1) // [1, 2, 3]
fmt.Println(cap(s1)) // 9
s2 := numbers[1:4:5]
fmt.Println(s2) // [1, 2, 3]
fmt.Println(cap(s2)) // 4
s3 := s2[:]
fmt.Println(s3) // [1, 2, 3]
fmt.Println(cap(s3)) // 4

Емкость s3 не может быть увеличена до более чем 4 (емкость s2), даже если базовая часть имеет 10 элементов и s1, s2 или s3 начинаются с индекса 1.

Правило из предыдущего раздела (highlen (input)) имеет одно исключение. Когда операнд является срезом, тогда high фактически не может быть больше, чем cap (input) (площадка):

numbers := [10]int{0,1,2,3,4,5,6,7,8,9}
s1 := numbers[0:1]
fmt.Println(s1) // [0]
fmt.Println(len(s1)) // 1
fmt.Println(cap(s1)) // 10
s2 := numbers[0:5]
fmt.Println(s2) // [0, 1, 2, 3, 4]
fmt.Println(cap(s1)) // 10

Когда он достигает значения max, существуют два дополнительных правила относительно его значения (игровая площадка):

  • высокий ≤ макс
  • max ≤ cap (ввод)
numbers := [10]int{0,1,2,3,4,5,6,7,8,9}
s1 := numbers[0:1]
s2 := numbers[0:5:11] // invalid slice index 11 (out of bounds for 10-element array)
fmt.Println(s1, s2)

В выражении полного среза необязателен только индекс low (площадка):

numbers := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s := numbers[:4:6]
fmt.Println(s) // [0, 1, 2, 3]
fmt.Println(cap(s)) // 6

Пропуск high не допускается (детская площадка):

numbers := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := numbers[:4:6]
s2 := s1[::5]
fmt.Println(s2)
fmt.Println(cap(s2))

Такой код даже не скомпилируется - middle index required in 3-index slice.

Нажмите ❤ ниже, чтобы помочь другим узнать эту историю. Если вы хотите получать обновления о новых сообщениях, подписывайтесь на меня.

Ресурсы