День 17 · Срезы изнутри: len, cap и общий массив · страница 5 из 7

Готовые функции: пакет slices

Сортировку, поиск и максимум в блоке 2 писали циклом. Для срезов всё это уже есть в стандартном пакете slices. Подключается он как strings в дне 04: строкой в import.


Шесть функций на каждый день

Ситуация. Остатки пяти ячеек: есть ли ячейка с 60, где лежит 12, сколько в самой полной и пустой, все остатки по возрастанию.

Код для чтения · разбираем, набирать не нужно
package main

import (
	"fmt"
	"slices"
)

func main() {
	stock := []int{25, 3, 60, 12, 40}
	fmt.Println(slices.Contains(stock, 60), slices.Contains(stock, 61))
	fmt.Println(slices.Index(stock, 12), slices.Index(stock, 13))
	fmt.Println(slices.Max(stock), slices.Min(stock))
	slices.Sort(stock)
	fmt.Println(stock)
	fmt.Println(slices.Equal(stock, []int{3, 12, 25, 40, 60}))
}

Что увидите.

Вывод · это печатает программа, набирать не нужно
true false
3 -1
60 3
[3 12 25 40 60]
true

Как это читать.

Функция Что делает
slices.Contains(s, v) есть ли в срезе значение v
slices.Index(s, v) индекс первого v или -1, если его нет
slices.Max(s), slices.Min(s) наибольший и наименьший элемент
slices.Sort(s) сортирует сам срез по возрастанию, ничего не возвращает
slices.Equal(a, b) одинаковые ли длины и элементы по порядку
slices.Clone(s) копия со своим массивом — страница 3

Те же функции работают и со строками: срез из маленьких русских слов slices.Sort расставит по алфавиту. С заглавными и с «ё» порядок другой — строки сравниваются по байтам, разберём в дне 19. Опыт со строками — в course extra 17.

В чужом коде встретите sort.Slice и slices.SortFunc: туда передают ещё одну функцию, записанную прямо на месте. Такую запись курс разберёт позже.


Справка не выходя из терминала

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day17/scratch$ go doc slices.Sort
package slices // import "slices"

func Sort[S ~[]E, E cmp.Ordered](x S)
    Sort sorts a slice of any ordered type in ascending order. When sorting
    floating-point numbers, NaNs are ordered before other values.

Строку с квадратными скобками после Sort пока пропускайте: она говорит, что функция принимает срез значений, которые можно сравнивать больше-меньше — чисел или строк. Главное — текст ниже: sorts a slice … in ascending order. Возвращаемого значения в заголовке нет. Сравните с go doc slices.Max:

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day17/scratch$ go doc slices.Max
package slices // import "slices"

func Max[S ~[]E, E cmp.Ordered](x S) E
    Max returns the maximal value in x. It panics if x is empty. For
    floating-point E, Max propagates NaNs (any NaN value in x forces the output
    to be NaN).

E в конце заголовка — результат, «элемент среза». И предупреждение: It panics if x is empty — на пустом срезе паникует.


Попробуйте сейчас: справка по slices.

Цель: прочитать справку по двум функциям и найти, какая из них возвращает значение.

▶ Выполните
cd ~/gocourse/day17/scratch
go doc slices.Sort
go doc slices.Clone

Готово, когда: вы можете сказать, что возвращает slices.Clone и почему x := slices.Sort(s) не соберётся, — пункт t_doc.


Sort меняет сам срез

slices.Sort не делает копию: он переставляет элементы в том массиве, который ему дали. Внутри функции — тот же дефект, что со страницы 4:

Код для чтения · разбираем, набирать не нужно
package main

import (
	"fmt"
	"slices"
)

func low(stock []int) []int {
	slices.Sort(stock)
	return stock[:2]
}

func main() {
	stock := []int{25, 3, 60, 12}
	fmt.Println(low(stock))
	fmt.Println(stock)
}
Вывод · это печатает программа, набирать не нужно
[3 12]
[3 12 25 60]

Два самых пустых остатка найдены верно, а порядок ячеек у вызывающего потерян. Нужны оба — значит, сортируют копию:

Код для чтения · разбираем, набирать не нужно
package main

import (
	"fmt"
	"slices"
)

func main() {
	stock := []int{25, 3, 60, 12, 40}
	sorted := slices.Clone(stock)
	slices.Sort(sorted)
	fmt.Println("по возрастанию:", sorted)
	fmt.Println("по ячейкам:", stock)
	fmt.Println("есть ячейка с 12:", slices.Contains(stock, 12))
	fmt.Println("где 60:", slices.Index(stock, 60))
}
Вывод · это печатает программа, набирать не нужно
по возрастанию: [3 12 25 40 60]
по ячейкам: [25 3 60 12 40]
есть ячейка с 12: true
где 60: 2

Попробуйте сейчас: задание report — отчёт с сортировкой.

Цель: отчёт по остаткам: ниже порога — по возрастанию, наибольший остаток, все ячейки в порядке ввода.

1. Прочитайте условие — порог у вас свой:

▶ Выполните
cd ~/gocourse/day17/report
cat TASK.txt

2. Допишите main.go вместо последней строки. Честных путей два: отобрать остатки ниже порога в новый срез через append и отсортировать его; или сделать копию stock, отсортировать копию и отбирать из неё. Сортировать сам stock нельзя: последняя строка отчёта печатает его в порядке ввода.

3. Не забудьте пустой ввод: slices.Max на пустом срезе паникует.

4. Проверьте на примере из условия — готовая команда с вашими числами напечатана в конце TASK.txt, — потом на вводе с пустой строкой и на пустом вводе, и закоммитьте:

▶ Выполните
printf '5\n\n70\n1\n' | go run .
printf '' | go run .
gofmt -l .
git add .
git commit -m "report: отчёт с сортировкой"

Готово, когда: пример из TASK.txt печатается буква в букву, пустой ввод даёт Ячеек нет — пункт report. Проверка прогонит и скрытые вводы: в разном порядке, с повторами, ровно на пороге.


Что может пойти не так

Что видите Что это значит Что делать
slices.Sort(stock) (no value) used as value Sort ничего не возвращает, а его результат присваивают отдельной строкой: slices.Sort(stock)
panic: slices.Max: empty list максимум пустого среза не определён сначала if len(stock) == 0
invalid operation: a == b (slice can only be compared to nil) срезы оператором == не сравниваются slices.Equal(a, b)
undefined: slices пакет не подключён "slices" в import
«По ячейкам» печатается по возрастанию отсортирован сам stock сортировать отобранное или копию
порог попал в «ниже» <= вместо < «ниже» — строго меньше

Дальше: тест, который проверяет, что функция не трогает вход, — course next