Golang: Передача срезов в функцию
Срезы в Go — это лёгкие структуры, содержащие указатель на базовый массив, длину (len
) и вместимость (cap
). При передаче в функцию срез копируется как структура, но оба среза продолжают указывать на один и тот же массив. Это поведение важно для понимания того, когда изменения внутри функции отражаются на исходных данных, а когда нет.
Передача срезов
При передаче среза в функцию изменения элементов видны снаружи, потому что копируется только структура, а не массив.
func modify(s []int) {
s[0] = 99
}
func main() {
nums := []int{1, 2, 3}
modify(nums)
fmt.Println(nums) // => [99 2 3]
}
Здесь функция modify()
изменила первый элемент, и изменение видно в main()
.
Вызов append() внутри функции
При использовании append()
поведение зависит от вместимости исходного среза:
- Если
append()
помещается в существующий массив, изменения видны снаружи. - Если
append()
создаёт новый массив (переполненаcap
), изменения не будут видны, если не вернуть новый срез.
func addElement(s []int) {
s = append(s, 100)
}
func main() {
nums := []int{1, 2, 3}
addElement(nums)
// изменений нет
fmt.Println(nums) // => [1 2 3]
}
Чтобы изменения сохранились, возвращаем срез из функции:
func addElement(s []int) []int {
return append(s, 100)
}
func main() {
nums := []int{1, 2, 3}
nums = addElement(nums)
fmt.Println(nums) // => [1 2 3 100]
}
Возврат срезов
Возвращение срезов из функций безопасно: копируется только структура, а массив остаётся в памяти столько, сколько нужен.
func newSlice() []int {
s := []int{1, 2, 3}
return s
}
func main() {
data := newSlice()
fmt.Println(data) // => [1 2 3]
}
Под-срезы и утечки памяти
Если взять под-срез большого массива, он продолжит удерживать в памяти весь массив, даже если используется только часть данных.
data := make([]int, 10000)
part := data[:10] // удерживает весь массив из 10000 элементов
Чтобы освободить неиспользуемую память, можно сделать копию:
import "slices"
data := make([]int, 10000)
part := slices.Clone(data[:10])
Вывод
- Срезы передаются по значению, но содержат указатель на общий массив.
- Изменения элементов внутри функции видны снаружи.
- При использовании
append()
новый срез может указывать на другой массив — его нужно возвращать. - Возврат срезов безопасен, но под-срезы больших массивов могут удерживать память, которую стоит освобождать через копирование.
- Чтобы избежать утечки памяти, скопируйте нужную часть среза, например через
slices.Clone()
.
Задание
Реализуйте функцию AddDiscount(prices []int, discount int) []int
, которая применяет скидку к списку цен. Скидка указывается в процентах (целое число от 0 до 100). Функция должна вернуть новый срез с изменёнными ценами, не изменяя оригинальный.
Примеры
prices := []int{100, 200, 300}
fmt.Println(AddDiscount(prices, 10)) // [90 180 270]
fmt.Println(prices) // [100 200 300] — оригинал не изменён
Полезное
Команда проекта находится в телеграм-сообществе. Там можно задать любой вопрос и повлиять на проект
Если вы зашли в тупик, то самое время поговорить с нашим асситентом Тота во вкладке "ИИ-помощник":
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи. В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в обратной связи нашего сообщества
Ваше упражнение проверяется по этим тестам
package solution
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestAddDiscount(t *testing.T) {
tests := []struct {
name string
prices []int
discount int
expected []int
}{
{
name: "No discount",
prices: []int{100, 200},
discount: 0,
expected: []int{100, 200},
},
{
name: "10 percent discount",
prices: []int{100, 200, 300},
discount: 10,
expected: []int{90, 180, 270},
},
{
name: "100 percent discount",
prices: []int{100, 50},
discount: 100,
expected: []int{0, 0},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
orig := append([]int(nil), tt.prices...) // копия для проверки
result := AddDiscount(tt.prices, tt.discount)
assert.Equal(t, tt.expected, result)
assert.Equal(t, orig, tt.prices, "original slice should remain unchanged")
})
}
}
Решение учителя откроется через:
20:00
