Вложенные ветвления и выбор
Как засовывать одно ветвление внутрь другого и не утонуть в скобках, отступах и «висячих» иначе.
Вложенное ветвление — это условный оператор, который стоит внутри ветки да или нет другого условного оператора. На блок-схеме это «ромб внутри ромба».
В линейном алгоритме команды идут одна за другой. Но реальные задачи редко бывают линейными: чтобы поставить оценку, сначала проверяют, сдан ли экзамен вообще, и только потом — на сколько баллов. Чтобы решить квадратное уравнение, сперва смотрят, не равен ли нулю старший коэффициент, и лишь затем считают дискриминант. Каждый такой «вопрос после вопроса» — это вложенность.
Зачем это нужно
Одного ромба хватает, когда выбор бинарный: число положительное или нет, год високосный или нет. Но как только вариантов становится три и больше, или один вопрос имеет смысл только при определённом ответе на предыдущий, появляется вложенность. Классический школьный пример — перевод процента правильных ответов в оценку: 5 — это «85 и больше», 4 — «от 70 до 84», 3 — «от 50 до 69», иначе 2. Здесь четыре исхода, и одним ромбом их не разрулить.
Ромб внутри ромба
Представьте блок-схему словами. Внешний ромб спрашивает: балл >= 85?. Если да — ставим 5 и выходим. Если нет — попадаем в ветку нет, а там стоит ещё один ромб: балл >= 70?. И так далее. Каждый следующий вопрос живёт внутри ветки нет предыдущего. Текстом блок-схему удобно записать с отступами — отступ показывает уровень вложенности:
[начало]
|
V
<балл >= 85?> --да--> [оценка := 5] --> [конец]
| нет
V
<балл >= 70?> --да--> [оценка := 4] --> [конец]
| нет
V
<балл >= 50?> --да--> [оценка := 3] --> [конец]
| нет
V
[оценка := 2] --> [конец]
Такая «лесенка» из ромбов, спускающихся по ветке нет, называется каскадом условий (по-английски — цепочка if … else if … else). Она проверяет условия сверху вниз и срабатывает на первом истинном.
Перевод каскада в псевдокод
Та же логика в псевдокоде читается почти как блок-схема, только вместо стрелок — слова если / иначе если / иначе:
если балл >= 85 то
оценка := 5
иначе если балл >= 70 то
оценка := 4
иначе если балл >= 50 то
оценка := 3
иначе
оценка := 2
всё
Обратите внимание: во втором ромбе мы пишем просто балл >= 70, а не 70 <= балл < 85. Верхнюю границу проверять не нужно — если бы балл был 85 и больше, мы бы сюда не дошли: первый ромб увёл бы нас в ветку да. Это и есть главная экономия каскада.
Каскад условий против множественного выбора
Каскад хорош для диапазонов (балл попадает в интервал) и сложных условий. Но бывает другая ситуация: переменная сравнивается с набором конкретных значений — день недели 1, 2, 3 …, код команды, номер месяца. Тогда удобнее множественный выбор — конструкция switch / выбор / case.
Сравните два способа определить число дней в месяце по его номеру. Каскад:
если месяц = 2 то дней := 28
иначе если месяц = 4 то дней := 30
иначе если месяц = 6 то дней := 30
иначе дней := 31
И множественный выбор:
выбор по месяц
случай 2: дней := 28
случай 4, 6, 9, 11: дней := 30
иначе: дней := 31
всё
Второй вариант короче и нагляднее: видно, что мы перебираем именно номер месяца, а ветка случай 4, 6, 9, 11 объединяет четыре значения с одинаковым результатом. На блок-схеме множественный выбор рисуют не цепочкой ромбов, а одним прямоугольником-«переключателем» с несколькими выходами-стрелками по числу веток.
Когда что выбирать
| Признак задачи | Лучше подходит |
|---|---|
Проверяем диапазоны (x < 10, 10 .. 20) | каскад если … иначе если |
Сложные условия с и/или | каскад |
| Сравниваем одну переменную с точными значениями | множественный выбор выбор/case |
| Много веток, по значению можно «прыгнуть» сразу | множественный выбор |
Как это работает
Под капотом каскад из n условий — это просто n вложенных бинарных проверок: каждая ветка иначе ведёт к следующему сравнению. Поэтому в худшем случае исполнитель сделает все n сравнений (когда истинно последнее или ни одно). А вот множественный выбор транслятор часто превращает в таблицу переходов: по значению переменной он сразу вычисляет адрес нужной ветки, не перебирая остальные. Для школьных задач разница в скорости незаметна, но идея важна: каскад — это «спросить по очереди», выбор — это «сразу попасть куда нужно».
Проверим логику оценок настоящим кодом. Пробежимся по нескольким баллам и посмотрим, какие оценки выставит каскад:
def ocenka(ball):
if ball >= 85:
return 5
elif ball >= 70:
return 4
elif ball >= 50:
return 3
else:
return 2
for b in [92, 85, 84, 70, 50, 49, 0]:
print(b, "->", ocenka(b))
Вывод:
92 -> 5 85 -> 5 84 -> 4 70 -> 4 50 -> 3 49 -> 2 0 -> 2
Видно, что границы 85, 70 и 50 относятся к верхней оценке (знак >= включает само число), а 84 и 49 «проваливаются» в следующую ветку. Это любимое место экзаменационных ловушек: перепутали > и >= — и граничный балл уехал не в ту оценку.
Частые ошибки
- Дублирование границ в условиях. Писать во втором ромбе
70 <= балл < 85не ошибка, но лишняя работа: в каскаде верхняя граница уже отсечена. А вот если ветки идут не каскадом (каждыйеслиотдельный, безиначе), то проверять оба конца диапазона обязательно — иначе сработает сразу несколько веток. - Неверный порядок условий. Если поставить
балл >= 50первым, то пятёрочник тоже получит тройку — он попадёт в первую же истинную ветку. В каскаде условия идут от самого строгого к самому мягкому. - «Висячее» иначе. При ручной вложенности легко привязать
иначене к томуесли. Спасают отступы и явныйвсё(endif), закрывающий каждый блок. - Выбор там, где нужны диапазоны. Конструкция
caseсравнивает с точными значениями, а не с интервалами — для «баллов от 70 до 84» она не годится, нужен каскад.
Итоги
- Вложенное ветвление — ромб внутри ветки другого ромба; так строят выбор из трёх и более исходов.
- Каскад
если … иначе если … иначепроверяет условия сверху вниз и срабатывает на первом истинном; верхние границы диапазонов отсекаются автоматически. - Множественный выбор
выбор/caseкомпактнее, когда одна переменная сравнивается с конкретными значениями; на схеме это «переключатель» с несколькими выходами. - Диапазоны и сложные условия — каскад; точные значения — выбор. Следите за порядком условий и знаком
>=на границах.