Что лучше использовать для множественного ветвления
Перейти к содержимому

Что лучше использовать для множественного ветвления

  • автор:

 

Урок 6. Множественное ветвление

Логика выполняющейся программы может быть сложнее, чем выбор одной из двух ветвей. Например, в зависимости от значения той или иной переменной, может выполняться одна из трех (или более) ветвей программы.

Как организовать такое множественное ветвление? Наверное, можно использовать несколько инструкций if : сначала проверяется условное выражение в первой инструкции if (если оно возвращает истину, то будет выполняться вложенный в нее блок кода), затем во второй инструкции if и т.д. Однако при таком подходе проверка последующих инструкций будет продолжаться даже тогда, когда первое условие было истинным, и блок кода при данной ветке был выполнен. Проверка последующих условий может оказаться бессмысленной.

Обычно такую проблему можно решить с помощью вложенных конструкций if-else . Однако при этом часто появляется проблема правильной трактовки кода: непонятно, к какому if относится else (хотя в Python такая путаница не возможна из-за обязательных отступов).

С другой стороны, в ряде языков программирования, в том числе и Python, предусмотрено специальное расширение инструкции if , позволяющее направить поток выполнения программы по одной из множества ветвей. Данная расширенная инструкция, помимо необязательной части else , содержит ряд ветвей elif (сокращение от «else if» — «еще если») и выглядит примерно так, как показано на блок-схеме. Частей elif может быть сколь угодно много (в пределах разумного, конечно).

В отличии от использования множества одиночных инструкций if , инструкция if-elif- else прекращает просмотр последующих ветвей, как только логическое выражение в

Учебник ”Основы программирования на Python”

текущей ветке вернет true . Например, если выражение при if (первая ветка) будет истинным, то после выполнения вложенного блока выражений, программа вернется в основную ветку.

Примеры скриптов с использованием инструкции if-elif-else на языке

программирования Python: x = -10

if x > 0: print 1

elif x < 0: else: print -1

result = «no result» num1 = 3

if num1 == 0: result = 0

elif num1==1: result = 1

elif num1==2: result = 2

elif num1==3: result = 3

elif num1==4: result = 4

elif num1==5: else: result = 5

print «Error» print result

В какой момент прекратиться выполнение инструкции if-elif-else в примерах выше. При каком значении переменной могла сработать ветка else ?

Практическая работа

1. Напишите программу по следующему описанию:

a. двум переменным присваиваются числовые значения;

b. если значение первой переменной больше второй, то найти разницу значений переменных (вычесть из первой вторую), результат связать с третьей переменной;

c. если первая переменная имеет меньшее значение, чем вторая, то третью переменную связать с результатом суммы значений двух первых переменных;

d. во всех остальных случаях, присвоить третьей переменной значение

Учебник ”Основы программирования на Python”

e. вывести значение третьей переменной на экран.

2. Придумайте программу, в которой бы использовалась инструкция if-elif-else . Количество ветвей должно быть как минимум четыре.

Информатика: учебник для студентов всех направлений и специальностей подготовки

Главная страница / 24. Базовые алгоритмические структуры: 24.2. Разветвляющая алгор.

24.2. Разветвляющая алгоритмическая структура

Разветвляющая алгоритмическая структура представляет собой конструкцию, состоящую из двух или более ветвей. Наиболее простой ее вариант – бинарное ветвление (альтернатива, структура if-else, если-то-иначе ). Ее блок-схема представлена на рис. 24.3, а , а псевдокод – следующим текстом:

При ее выполнении сначала вычисляется логическое выражение. Если оно имеет значение Истина , то выполняется ветвь A, если же Ложь – то ветвь B. Каждая ветвь может включать в себя одно или несколько элементарных действий. Если в ветвь входит несколько действий (операторов), то их необходимо объединить в одно составное действие с помощью служебных слов нач и кон (см. пример алгоритма решения квадратного уравнения в гл. 23). На блок-схеме бинарное ветвление изображается в виде ромбовидного графического элемента альтернатива . Направления перехода могут помечаться также 1 или да (истина) и 0 или нет (ложь).

Частным случаем бинарного ветвления является обход, при котором в случае равенства логического выражения значению Ложь не должно выполняться никакого действия – ветвь пустая.

Блок-схема этой структуры приведена на рис. 24.3, б .

img243a img243b
a б

Рис. 24.3. Блок-схема структур «ветвление» ( а ) и «обход» ( б )

В качестве логического выражения может быть использовано выражение отношения (условие), в котором два выражения сравниваются знаками отношения, например, k = 0 или i < n или sin( x + π/2)> =exp(-2 y )-1. В более сложных случаях в логических выражениях используются знаки логических операций: инверсии not, дизъюнкции or или конъюнкции and. Например, not( k = 0 and (( i < n ) or (sin( x + π/2)> =exp(-2 y )-1))). При конструировании сложных логических выражений необходимо использовать правила и законы булевой алгебры.

Множественное ветвление представляет собой структуру, разветвляющуюся на более чем две ветви. С точки зрения теоретического программирования она является избыточной, так как может быть реализована с помощью бинарных ветвлений. Но практически все языки программирования имеют оператор, поддерживающий эту структуру, поэтому рассмотрим ее на примере ветвления на четыре ветви (блок-схема на рис. 24.4). Ветвлением управляет выражение-селектор s , которое может принимать предусмотренные значения a , b и c . Если s = a , то выполняется ветвь A, если s = b , то выполняется ветвь B, и если s = с , то выполняется ветвь С. В структуре также имеется ветвь X, которая будет выполняться, если селектор s примет не предусмотренное для исполнения предыдущих ветвей значение.

На рис. 24.5 показана реализация этой структуры с помощью бинарных ветвлений.

img244

Рис. 24.4. Блок-схема множественного ветвления

img245

Рис. 24.5. Реализация множественного ветвления с помощью бинарных ветвлений

Name already in use

playground-dhn48n96 / sprint-1.-bazovye-znaniya-i-osnovy-programmirovaniya / 2.-osnovy-python / potok-komand-control-flow.md

  • Go to file T
  • Go to line L
  • Copy path
  • Copy permalink
  • Open with Desktop
  • View raw
  • Copy raw contents Copy raw contents

Copy raw contents

Copy raw contents

Поток команд (Control Flow)

В программах, рассмотренных на настоящий момент, каждая из инструкций выполняется только однажды, причем по порядку. Большинство программ куда сложнее, поскольку последовательность инструкций и количество их выполнений могут изменяться.

Для описания изменения порядка инструкций в программе используется термин контроль потока (control flow). Почти во всех языках программирования есть инструкции, позволяющие изменять последовательность выполнения на основании логики и значения переменных.

Ход выполнения программы может быть линейным, то есть таким, когда выражения выполняются друг за другом, начиная с первого и заканчивая последним. Ни одна строка кода программы не пропускается.

Однако чаще в программах бывает не так. При выполнении кода, в зависимости от тех или иных условий, некоторые его участки могут быть опущены, в то время как другие — выполнены. Иными словами, в программе может присутствовать ветвление, которое реализуется условным оператором — особой конструкцией языка программирования.

Проведем аналогию с реальность. Человек живет по расписанию. Можно сказать, расписание — это алгоритм для человека, его программный код, подлежащий исполнению. В расписании на 18.00 стоит поход в бассейн. Однако, если воду из бассейна слили, разумно было бы отменить занятие по плаванию, то есть изменить ход выполнения программы-расписания. Одним из условий посещения бассейна должно быть его функционирование, иначе должны выполняться другие действия.

Подобная нелинейность действий может быть реализована в компьютерной программе. Например, часть кода будет выполняться лишь при определенном значении конкретной переменной. В языках программирования используется приблизительно такая конструкция условного оператора:

Перевести на человеческий язык можно так: если логическое выражение возвращает истину, то выполняются выражения внутри фигурных скобок; если логическое выражение возвращает ложь, то код внутри фигурных скобок не выполняется.

Конструкция if логическое_выражение называется заголовком условного оператора. Выражения внутри фигурных скобок — телом условного оператора. Тело может содержать как множество выражений, так и всего одно или даже может быть пустым.

Пример использования условного оператора в Python

В Python вместо фигурных скобок используется двоеточие. Обособление вложенного кода, то есть тела оператора, достигается за счет отступов. В программировании принято делать отступ равный четырем пробелам. Однако также можно использовать Tab на клавиатуре.

<% hint style="info" %>Python считается языком с ясным синтаксисом и легко читаемым кодом. Это достигается сведением к минимуму таких вспомогательных элементов как скобок и точек с запятой. Для разделения инструкций используется переход на новую строку, а для обозначения вложенных выражений — отступы от начала строки.

В других языках данный стиль программирования также используется, но лишь для удобочитаемости кода человеком. В Python же он возведен в ранг синтаксического правила.

В примере выше логическим выражением является n < 100. Если оно возвращает истину, то выполнится строчка код b = n + a. Если логическое выражение ложно, то выражение b = n + a не выполнится.

 

Приведем полную версию программы

Последняя строчка кода print(b) уже не относится к условному оператору, что обозначено отсутствием перед ней отступа. Она не является вложенной в условный оператор, значит, не принадлежит ему.

Поскольку переменная n равна 98, а это меньше 100, то b станет равной 148. Это значение будет выведено на экран. Если переменная n изначально была бы связана, например, со значением 101, то на экран был бы выведен 0. При n, равной 101, логическое выражение в заголовке условного оператора вернуло бы ложь. Значит, тело не было бы выполнено, и переменная b не изменилась бы.

Структуру программы можно изобразить следующим образом:

Основная ветка программы выполняется всегда, а вложенный код лишь тогда, когда в темно-зеленой строчке, обозначающей заголовок условного оператора, случается истина.

Для небольших программ иногда чертят так называемые блок-схемы (flowchart), отражающие алгоритм выполнения. В языке блок-схем определенные конструкции обозначаются своими фигурами. Так блок действий обозначается прямоугольником, а логическое выражение — ромбом. Для кода выше блок-схема может выглядеть так

Блок инструкций — часть кода, которая сгруппирована и воспринимается как единое целое. Блоки могут состоять из одного или нескольких инструкций, а также могут быть пустыми. В языках C, C++ и некоторых других языках, блоки выделяются фигурными скобками < >. В языке Pascal и некоторых других языках блоки выделяются словами «begin» и «end».

Вложенные инструкции объединяются в блоки по величине отступов. Отступ может быть любым, главное, чтобы в пределах одного вложенного блока отступ был одинаков. Для отступа принято использовать 4 пробела.

Вложенные инструкции в Python записываются в соответствии с одним и тем же шаблоном, когда основная инструкция завершается двоеточием, вслед за которым располагается вложенный блок кода, обычно с отступом под строкой основной инструкции.

Рассмотрим пример кода:

Python понимает, какие строки относятся к if на основе отступов. Выполнение блока if a > b заканчивается, когда встречается строка с тем же отступом, что и сама строка if a > b .

Часто в программе необходимо выполнить одно действие в том случае, если условие истинно, и другое действие, если оно ложно. С синтаксисом if-else это возможно. Блок if-else в целом похож на оператор if, но секция else определяет действие или набор действий, выполняемых при неудачной проверке.

Если условие в строке 3 истинно, то выполняется первый блок с функцией print(). Если же условие ложно, выполняется блок else в строке 6. Так как значение age меньше 18, условие оказывается ложным и выполняется код в блоке else:

Этот код срабатывает, потому что существует обе возможные ситуации: возраст либо достаточен для голосования, либо недостаточен. Структура if-else хорошо подходит для тех ситуаций, в которых Python всегда выполняет только одно из двух возможных действий. В подобных простых цепочках if-else всегда выполняется одно из двух возможных действий.

Условный оператор может включать не одну ветку, а две, реализуя тем самым полноценное ветвление.

В случае возврата логическим выражением False поток выполнения программы не возвращается сразу в основную ветку. На случай False существует другой вложенный код, отличный от случая True. Другими словами, встретившись с расширенной версией условного оператора, поток выполнения программы не вернется в основную ветку, не выполнив один из вложенных кодов.

В языках программирования разделение на две ветви достигается с помощью добавления блока else, получается так называемое if-else (если-иначе). Синтаксис выглядит примерно так:

Если условие при инструкции if оказывается ложным, то выполняется блок кода при инструкции else. Ситуация, при которой бы выполнились обе ветви, невозможна. Либо код, принадлежащий if, либо код, принадлежащий else. Никак иначе. В заголовке else никогда не бывает логического выражения.

Пример кода с веткой else на языке Python

Нестандартные логические выражения

Следует иметь ввиду, что логическое выражение при if может выглядеть «нестандартно», то есть не так просто, как a > b и тому подобное. Там может стоять просто одна переменная, число, слово True или False, а также сложное логическое выражение, когда два простых соединяются через логически И или ИЛИ

Если вместо знака вопроса будет стоять 0, то с логической точки зрения это False, значит выражение в if не будет выполнено. Если a будет связано с любым другим числом, то оно будет расцениваться как True, и тело условного оператора выполнится. Другой пример:

Здесь a уже связана с булевым значением. В данном случае это True. Отметим, что в выражении a = 5 > 0 присваивание выполняется после оператора сравнения, так что подвыражение 5 > 0 выполнится первым, после чего результат будет присвоен переменной a. На будущее, если вы сомневаетесь в последовательности выполнения операторов, используйте скобки, например так: a = (5 > 0)

Тут, чтобы вложенный код выполнился, a должно быть больше 0 и одновременно меньше b . Также в Python, в отличие от других языков программирования, позволительна такая сокращенная запись сложного логического выражения

Множественное ветвление: if-elif-else

Ранее мы рассматривали работу условного оператора if. С помощью его расширенной версии if-else можно реализовать две отдельные ветви выполнения. Однако алгоритм программы может предполагать выбор больше, чем из двух путей, например, из трех, четырех или даже пяти. В данном случае следует говорить о необходимости множественного ветвления.

Для таких ситуаций в Python предусмотрен синтаксис if-elif-else. Python выполняет только один блок в цепочке if-elif-else. Все условия проверяются по порядку до тех пор, пока одно из них не даст истинный результат. Далее выполняется код, следующий за этим условием, а все остальные проверки Python пропускает.

Во многих реальных ситуациях существует более двух возможных результатов. Представьте себе парк аттракционов, который взимает разную плату за вход для разных возрастных групп:

  • для посетителей младше 4 лет вход бесплатный;
  • для посетителей от 4 до 18 лет билет стоит 25 гривен;
  • для посетителей от 18 лет и старше билет стоит 40 гривен.

Как использовать команду if для определения платы за вход? Следующий код определяет, к какой возрастной группе относится посетитель, и выводит сообщение со стоимостью билета:

Условие if в строке 1 проверяет, что возраст посетителя меньше 4 лет. Если условие истинно, то программа выводит соответствующее сообщение и Python пропускает остальные проверки. Оператор elif в строке 3 в действительности является еще одной проверкой if, которая выполняется только в том случае, если предыдущая проверка закончилась неудачей. В этом месте цепочки известно, что возраст посетителя не меньше 4 лет, потом что первое условие было ложным. Если посетителю меньше 18 лет, программа выводит соответствующее сообщение и Python пропускает блок else. Если ложны оба условия — if и elif, то Python выполняет код в блоке else в строке 6.

В данном примере условие в строке 1 дает ложный результат, поэтому его блок не выполняется. Однако второе условие оказывается истинным (12 меньше 18), поэтому код будет выполнен.

При любом значении возраста больше 17 первые два условия ложны. В таких ситуациях блок else будет выполнен и цена билета составит 40 гривен.

Вместо того чтобы выводить сообщение с ценой билета в блоках if-elif-else, лучше использовать другое, более компактное решение: присвоить цену в цепочке if-elif-else, а затем добавить одну команду print после выполнения цепочки:

Строки 4, 6 и 8 присваивают значение price в зависимости от значения age, как и в предыдущем примере. После присваивания цены в цепочке if-elif-else функция print() использует это значение для вывода сообщения с ценой билета.

Этот пример выводит тот же результат, что и предыдущий, но цепочка if-elif-else имеет более четкую специализацию. Вместо того, чтобы определять цену и выводить сообщения, она просто определяет цену билета. Кроме повышения эффективности, у этого кода есть дополнительное преимущество: его легче модифицировать. Чтобы изменить текст выходного сообщения, достаточно будет отредактировать всего одну функцию print() вместо трех разных функций.

Множественные ветвления и шаблон «Правила»

Здравствуйте, уважаемые хабрачитатели. В этой статье я хотел бы поделиться знаниями об одном небольшом и простом, но полезном шаблоне, про который обычно не пишут в книжках (возможно, потому, что он является частным случаем шаблона «Команда»). Это шаблон «Правила» (Rules Pattern). Вероятно, для многих он будет очень знакомым, но кому-то будет интересно с ним познакомиться.

Суть вопроса

Знакомо? Итак, какие тут встречаются проблемы?

Проблема 1: Растущая цикломатическая сложность. Если говорить просто, то цикломатическая сложность — это глубина вложенности if-ов и циклов c учетом логических операторов. Инструменты анализа кода позволяют оценить этот параметр для всех участков кода. Считается, что параметр цикломатической сложности для отдельного участка кода не должен превышать 10. Из этой проблемы растет следующая.

Проблема 2: Добавление новой логики. С течением времени и добавлением новых условий становится сложно понять, куда именно добавлять новую логику и как.

Проблема 3: Дублирование кода. Если дерево условий разветвлено, то порой нельзя избавиться от ситуации, когда один и тот же код присутствует в нескольких ветках.

Тут и приходит на помощь шаблон «Правила». Его структура очень проста:

Здесь класс Evaluator содержит коллекцию реализаций интерфейса IRule. Evaluator выполняет правила и решает, какое правило надо использовать для получения результата. Чтобы понять, как это работает и выглядит в коде, рассмотрим небольшой пример на C#.

Пример. Игра в кости (наподобие «Тали»)

Правила игры:

Игрок кидает одновременно 5 кубиков, и в зависимости от их комбинации получает определенное количество очков.
Комбинации могут быть следующими:
1 X X X X — 100 очков
5 X X X X — 50 очков
1 1 1 X X — 1000 очков
2 2 2 X X — 200 очков
3 3 3 X X — 300 очков
4 4 4 X X — 400 очков
5 5 5 X X — 500 очков
6 6 6 X X — 600 очков

Примеры комбинаций:
[1,1,1,5,1] — 1150 очков
[2,3,4,6,2] — 0 очков
[3,4,5,3,3] — 350 очков

Конечно все они могут быть и другими, и их может быть гораздо больше, но об этом позже.

Делай раз! Без шаблонов.
Делай два! Добавление правил? Модульные тесты.

Вроде бы 50 строк кода это очень мало. Но что будет, если правила игры будут изменяться и добавляться?
Например, мы добавим правила для различных комбинаций кубиков:

1 1 1 1 X — 2000
1 1 1 1 1 — 4000
1 2 3 4 5 — 8000
2 3 4 5 6 — 8000
A A B B X — 4000
и так далее.

В этом случае код рискует превратиться в очень запутанный. Чтобы этого избежать, перепишем код с использованием шаблона «Правила».
(Здесь мне также стоило бы сказать о том, что до рефакторинга надо покрыть все случаи модульными тестами, побурчать об их важности и необходимости для рефакторинга кода)

Делай три! Применяем шаблон «Правила»

Ура! Задача решена! Теперь каждый класс занимается тем, что ему положено, цикломатическая сложность не растет,
а новые правила добавляются легко и просто. Выбор правила теперь осуществляется при помощи класса RuleSet , содержащего набор правил, а добавление правил и подсчет очков — классом Game .

О чем нужно помнить?

При проектировании программы, содержащей логику, основанную на правилах, полезно иметь ввиду следующие вопросы:
— Следует ли правилам быть read-only в отношении системы, чтобы не изменять ее состояние?
— Должны ли быть зависимости между правилами? Стоит ли уделить внимание порядку выполнения правил, в случае, когда одно правило может требовать результат работы другого правила для работы.
— Должны ли порядок выполнения правил быть строго определенным?
— Должны ли быть приоритеты в выполнении правил?
— Стоит ли позволять конечным пользователям редактировать правила?
и многие другие.

Пара слов о системах правил бизнес-логики (Business Rules Engines)

Концепция Business Rules Engines очень близка к идее шаблона «Правила» — это системы, которые позволяют
определять системы правил для бизнес-логики. Обычно они имеют некий графический интерфейс и позволяют пользователям определять правила и иерархии правил, которые могут храниться в базе данных или файловой системе. В частности, данный функционал имеет и Workflow Foundation от Microsoft.

Резюме

1) Используем шаблон «правила», когда надо избавиться от сложности условий и ветвлений
2) Помещаем логику каждого правила и его эффекты в свои классы
3) Отделяем выбор и обработку правил в отдельный класс — Evaluator
4) Знаем, что есть готовые «движковые» решения для бизнес-логики

 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *