Цикл с условием, break и continue
Число проходов не всегда известно заранее: «вывозить, пока на складе что-то есть». Для таких задач у for есть ещё две формы.
for с одним условием
Ситуация. На складе 50 штук, машина забирает 12. Сколько нужно рейсов?
stock := 50
trips := 0
for stock > 0 {
stock -= 12
trips++
}
fmt.Println("Рейсов:", trips)
fmt.Println("Остаток после вывоза:", stock)Что увидите.
stagiaire@lab:~/gocourse/day08/scratch$ go run .
Рейсов: 5
Остаток после вывоза: -10Как это читать. В заголовке только условие, и проверяется оно как вторая часть обычного for: истинно — тело, ложно — выход. Своей переменной у такого цикла нет: stock и trips объявлены до него и после живы. В других языках это while; в Go отдельного слова нет.
Остаток −10 значит, что пятая машина ушла недогруженной на 10 штук: тело выполняется целиком, а условие проверяется только перед следующим проходом. Было бы на складе 0 с начала — рейсов было бы 0. Счётчик в такой форме пишут руками: i := 0 до цикла, i++ последней строкой тела.
Попробуйте сейчас: рейсы при других остатках.
Цель: посчитать рейсы и остаток при
stock := 60иstock := 0заранее, потом проверить.1. Наберите программу про рейсы в
scratch/main.go:✎ Наберите в файлscratch/main.gopackage main import "fmt" func main() { stock := 50 trips := 0 for stock > 0 { stock -= 12 trips++ } fmt.Println("Рейсов:", trips) fmt.Println("Остаток после вывоза:", stock) }2. Посчитайте на бумаге, что будет при
stock := 60и приstock := 0, и проверьте — по разу с каждым значением:▶ Выполнитеcd ~/gocourse/day08/scratch go run .Готово, когда: оба запуска совпали с посчитанным.
Бесконечный цикл и Ctrl+C
Ситуация. В программе про рейсы забыли строку stock -= 12.
stock := 50
trips := 0
for stock > 0 {
trips++
fmt.Println("Рейс", trips, "остаток", stock)
}
fmt.Println("Рейсов:", trips)stagiaire@lab:~/gocourse/day08/scratch$ go run .
Рейс 1 остаток 50
Рейс 2 остаток 50
Рейс 3 остаток 50
Рейс 4 остаток 50
Рейс 5 остаток 50
Рейс 6 остаток 50
Рейс 132025 остаток 50
Рейс 132026 остаток 50
Рейс 132027 остаток 50
Рейс 132028 остато^C
signal: interruptЗдесь пропущено больше ста тысяч строк — столько успело напечататься за секунды (номер последнего рейса у вас будет свой). Условие stock > 0 зависит только от stock, а тело его не меняет: значит, истинно всегда. Останавливают такую программу Ctrl+C: ^C — след нажатия, signal: interrupt — сообщение go run о том, что её прервали. Правило: в теле цикла с условием должно меняться то, от чего оно зависит.
Бывает и нарочно бесконечный цикл — for без заголовка; он крутится, пока его не прервут изнутри (об этом ниже). Строка после него не выполнится никогда: компилятор это пропускает, а go vet находит.
for {
fmt.Println("Жду заказы")
}
fmt.Println("Смена окончена")stagiaire@lab:~/gocourse/day08/scratch$ go vet .
main.go:9:2: unreachable codeunreachable code — «недостижимый код»: до строки 9 не дойти.
Попробуйте сейчас: бесконечный цикл и go vet.
Цель: остановить зациклившуюся программу и увидеть
unreachable codeотgo vet.1. Наберите в
scratch/main.goпрограмму безstock -= 12:✎ Наберите в файлscratch/main.gopackage main import "fmt" func main() { stock := 50 trips := 0 for stock > 0 { trips++ fmt.Println("Рейс", trips, "остаток", stock) } fmt.Println("Рейсов:", trips) }Запустите и остановите
Ctrl+C.▶ Выполнитеcd ~/gocourse/day08/scratch go run .2. Замените файл на цикл «Жду заказы»:
✎ Наберите в файлscratch/main.gopackage main import "fmt" func main() { for { fmt.Println("Жду заказы") } fmt.Println("Смена окончена") }Выполните
go vet(запускать не нужно):▶ Выполнитеgo vet .Готово, когда: после
Ctrl+Cв терминалеsignal: interrupt, аgo vet .печатает строку сunreachable code— пунктt_vet.
break: выйти из цикла
Ситуация. На складе 100 штук, в день уходит 17. Нужен первый день, когда остаток станет меньше 30: в этот день заказывают поставку.
stock := 100
day := 0
for {
day++
stock -= 17
if stock < 30 {
break
}
}
fmt.Println("Заказать поставку на день", day)
fmt.Println("Остаток в этот день:", stock)stagiaire@lab:~/gocourse/day08/scratch$ go run .
Заказать поставку на день 5
Остаток в этот день: 15Как это читать. break сразу выходит из цикла: остаток тела не выполняется, следующих проходов нет, программа идёт за закрывающую }. Условие выхода здесь проверяется в середине тела, после того как день посчитан. Если один цикл стоит в теле другого (course extra 08), break выходит только из того, в котором написан.
continue: к следующему проходу
Ситуация. Заказы приходят по будням, по 40 в день; суббота и воскресенье — выходные.
orders := 0
for day := 1; day <= 7; day++ {
if day == 6 || day == 7 {
continue
}
orders += 40
fmt.Println("День", day, "заказов с начала недели", orders)
}
fmt.Println("За неделю:", orders)stagiaire@lab:~/gocourse/day08/scratch$ go run .
День 1 заказов с начала недели 40
День 2 заказов с начала недели 80
День 3 заказов с начала недели 120
День 4 заказов с начала недели 160
День 5 заказов с начала недели 200
За неделю: 200Как это читать. continue пропускает остаток тела и переходит к следующему проходу: в цикле с тремя частями — к day++, потом проверка; в цикле с одним условием — сразу к проверке. Поэтому если строка, меняющая условие, стоит после continue, цикл может стать бесконечным.
break |
continue |
|
|---|---|---|
| остаток тела | пропускается | пропускается |
| следующие проходы | не будет | будут |
| куда идёт программа | за цикл | к i++ и проверке (или сразу к проверке) |
Попробуйте сейчас: continue и break в одном цикле.
Цель: предсказать, что напечатает программа.
1. Сначала на бумаге: для каждого
i— сработал лиcontinue, сработал лиbreak, чему стала равнаsum.Код для чтения · разбираем, набирать не нужноsum := 0 for i := 1; i <= 10; i++ { if i%2 == 0 { continue } if sum > 10 { break } sum += i } fmt.Println(sum)2. Сдать число (вместо
ЧИСЛО— своё предсказание):▶ Выполните · выделенное замените своимcourse answer day08.q3 ЧИСЛО3. Потом проверьте запуском — программа целиком:
✎ Наберите в файлscratch/main.gopackage main import "fmt" func main() { sum := 0 for i := 1; i <= 10; i++ { if i%2 == 0 { continue } if sum > 10 { break } sum += i } fmt.Println(sum) }▶ Выполнитеcd ~/gocourse/day08/scratch go run .Готово, когда: пункт
q3вcourse check 08зелёный.
Что может пойти не так
Слово while компилятор принимает за имя переменной — отсюда сразу две строки сообщений (код и вывод в course extra 08).
| Что видите | Что это значит | Что делать |
|---|---|---|
unexpected name stock at end of statement и non-declaration statement outside function body |
написано while |
for stock > 0 { |
вывод не останавливается, потом ^Csignal: interrupt |
условие не меняется в теле | найти, где переменная из условия должна меняться |
unreachable code от go vet |
строка после for { без break не выполнится |
убрать её или добавить выход |
break is not in a loop, switch, or select, continue is not in a loop |
они стоят вне цикла | только внутри for |
Дальше: трассировка цикла — course next