[Перевод] Советы по срезу Голанга

Go

Эта статья переведена сSliceTricks. Я добавлю некоторые из моего понимания. Код примера, приведенный официалом, очень красив, рекомендуем вам взглянуть на него, особенно очень интересна работа на месте функции разделения базового массива с несколькими слайсами.


container/vectorПакет был удален в Go 1 из-за введения встроенных модулей.append, что плюс встроенная функцияcopyВ основном заменить функцию этого пакета.

AppendVector

a = append(a, b...)

appendПоддерживаются два параметра: первый — добавляемый срез, а второй — добавляемые данные. Второй параметр — это параметр переменной длины, который может передавать несколько значений.

Copy

b = make([]T, len(a))
copy(b, a)
// or
b = append([]T(nil), a...)

copyФункция копирует данные a в b. Он имеет яму.Длина копируемых данных зависит от текущей длины b.Если b не инициализирован, операция копирования не произойдет. Таким образом, первая скопированная строка должна инициализировать длину.

Есть еще одна альтернатива копированию, использованиеappendмногозначный случай для добавления. Но у этого будет проблема, когда добавление добавляется к[]T(nil)Внутри начальная длина по умолчанию равна 0. Каждый раз, когда вы добавляете элемент, вам нужно проверить, удовлетворяет ли текущая длина.Если она не удовлетворяет, вам нужно расширить емкость.Каждый раз, когда вы расширяете емкость, вы можете удвоить текущая емкость (для получения подробной информации вы можете проверить внутреннюю реализацию слайса). Таким образом, если длина a равна 3, длина и пропускная способность первого метода равны 3, а длина второго метода равна 3, но пропускная способность равна 4. Если это просто копия, я рекомендую первую.

Cut

a = append(a[:i], a[j:]...)

Отрежьте элементы в середине [i, j). Все ломтики ломтиков основаны на принципе открытия перед закрытием.

Delete

a = append(a[:i], a[i+1:]...)
// or
a = a[:i+copy(a[i:], a[i+1:])]

Удалить элемент в позиции i. Первый заключается в удалении путем вырезания. Потому что это просто удаление, этоappendОперация не приводит к расширению базовых данных. Просто данные после i обновляются. В это время длина уменьшается на 1, а вместимость не меняется.

Второй способ, используяcopyМетоды.将 i 后面所有的数据迁移,然后删除最后一位数据。将 i+1 到 len(a) 的数据复制到 i 到 len(a)-1的位置上。copyВозвращаемое значение метода — это длина скопированного элемента, поэтому он напрямую используется для усечения, а последний бит a не удаляется.

Основные результаты этих двух операций одинаковы. Элементы, следующие за удаленным элементом, необходимо скопировать.

Удалить без сохранения порядка

a[i] = a[len(a)-1] 
a = a[:len(a)-1]

Удалить элемент в i-й позиции, поставить последний бит в i-ю позицию и удалить последний элемент. Таким образом, базовая операция копирования не выполняется.

УведомлениеЕсли тип среза является указателем на структуру или если срез структуры содержит указатели, данные необходимо очистить от мусора, чтобы освободить память после удаления. Тем не менее, вышеизложенноеCutиDeleteЭтот метод может вызвать утечку памяти: на удаленные данные по-прежнему ссылается a (ссылка в базовых данных), и сборка мусора невозможна. Эту проблему можно решить с помощью следующего кода:

Cut

copy(a[i:], a[j:])
for k, n := len(a)-j+i, len(a); k < n; k++ {
	a[k] = nil // or the zero value of T
}
a = a[:len(a)-j+i]

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

Delete

copy(a[i:], a[i+1:])
a[len(a)-1] = nil // or the zero value of T
a = a[:len(a)-1]

Delete without preserving order

a[i] = a[len(a)-1]
a[len(a)-1] = nil
a = a[:len(a)-1]

Expand

a = append(a[:i], append(make([]T, j), a[i:]...)...)

Продлите кусок длины j в промежуточном положении i.

Extend

a = append(a, make([]T, j)...)

Расширьте кусок длины j в конце.

Insert

a = append(a[:i], append([]T{x}, a[i:]...)...)

Вставьте элемент x в позицию i.

Уведомлениечерез второйappendдобавить a[i:] к x (эта операция вызовет[]T{x}происходит несколько расширений), затем передайте первыйappendДобавьте этот новый фрагмент в a[:i] (Эта операция приведет к расширению один раз). Эти две операции создают новый слайс (что эквивалентно созданию мусора в памяти), и второй копии также можно избежать:

Insert

s = append(s, 0)
copy(s[i+1:], s[i:])
s[i] = x

сначала черезappendРасширение среза, а затем после заднего сдвига элемента i окончательная копия. Вся операция после расширения.

InsertVector

a = append(a[:i], append(b, a[i:]...)...)

Вставьте ломтик B в положение I.

Pop/Shift

x, a = a[0], a[1:]

Одна строка реализует pop для удаления заголовка очереди из очереди.

Pop Back

x, a = a[len(a)-1], a[:len(a)-1]

Одна строка реализует pop для удаления конца очереди.

Push

a = append(a, x)

поместите x в конец очереди.

Push Front/Unshift

a = append([]T{x}, a...)

поместите x в начало очереди.

Дополнительные приемы

Фильтрация без выделения не применяется к памяти для фильтрации данных

Базовый массив из нескольких срезов, вероятно, один и тот же, и этот принцип может реализовать мультиплексную фильтрацию данных реализации базового массива. Конечно, содержимое базового массива будет изменено после фильтрации.

b := a[:0]
for _, x := range a {
	if f(x) {
		b = append(b, x)
	}
}

Фильтрация на месте. Сначала объявите срез b, который разделяет базовый массив с массивом a. Перейдите a, чтобы отфильтровать, и добавьте к b после фильтрации. Таким образом, a и b изменяются одновременно. b — правильный срез после фильтрации, тогда как данные a будут перепутаны.

Реверс

Обратный порядок данных среза:

for i := len(a)/2-1; i >= 0; i-- {
	opp := len(a)-1-i
	a[i], a[opp] = a[opp], a[i]
}

Чтобы снова упростить код, вы также можете опустить индекс, используемый для инверсии:

for left, right := 0, len(a)-1; left < right; left, right = left+1, right-1 {
	a[left], a[right] = a[right], a[left]
}

Случайное перемешивание

Алгоритм Фишера – Йейтса:

Требуется Go 1.10+math/rand.Shuffle

for i := len(a) - 1; i > 0; i-- {
    j := rand.Intn(i + 1)
    a[i], a[j] = a[j], a[i]
}

Каждые данные выходят в случайной новой позиции.


Оригинальная ссылка:Советы по нарезке,Если воспроизводится, укажите источник!