Накопители и максимум по срезу
В дне 08 сумму и максимум считали на лету: прочитал число — добавил, сравнил, забыл. Со срезом данные остаются, и по ним можно пройти второй раз. Иногда без этого задачу не решить.
Сумма и максимум — те же накопители
Ситуация. Итог по стеллажу: сколько штук всего и в каких ячейках лежит больше всего.
package main
import "fmt"
func main() {
stock := []int{12, 40, 5, 40, 7}
sum := 0
most := stock[0]
for _, qty := range stock {
sum += qty
if qty > most {
most = qty
}
}
fmt.Println("Всего:", sum, "шт.")
fmt.Print("Больше всего: ", most, " шт., ячейки:")
for i, qty := range stock {
if qty == most {
fmt.Print(" ", i+1)
}
}
fmt.Println()
}Что увидите.
Всего: 104 шт.
Больше всего: 40 шт., ячейки: 2 4Как это читать.
| Кусок | Что делает |
|---|---|
sum := 0 и sum += qty |
накопитель суммы из дня 08, только числа берутся из среза |
most := stock[0] |
максимум начинается с первого элемента, а не с нуля: первый элемент — всегда настоящий кандидат. Флаг «первое число» из дня 08 больше не нужен |
| первый цикл | находит сумму и максимум |
| второй цикл | идёт по тому же срезу ещё раз и печатает номера всех ячеек с максимумом |
fmt.Print |
печать без перевода строки: номера дописываются в одну строку, а fmt.Println() в конце её закрывает |
Второй цикл — главное: пока ячейки читаются по одной, неизвестно, окажется ли 40 максимумом — дальше может прийти 60. Номера ячеек с максимумом называют после первого прохода, и для этого данные хранят в срезе.
Пустой срез и первый элемент
most := stock[0] берёт элемент, который обязан существовать. Если ввод пустой, срез пустой, и программа падает на первой же строке — вот три строки тела main, где это видно:
var stock []int
most := stock[0]
fmt.Println(most)stagiaire@lab:~/gocourse/day16/scratch$ go run .
panic: runtime error: index out of range [0] with length 0
goroutine 1 [running]:
main.main()
/home/stagiaire/gocourse/day16/scratch/main.go:7 +0x9
exit status 2Поэтому пустой случай проверяют до того, как обращаться к элементам:
if len(stock) == 0 {
fmt.Println("Ячеек нет")
return
}return в main — ранний выход из дня 11.
Попробуйте сейчас: максимум и его ячейки.
Цель: программа из раздела выше работает в черновике, и вы видите, как меняется вывод от данных.
1. Наберите программу из раздела «Сумма и максимум» в
scratch/main.goцеликом и запустите:✎ Наберите в файлscratch/main.gopackage main import "fmt" func main() { stock := []int{12, 40, 5, 40, 7} sum := 0 most := stock[0] for _, qty := range stock { sum += qty if qty > most { most = qty } } fmt.Println("Всего:", sum, "шт.") fmt.Print("Больше всего: ", most, " шт., ячейки:") for i, qty := range stock { if qty == most { fmt.Print(" ", i+1) } } fmt.Println() }▶ Выполнитеcd ~/gocourse/day16/scratch go run .2. Замените в срезе последний элемент 7 на 40 и запустите снова: номеров ячеек с максимумом должно стать три.
3. Замените срез на пустой —
stock := []int{}— и посмотрите на панику.4. Добавьте проверку длины сразу после объявления среза — файл целиком:
✎ Наберите в файлscratch/main.gopackage main import "fmt" func main() { stock := []int{} if len(stock) == 0 { fmt.Println("Ячеек нет") return } sum := 0 most := stock[0] for _, qty := range stock { sum += qty if qty > most { most = qty } } fmt.Println("Всего:", sum, "шт.") fmt.Print("Больше всего: ", most, " шт., ячейки:") for i, qty := range stock { if qty == most { fmt.Print(" ", i+1) } } fmt.Println() }▶ Выполнитеgo run .Готово, когда: на пустом срезе программа печатает
Ячеек нети не падает.
Отбор по условию — ещё один срез
Задание дня просит перечислить ячейки ниже порога — а если таких нет, написать слово. Удобно сначала собрать номера в отдельный срез, как в трассировке на прошлой странице, а потом решить, что печатать: по len нового среза сразу видно, пуст ли список.
low := []int{}
for i, qty := range stock {
if qty < limit {
low = append(low, i+1)
}
}Порог записывают один раз, константой из дня 02 — const limit = … над func main(), — а не числом в нескольких местах программы.
Попробуйте сейчас: задание stock.
Цель: программа
~/gocourse/day16/stock/main.goпечатает отчёт по ячейкам из ввода — порог у вас свой.1. Прочитайте условие и пример:
▶ Выполнитеcd ~/gocourse/day16/stock cat TASK.txt cat example.txt2. Напишите
main.goсами. Порядок работы: весь ввод в срез — программа со второй страницы; пустой ввод — проверкаlen; сумма и максимум — первый проход; ячейки с максимумом — второй; ячейки ниже порога — отбор в свой срез.3. Проверьте на примере, на пустом вводе и на своих числах — в том числе на ячейке ровно на пороге:
▶ Выполнитеgo run . < example.txt go run . < /dev/null gofmt -l .4. Закоммитьте:
▶ Выполнитеcd ~/gocourse/day16 git add . git commit -m "stock: отчёт по ячейкам"Готово, когда: вывод на
example.txtсовпадает с примером изTASK.txtбуква в букву — пунктыstockиstocklimit. Проверка прогонит программу на скрытых вводах, а в копии испортит срез: отчёт, посчитанный по срезу, изменится, мимо среза — нет.
Что может пойти не так
| Что видите | Что это значит | Что делать |
|---|---|---|
panic: runtime error: index out of range [0] with length 0 на пустом вводе |
обращение к stock[0] раньше проверки длины |
if len(…) == 0 — первым делом после чтения |
| ячейка ровно на пороге попала в список | в условии <= |
«ниже порога» — строго меньше: < |
| номера ячеек на единицу меньше, первая — 0 | печатается i, а не i+1 |
номер = индекс + 1 |
| напечатан только первый номер с максимумом | номера собираются в первом проходе, когда максимум ещё не окончательный | номера — вторым проходом, после того как максимум найден |
| проверка пишет: сумма и максимум мимо среза | отчёт считается на лету, в переменных | сумма и максимум — циклом по срезу |
| между словом и числом лишний или пропавший пробел | fmt.Print ставит пробел только между двумя нестроками |
сверьте пробелы с примером, пробел пишите внутри строки " " |
Дальше: срез в функции и тест, который сравнивает срезы — course next