Практические задачи со срезами
1. Генерация случайных чисел
Задача
Создать срез из 5 случайных чисел в диапазоне 0-99.
Решение с предварительным выделением памяти
randomNums := make([]int, 0, 5) // len=0, cap=5
for i := 0; i < 5; i++ {
randomNums = append(randomNums, rand.Intn(100))
}
fmt.Println(randomNums) // [42 17 89 3 56]
Почему make([]int, 0, 5)?
Заранее резервируем ёмкость для 5 элементов, чтобы избежать лишних перевыделений памяти.
Альтернатива: без append
randomNums := make([]int, 5) // len=5, cap=5
for i := 0; i < 5; i++ {
randomNums[i] = rand.Intn(100)
}
fmt.Println(randomNums) // [42 17 89 3 56]
Разница:
- Первый способ:
len=0, добавляем черезappend - Второй способ:
len=5, присваиваем по индексу
2. Фильтрация чётных чисел
Задача
Из исходного среза выбрать только чётные числа.
Решение через for range
allNums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
evenNums := []int{} // Пустой срез
for _, num := range allNums {
if num%2 == 0 {
evenNums = append(evenNums, num)
}
}
fmt.Println(allNums) // [1 2 3 4 5 6 7 8 9 10]
fmt.Println(evenNums) // [2 4 6 8 10]
Механизм:
- Перебираем исходный срез
- Проверяем условие
num % 2 == 0 - Если истина — добавляем в новый срез
Классический for (альтернатива)
evenNums := []int{}
for i := 0; i < len(allNums); i++ {
if allNums[i]%2 == 0 {
evenNums = append(evenNums, allNums[i])
}
}
3. Разворот (реверс) среза
Задача
Развернуть срез в обратном порядке in-place (без создания нового).
Решение: обмен элементов с концов
toReverse := []int{1, 2, 3, 4, 5}
fmt.Println(toReverse) // [1 2 3 4 5]
for i, j := 0, len(toReverse)-1; i < j; i, j = i+1, j-1 {
toReverse[i], toReverse[j] = toReverse[j], toReverse[i]
}
fmt.Println(toReverse) // [5 4 3 2 1]
Разбор цикла
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1
| Компонент | Описание |
|---|---|
i, j := 0, len(s)-1 |
Два счётчика: начало и конец |
i < j |
Пока не встретились в середине |
i, j = i+1, j-1 |
i движется вправо, j влево |
Визуализация:
Итерация 1: i=0, j=4 → меняем [1, 2, 3, 4, 5] → [5, 2, 3, 4, 1]
Итерация 2: i=1, j=3 → меняем [5, 2, 3, 4, 1] → [5, 4, 3, 2, 1]
Итерация 3: i=2, j=2 → i >= j, стоп
Специфичная форма цикла for
Из транскрипта:
“Специфичная форма цикла for, которую вы скорее всего в реальных задачах не увидите, но тем не менее делать это можно”.
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
// Два счётчика изменяются одновременно
}
Особенности:
- Инициализация двух переменных через запятую
- Обновление обеих переменных в одной строке
- Нетипично для других языков программирования
4. Обмен значений (swap)
Идиоматичный Go-способ
a, b := 1, 2
a, b = b, a // Без временной переменной!
fmt.Println(a, b) // 2 1
В контексте среза:
s[i], s[j] = s[j], s[i]
5. Сводная таблица паттернов
| Задача | Паттерн | Ключевая идея |
|---|---|---|
| Генерация N элементов | make([]T, 0, N) + append |
Резервируем cap заранее |
| Фильтрация | for range + условие + append |
Новый срез с подходящими элементами |
| Реверс | Два указателя i, j + swap |
Меняем элементы с концов к центру |
6. Почему резервировать cap заранее?
Без резервирования (много перевыделений)
s := []int{} // cap=0
for i := 0; i < 1000; i++ {
s = append(s, i) // Множество перевыделений памяти
}
Проблема: При len == cap каждый append создаёт новый массив.
С резервированием (одно выделение)
s := make([]int, 0, 1000) // cap=1000
for i := 0; i < 1000; i++ {
s = append(s, i) // Без перевыделений
}
Оптимизация: Память выделяется один раз для 1000 элементов.
7. Фильтрация с сохранением порядка
Задача: удалить отрицательные числа
nums := []int{-5, 3, -2, 7, -1, 9}
positive := []int{}
for _, num := range nums {
if num >= 0 {
positive = append(positive, num)
}
}
fmt.Println(positive) // [3 7 9]
8. Реверс без изменения оригинала
original := []int{1, 2, 3, 4, 5}
// Создаём копию
reversed := make([]int, len(original))
for i := 0; i < len(original); i++ {
reversed[i] = original[len(original)-1-i]
}
fmt.Println(original) // [1 2 3 4 5]
fmt.Println(reversed) // [5 4 3 2 1]
9. Итоги по паттернам
Генерация данных
✅ Используйте make([]T, 0, capacity) для известного размера
✅ Заполняйте через append() или прямое присваивание
Фильтрация
✅ Создавайте новый срез: filtered := []T{}
✅ Перебирайте через for range
✅ Добавляйте элементы, удовлетворяющие условию
Реверс
✅ Два указателя: i, j := 0, len(s)-1
✅ Swap: s[i], s[j] = s[j], s[i]
✅ Цикл: for i < j
10. Ключевые принципы
✅ Предварительное выделение памяти через make([]T, 0, cap) экономит операции
✅ Фильтрация всегда создаёт новый срез через append()
✅ Реверс через обмен элементов с концов — классический алгоритм
✅ Множественное присваивание i, j = i+1, j-1 — идиома Go
✅ Специфичные формы циклов возможны, но редко используются на практике
Формула успеха:
Знание задачи → Выбор паттерна → Применение функций (append, make, copy)