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] — оригинал не изменёнПолезное
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
