Логические выражения и операторы
Логические выражения и логический тип данных
Часто в реальной жизни мы соглашаемся с каким-либо утверждением или отрицаем его. Например, если вам скажут, что сумма чисел 3 и 5 больше 7, вы согласитесь, скажете: “Да, это правда”. Если же кто-то будет утверждать, что сумма трех и пяти меньше семи, то вы расцените такое утверждение как ложное.
Подобные фразы предполагают только два возможных ответа – либо “да”, когда выражение оценивается как правда/истина, либо “нет”, когда утверждение оценивается как ошибочное/ложное. В программировании и математике если результатом вычисления выражения может быть лишь истина или ложь, то такое выражение называется логическим.
Например, выражение 4 > 5 является логическим, так как его результатом является либо правда, либо ложь. Выражение 4 + 5 не является логическим, так как результатом его выполнения является число.
На позапрошлом уроке мы познакомились с тремя типами данных – целыми и вещественными числами, а также строками. Сегодня введем четвертый – логический тип данных (тип bool
). Его также называют булевым. У этого типа всего два возможных значения: True (правда) и False (ложь).
>>> a = True >>> type(a) <class 'bool'> >>> b = False >>> type(b) <class 'bool'>
Здесь переменной a было присвоено значение True
, после чего с помощью встроенной в Python функции type()
проверен ее тип. Интерпретатор сообщил, что это переменная класса bool
. Понятия “класс” и “тип данных” в данном случае одно и то же. Переменная b также связана с булевым значением.
В программировании False
обычно приравнивают к нулю, а True
– к единице. Чтобы в этом убедиться, можно преобразовать булево значение к целочисленному типу:
>>> int(True) 1 >>> int(False) 0
Возможно и обратное. Можно преобразовать какое-либо значение к булевому типу:
>>> bool(3.4) True >>> bool(-150) True >>> bool(0) False >>> bool(' ') True >>> bool('') False
И здесь работает правило: всё, что не 0 и не пустота, является правдой.
Логические операторы
Говоря на естественном языке (например, русском) мы обозначаем сравнения словами “равно”, “больше”, “меньше”. В языках программирования используются специальные знаки, подобные тем, которые используются в математике: >
(больше), <
(меньше), >=
(больше или равно), <=
(меньше или равно), ==
(равно), !=
(не равно).
Не путайте операцию присваивания значения переменной, обозначаемую в языке Python одиночным знаком “равно”, и операцию сравнения (два знака “равно”). Присваивание и сравнение – разные операции.
>>> a = 10 >>> b = 5 >>> a + b > 14 True >>> a < 14 - b False >>> a <= b + 5 True >>> a != b True >>> a == b False >>> c = a == b >>> a, b, c (10, 5, False)
В данном примере выражение c = a == b
состоит из двух подвыражений. Сначала происходит сравнение (==
) переменных a и b. После этого результат логической операции присваивается переменной c. Выражение a, b, c
просто выводит значения переменных на экран.
Сложные логические выражения
Логические выражения типа kbyte >= 1023
являются простыми, так как в них выполняется только одна логическая операция. Однако, на практике нередко возникает необходимость в более сложных выражениях. Может понадобиться получить ответа “Да” или “Нет” в зависимости от результата выполнения двух простых выражений. Например, “на улице идет снег или дождь”, “переменная news больше 12 и меньше 20″.
В таких случаях используются специальные операторы, объединяющие два и более простых логических выражения. Широко используются два оператора – так называемые логические И (and) и ИЛИ (or).
Чтобы получить True
при использовании оператора and
, необходимо, чтобы результаты обоих простых выражений, которые связывает данный оператор, были истинными. Если хотя бы в одном случае результатом будет False
, то и все сложное выражение будет ложным.
Чтобы получить True
при использовании оператора or
, необходимо, чтобы результат хотя бы одного простого выражения, входящего в состав сложного, был истинным. В случае оператора or
сложное выражение становится ложным лишь тогда, когда ложны оба составляющие его простые выражения.
Допустим, переменной x было присвоено значение 8 (x = 8
), переменной y присвоили 13 (y = 13
). Логическое выражение y < 15 and x > 8
будет выполняться следующим образом. Сначала выполнится выражение y < 15
. Его результатом будет True
. Затем выполнится выражение x > 8
. Его результатом будет False
. Далее выражение сведется к True and False
, что вернет False
.
>>> x = 8 >>> y = 13 >>> y < 15 and x > 8 False
Если бы мы записали выражение так: x > 8 and y < 15
, то оно также вернуло бы False
. Однако сравнение y < 15
не выполнялось бы интерпретатором, так как его незачем выполнять. Ведь первое простое логическое выражение (x > 8
) уже вернуло ложь, которая, в случае оператора and
, превращает все выражение в ложь.
В случае с оператором or
второе простое выражение проверяется, если первое вернуло ложь, и не проверяется, если уже первое вернуло истину. Так как для истинности всего выражения достаточно единственного True
, неважно по какую сторону от or
оно стоит.
В языке Python есть еще унарный логический оператор not
, то есть отрицание. Он превращает правду в ложь, а ложь в правду. Унарный он потому, что применяется к одному выражению, стоящему после него, а не справа и слева от него как в случае бинарных and
и or
.
Здесь у < 15
возвращает True
. Отрицая это, мы получаем False
.
>>> a = 5 >>> b = 0 >>> not a False >>> not b True
Число 5 трактуется как истина, отрицание истины дает ложь. Ноль приравнивается к False
. Отрицание False
дает True
.
Практическая работа
-
Присвойте двум переменным любые числовые значения.
-
Используя переменные из п. 1, с помощью оператора
and
составьте два сложных логических выражения, одно из которых дает истину, другое – ложь. -
Аналогично выполните п. 2, но уже с оператором
or
. -
Попробуйте использовать в логических выражениях переменные строкового типа. Объясните результат.
-
Напишите программу, которая запрашивала бы у пользователя два числа и выводила бы
True
илиFalse
в зависимости от того, больше первое число второго или нет.
Примеры решения и дополнительные уроки в pdf-версии
курса
Логические выражения в C++ — урок 5
Всем привет! Сейчас пойдет о логических операторах в C++. Поскольку нам постоянно надо что-то сравнивать (например что больше, а что меньше), то нам надо иметь возможность реализовывать подобный функционал и в C++.
Логические переменные
Для того, чтобы мы могли хранить данные логического типа, нам надо знать о логических переменных. Мы уже встречались и ими в нашем прошлом уроке. Но давайте повторим:
- Логические данные хранятся в переменных типа bool.
- Хранить они могут только два значения:
- «Верно» — это
true
; - «Лож» — это
false
;
- «Верно» — это
Теперь давайте узнаем какие логические операторы существуют в C++.
Операторы сравнения
Язык C++ имеет 5 различных операторов сравнения в своем арсенале. Также существуют такие операторы, которые являются комбинациями других. Все они вам должны быть знакомы из курса математики, поэтому их изучение не должно вызвать у вас проблем.
Давайте разберем по порядку каждый из них:
- A < B — сравнивает две переменные и возвращает true, если A меньше B.
- A > B — возвращает true, если A строго больше B.
- A == B — проверяет на равенство переменные A и B.
- A != B — проверяет переменные A и B на неравенство.
- A >= B — нестрогое неравенство. Возвращает true, если A больше или равно B.
- A <= B — противно неравенству A > B.
Теперь давайте разберем пару примеров, тем самым подкрепим теорию практикой:
bool r; int a = 5, b = 7; // создали переменные с которыми будем работать r = a > b; // r содержит false, поскольку 5 < 7 r = a <= b; // r содержит true r = a <= 5 // r равен true r = b == 9 // r содержит false, поскольку 7 != 9 |
Из примера видно, что в качестве A и B мы можем использовать не только переменные, но и простые числа.
Мы немного поэкспериментировали с операторами сравнения, однако пока не можем сгруппировать несколько из них и следственно создать серьезное логическое выражение. Для этих целей мы и будем применять логические операторы.
Логические операторы
Для комбинации сразу нескольких логических выражений мы должны использовать один или набор логических операторов.
Давайте рассмотрим следующий список:
- A && B — эквивалент «И». Соответственно возвращает true, если A и B являются истиной.
- A || B — эквивалент логического «ИЛИ». Вернет true ели хотя бы одно из выражений является истинным.
- A xor B — этот оператор можно сравнить с «ТОЛЬКО ОДИН», соответственно вернет true если A == true и B == false, или наоборот.
- !A — данный оператор инвертирует значение A. То есть, если A == true, то он вернет false и наоборот.
Здесь самая главная «причуда» логических операторов — это их обозначения в C++. В остальном они интуитивно понятны.
Теперь давайте попробуем на примере скомбинировать несколько логических выражений и вывести их значения на экран. Заранее расскажу про следующую строку:
cout.setf(ios::boolalpha); |
она отвечает за форматный вывод bool
переменных (вывод слов вместо чисел). Дело в том, что по умолчанию C++ при выводе логических значений используются два значения:
- 1 для true;
- 0 для false;
Таким образом мы «приукрасим» вывод нашей программы и сделаем его более читабельным.
Вот наша программа:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <iostream> using namespace std; int main() { cout.setf(ios::boolalpha); bool r; // создаем переменную bool типа int a = 10, b = 7; // а также две переменные типа int r = (a < b) && (b == 7); // r равно false, поскольку a > b cout << “r = “ << r << endl; // вывод результата r = a < b || b == 7; // r равен true cout << “r = “ << r << endl; // вывод результата r = (a < b) xor (b == 7); // r равен true, поскольку только b == 7 верно cout << “r = “ << r << endl; // вывод результата r = !(a == 10 && (b <= 8 || true)); // комбинируем целую кучу операторов cout << “r = “ << r << endl; // и снова выводим результат return 0; } |
Как видите мы можем пользоваться скобками, чтобы указать порядок выполнения логических операций также, как и при арифметических операциях. Также скобки можно и вовсе опустить, но это может создать путаницу в программе (именно поэтому я не рекомендую так делать).
Давайте посмотрим, что же выводит наша программа:
r = false
r = true
r = true
r = false
Process returned 0 (0x0) execution time : 0.020 s
Press any key to continue.
Из судя по выводу программы вы можете проверить сами себя, если предварительно вручную на листочке или в уме вычислите ответ.
Теперь я вам рекомендую пройти тест по пройденной теме.
Тест на тему «Логические выражения»
Пожалуйста, подождите пока страница загрузится полностью.
Если эта надпись не исчезает долгое время, попробуйте обновить страницу. Этот тест использует javascript. Пожалуйста, влкючите javascript в вашем браузере.
If loading fails, click here to try again
Если ты прочитал весь материал описанный выше, то попробуй пройти тест, который покажет твой уровень знания данного материала.
А на это я с вами прощаюсь. До скорых встреч!
Вопросы:
· Разветвляющиеся
алгоритмы.
· Логический
тип данных.
· Инструкция
ветвления.
Линейный
алгоритм представляет собой простую и предсказуемую последовательность
действий, которые не зависят ни от каких факторов. Однако в повседневной жизни
мы не всегда можем однозначно предсказать последовательность действий
чего-либо, потому как они зависят от некоторых условий.
Например,
вы решили пойти на улицу и думаете, что надеть. Ваш выбор одежды будет зависеть
от того, какая погода на улице. Можно привести и массу других примеров.
Когда
выполняемые действия зависят от некоторых условий, для их записи используются
разветвляющиеся алгоритмы. Разветвляющимися называются алгоритмы,
содержащие ветвления. Что же такое ветвление? Ветвление – это
алгоритмическая конструкция, в которой, в зависимости от некоторого условия,
происходит исполнение одной из двух последовательностей действий, которые
называются ветвями. Рассмотрим блок-схему ветвления. В верхнем блоке
записывается условие. Если условие выполняется, то выполняется одна
последовательность команд, в противном случае – другая.
Рассмотрим
условие ветвления. В качестве условий в ветвлениях используются логические
высказывания. Логическим высказыванием называется утверждение, которое
однозначно можно определить как истинное или ложное. Рассмотрим пример.
Утверждения о том, что снег белый, в Африке всегда холодно, а человек – хищник,
являются логическими высказываниями, так как их правдивость можно легко
проверить. А вот вопрос о том, какая сейчас погода, или предложение проверить, не
идёт ли на улице дождь, не являются логическими высказываниями, так как вопросы
и предложения не являются утверждениями.
Информация
об истинности логических высказываний может храниться в логических
переменных. Очевидно, что такие
переменные могут принимать одно из двух значений: «Истина» или «Ложь». Эти
значения могут быть заменены символами двоичного алфавита. Как правило,
«Истина» заменяется единицей, а «Ложь» – нулём. Поэтому считается, что «Истина»
всегда больше, чем «Ложь». В языке Python логические переменные принадлежат к
логическому типу данных, который называется bool
— в честь основоположника алгебры и логики Джорджа Буля. Логические переменные
в языке Python также могут иметь всего два значения: True,
что в переводе на русский язык означает «истина» и False,
что означает «ложь». При программировании переменной логического типа можно
присвоить один из двух указанных литералов либо результат истинности
логического высказывания.
Логическим
утверждением в программировании чаще всего является результат одной из операций
сравнения. Приведём пример. Утверждение «3 > 2» однозначно истинно, в
отличие от «6 = 7». Всего есть шесть операций сравнения: больше, меньше, равно,
не равно, больше либо равно и меньше либо равно. В языке Python они
записываются знаками >, <, ==, !=, >=, <= соответственно. Все
операции сравнения бинарные, то есть могут сравнивать между собой лишь два
операнда. Они работают таким образом: сначала вычисляются значения обоих
операндов, после чего определяется истинность записанного логического
высказывания. Таким образом, операции сравнения имеют самый низкий приоритет из
всех.
В
интерактивном режиме среды разработки языка Python создадим несколько
логических переменных и проверим работу некоторых операций сравнения. Сначала создадим
логическую переменную a
и присвоим ей значение True.
Логические литералы в языке Python всегда записываются с большой буквы. Подтвердим
принадлежность переменной a
к типу bool, вызвав функцию type
(a).
Переменная a действительно
принадлежит типу bool.
Создадим логическую переменную b
и присвоим ей результат истинности логического высказывания «3 + 5 < 8». Очевидно,
что после этого присваивания переменная b
будет иметь значение False,
так как 3 + 5 = 8. Теперь запишем логическое высказывание «a
> b». Как видим, результатом
этого логического выражения является True,
так как переменная a
имеет значение True,
а b – False.
При этом всегда True
> False.
Логические
значения «истина» и «ложь» могут быть неявно преобразованы к числовым значениям
единица и ноль соответственно. В доказательство запишем выражение «False
+
3». Как видим, результат этого выражения – 3. Запишем выражение: «True
+
2». Результат этого выражения – 3.
При
использовании операций сравнения важно помнить о соответствии типов операндов.
Так можно сравнить между собой две символьные строки или два числа, но при
попытке сравнить между собой символьную строку и число программа вернёт
сообщение об ошибке. Однако, так как логические выражения могут быть неявно
преобразованы к числовым, то их можно сравнивать с числами.
Решим
задачу. Написать модуль для определения истинности утверждения о том, что
введённое число является положительным.
Начнём
написание модуля. Сначала с помощью инструкции print
выведем на экран сообщение о том, что это программа, определяющая истинность утверждения
о том, что введённое число –положительное и запрос на ввод числа. Дальше
считаем число, введённое пользователем в переменную a.
Так как в условии задачи не сказано, что число целое, то при считывании будем
преобразовывать значение в вещественный тип float.
Дальше с помощью инструкции print
выведем на экран истинность утверждения «a
> 0».
print (‘Программа,
определяющая истинность утверждения о том, что введённое число – положительное.
Введите число.’)
a = float (input
())
print (a > 0)
Сохраним
написанный модуль и запустим его на выполнение. Введём число 3. Это число
положительное, поэтому программа вывела значение True,
то есть «Истина». Снова запустим модуль на выполнение и введём число -1.
Программа вывела значение False,
так как введённое число отрицательное. Программа работает правильно. Задача
решена.
Рассмотрим
алгоритмы, в которых последовательность действий изменяется в зависимости от
входных данных. Для записи таких алгоритмов в языке Python используется
инструкция ветвления. Эта инструкция является составной, то есть она содержит в
себе другие инструкции. Рассмотрим её запись. Она начинается со служебного
слова if, после которого, через
пробел, следует условие ветвления. Условием ветвления является выражение
логического типа. Дальше следует двоеточие, после которого, начиная со
следующей строки с отступом, записываются инструкции, которые будут выполняться
в том случае, если условие ветвления будет возвращать значение «истина». Далее
на одном уровне со словом if
следует служебное слово else,
после которого записывается двоеточие. Со следующей строки с отступом
записываются инструкции, которые будут выполняться в случае, если условие
ветвления вернёт значение «ложь». Служебные слова if
и else переводятся на
русский язык, как «если» и «иначе» соответственно. В языке Python, при записи
инструкции ветвления, важно соблюдать отступы, иначе программа не будет
работать.
Приведём
пример инструкции ветвления. Предположим, что нам нужно присвоить переменной M
наибольшее из значений, содержащихся в переменных a
и b,
или любое из значений, если они равны. Для того, чтобы это сделать, запишем
инструкцию ветвления с условием: a
> b.
Если это условие будет выполняться, то мы присвоим переменной M
значение
переменной a. Если же это условие не будет
выполняться, то мы присвоим переменной M
значение переменной b.
if a > b:
M = a
else:
M = b
Приведённая
запись инструкции ветвления называется её полной формой, но описанную
задачу можно решить иначе. Для этого можно до инструкции ветвления присвоить
переменной M значение переменной b.
Тогда нам не нужна часть инструкции ветвления, начиная со служебного слова else.
Её можно не записывать. Тогда в случае, если условие ветвления выполняется,
будет выполнена инструкция, которая следует после условия, в противном случае
выполнение инструкции ветвления будет завершено. Такая запись инструкции
ветвления называется её сокращённой формой.
M = b
if a
> b:
M = a
Решим
задачу с использованием ветвлений. Треугольник на координатной плоскости задан
целочисленными координатами своих вершин, не лежащих на одной прямой.
Определить, является ли заданный треугольник прямоугольным.
Для
решения задачи мы вычислим расстояния между вершинами треугольника. Таким
образом, мы определим длины его сторон. Согласно теореме Пифагора, в
прямоугольном треугольнике сумма квадратов длин двух наименьших сторон равна
квадрату длины наибольшей стороны. Обратим внимание, что для проверки того,
является ли треугольник прямоугольным, нам необязательно знать длины его
сторон. Достаточно знать их квадраты. Таким образом в начале мы можем вычислять
не расстояния между вершинами треугольника, а их квадраты. Это позволит избежать
лишних вычислений.
Напишем
модуль для решения задачи. С помощью инструкции print
выведем на экран сообщение о том, что это программа, определяющая является ли
треугольник прямоугольным. При помощи ещё одной инструкции print
выведем
на экран запрос на ввод координат первой вершины треугольника. Дальше запишем
инструкцию считывания значений переменных x1
и y1.
Так как по условию задачи координаты вершин целочисленные, то при считывании мы
будем преобразовывать их значения в целочисленный тип int.
Теперь дважды скопируем последние две инструкции и изменим их для ввода
координат второй и третьей вершин треугольника соответственно. После того, как
мы считали координаты вершин треугольника, определим квадраты длин его сторон.
Для этого присвоим переменным kv_a,
kv_b
и kv_c
соответственно квадраты расстояний между вершинами 1 и 2, 2 и 3, а также 3 и 1.
Для того, чтобы определить является ли треугольник прямоугольным, нам нужно
определить, какая из его сторон является наибольшей. Условимся, что наибольшей
будет сторона c. Чтобы наибольшей
стала сторона c, запишем две
инструкции ветвления. В первой инструкции, если значение переменной kv_a
больше значения kv_c,
то мы поменяем их местами. Во втором ветвлении, если kv_b
больше
kv_c,
то мы поменяем их значения местами. Теперь запишем инструкцию ветвления,
определяющую, является ли треугольник прямоугольным. Его условием будет то, что
сумма значений kv_a
и kv_b
равна значению kv_c.
Если это так, то с помощью инструкции print
выведем на экран сообщение о том, что заданный треугольник является
прямоугольным. В противном случае мы с помощью инструкции print
выведем на экран сообщение о том, что заданный треугольник не является
прямоугольным.
print (‘Программа,
определяющая, является ли треугольник прямоугольным.’)
print (‘Введите
координаты первой вершины треугольника.’)
x1,
y1 = int (input
()), int (input
())
print (‘Введите
координаты второй вершины треугольника.’)
x2,
y2 = int (input
()), int (input
())
print (‘Введите
координаты третьей вершины треугольника.’)
x3,
y3 = int (input
()), int (input
())
kv_a,
kv_b, kv_c = (x2 – x1) ** 2 + (y2 – y1) ** 2, (x3 – x2) ** 2 + (y3 – y2) ** 2,
(x1 – x3) ** 2 + (y1 – y3) ** 2
if kv_a > kv_c:
kv_a, kv_c = kv_c, kv_a
if kv_b > kv_c:
kv_b, kv_c = kv_c, kv_b
if kv_a + kv_b == kv_c:
print (‘Заданный
треугольник является прямоугольным.’)
else:
print (‘Заданный
треугольник не является прямоугольным.’)
Сохраним
описанный модуль и запустим его на выполнение. Зададим вершины треугольника в
точках: (1; 7), (7; 7) и (1; 1). Как видно на рисунке, данный треугольник
является прямоугольным. Программа вывела на экран сообщение об этом. Снова
запустим модуль на выполнение и зададим вершины треугольника в точках: (1; 7),
(7; 4) и (13; 1). Очевидно, что заданный треугольник не является прямоугольным.
Программа вывела сообщение об этом. Программа работает правильно. Задача
решена.
Мы
узнали:
·
Разветвляющимися называются
алгоритмы, которые содержат конструкции ветвления.
·
Ветвление – это алгоритмическая
конструкция, в которой, в зависимости от некоторого условия, происходит
исполнение одной из двух последовательностей действий, которые называются
ветвями.
·
Условиями
ветвлений являются логические высказывания – утверждения, истинность
которых можно определить однозначно.
·
Для
хранения данных об истинности логических высказываний в языке Python
используются переменные логического типа bool.
Они могут принимать одно из двух значений: True
или False.
·
Для
записи ветвлений в языке Python используется соответствующая инструкция, у
которой есть полная и сокращённая формы.
Опубликовано 24.11.2020 08:51
Самое время вспомнить школьную программу по математике! В программировании всё построено на арифметических и логических задачах. Чтобы их решить, необходимо соблюдать порядок математических вычислений и применять соответствующие операторы.
Арифметические выражения
К таким выражениям относятся вычислительные операции, которые учат в начальной школе на уроках математики:
— сложение;
— вычитание;
— умножение;
— деление.
Разберём примеры в языке JavaScript
В арифметических выражениях допускается использовать переменные с числовыми значениями.
При присвоении выражения переменной в неё записывается не само выражение, а его результат.
Создадим переменную a со значением 4 и переменную b со значением 2 и выполним простые арифметические выражения:
let a = 4;
let b = 2;
Сложение:
let c = a + b;
console.log(c);
Результат:
6
Вычитание:
let d = a - b;
console.log(d);
Результат:
2
Умножение:
let e = a * b;
console.log(e);
Результат:
8
Деление:
let f = a/b;
console.log(f);
Результат:
2
При необходимости выполнить сразу несколько операций в одной строке важно соблюдать правило приоритетности:
Умножение и деление имеют более высокий приоритет, чем сложение и вычитание.
Если для получения нужного результата нам необходимо сначала выполнить сложение или вычитание, а затем умножение или деление, то первую комбинацию мы заключаем в скобки:
let g = (a + b)*8;
console.log(g);
Результат:
48
Без скобок компьютер сначала бы провёл операцию умножения b*8, а затем прибавил бы к этому a. Мы бы получили результат 20, а не 48.
Ещё один пример. Представим, что у нас есть:
— печенье – 8 шт.;
— яблоки – 4 шт.
1. Создадим и инициализируем две переменные:
let cookies = 8;
let apples = 4;
2. Создадим переменную food, в которой запишем сумму cookies и apples:
let food = cookies + apples;
3. Выведем значение переменной food в консоль:
console.log(food);
Результат:
12
Теперь в переменной food хранится значение суммы переменных «12», а не выражение «cookies + apples».
Логические выражения
Логические выражения подразумевают, что результатом вычисления являются два значения:
— истина (true);
— ложь (false).
Такие выражения часто используются в операторах сравнения. JavaScript и многие другие языки программирования содержат набор встроенных операторов сравнения, с помощью которых строятся простые логические выражения:
— равно (==);
— не равно (!=);
— меньше чем (<);
— больше чем (>);
— меньше либо равно (<=);
— больше либо равно (>=).
Посмотрим, как работать с логическими выражениями в JavaScript.
Сравним между собой два числа с помощью оператора сравнения ==:
console.log(3==3);
console.log(3==2);
Увидим результат:
true
false
Справка! Для сравнения применяется именно удвоенный знак равенства. Об этом мы рассказывали в Уроке 2.
Первое выражение равно true, так как три действительно равно трём. А второе выражение имеет значение false, так как утверждение «три равно два» ложное.
В логических выражениях также используются переменные. Например, создадим переменную age со значением 7:
let age = 7;
Проведём несколько логических выражений:
console.log(age==7);
console.log(age>11);
console.log(age<18);
Компьютер возьмёт значение переменной age и подставит во все выражения вот так:
console.log(7==7);
console.log(7>11);
console.log(7<18);
И выдаст результат:
true
false
true
Чтобы произвести более серьезную проверку, можно объединить несколько операций сравнения. Это происходит с помощью двух операторов:
— «И», который обозначается, как &&
— «ИЛИ», который обозначается, как ||
Логические выражения, которые содержат несколько операций сравнения, называют сложными выражениями.
Пример
У нас есть аттракцион «Американские горки», на который допускаются люди, достигшие определённого возраста и не ниже определённого роста.
1. Представим мальчика, который решил покататься на горках. Ему 14 лет, рост – 150 см. Создадим переменные для хранения возраста в годах и роста в сантиметрах:
let age = 14;
let height = 150;
2. Запишем такое условие: «Человек должен быть старше 10 лет и выше 120 сантиметров». Оно выглядит так:
console.log((age > 10) && (height > 120));
Для текущих значений переменных age и height это условие выполняется, поэтому результат сложного логического выражения равен true:
true
3. Следующее выражение соответствует такому условию: «Человек должен быть старше 10 лет и младше 18 лет»:
console.log((age>10)&&(age<18));
Результат также true:
true
4. Теперь сложное логическое выражение с оператором «ИЛИ» – «Человек должен быть старше 10 лет или выше 160 сантиметров»:
console.log((age>10)||(height > 160));
Результат также равен true, поскольку выполняется одно из условий:
true
5. Если написать такое выражение с оператором «ИЛИ»: «Человек должен быть старше 15 лет или выше 160 сантиметров»:
console.log((age>15)||(height > 160));
То результат будет отрицательным, так как оба условия не выполнены:
false
Домашнее задание
Напишите сложную операцию сравнения с учётом вашего возраста и роста, чтобы узнать, сможете ли вы управлять вертолётом. Исходные условия:
— возраст – старше 18 лет;
— рост – не выше 190 см.
Логические выражения и ветвление
Для работы с целыми числами в C/C++ существует тип
переменных int
. Но
операции над целыми числами — не единственный инструмент в арсенале
разработчика. Одними из важнейших элементов в программировании являются
логические операции, выражения и типы данных.
В то время как переменные типа int
могут хранить произвольные целые числа,
а операции над ними подчиняются законам целочисленной арифметики; логические
переменные могут хранить лишь одно из двух значений: истину или ложь,
а операции над ними подчиняются законам алгебры логики.
Алгебра логики — умное название для интуитивно понятных вещей: операций «и»,
«или», «не» над высказываниями, которые либо справедливы, либо нет. Например,
рассмотрим такое логическое выражение:
«в коридоре темно
и
по коридору идёт человек
». Оно состоит из
двух логических значений (утверждения про темноту и человека) и одного
оператора (союз «и»). Его итоговым значением также будет логическое значение,
которое можно использовать, скажем, как сигнал для включения света в коридоре
гостиницы.
В программировании переменные, которые хранят логические значения называются
булевыми переменными, а операторы, которые производят над ними действия
называют булевыми операторами или просто логическими операторами.
Логические операции в C++
В C++ для булевых переменных существует тип bool
. Для обозначения истины в
C++ используется слово true
, а для обозначения лжи — слово false
.
Таким образом, если для объявления целочисленных переменных мы используем:
int myValue = 42;
Для логических переменных, используется:
bool tooDark = false; bool humanDetected = true;
Поскольку C++ для Arduino — это некоторая надстройка над голым C++, для
обозначения логического типа наряду с bool
существует ещё слово
boolean
. Это абсолютные синонимы. Можете использовать и то и другое.
Просто из соображений единого стиля и повышения читаемости кода выберите один
из терминов и используйте его всюду. Мы в статье будем использовать bool
,
просто потому что в нём меньше букв и он входит в стандарт языка.
Применение на практике: экологичный отель
Давайте разовьём тему с энергосберегающим освещением и сделаем на Arduino
устройство, которое включает свет в коридоре только тогда, когда это
действительно необходимо.
Допустим, к Arduino подключён аналоговый датчик
уровня освещённости, пироэлектрический цифровой датчик
движения тёплых объектов и экологичная светодиодная лампа.
Тогда скетч будет выглядеть следующим образом:
#define LIGHT_SENSOR_PIN A0 #define MOTION_SENSOR_PIN 2 #define LED_LAMP_PIN 5 #define LIGHT_LEVEL_THRESHOLD 600 void setup() { pinMode(LIGHT_SENSOR_PIN, INPUT); pinMode(MOTION_SENSOR_PIN, INPUT); pinMode(LED_LAMP_PIN, OUTPUT); } void loop() { int lightLevel = analogRead(LIGHT_SENSOR_PIN); bool motionDetected = digitalRead(MOTION_SENSOR_PIN); bool tooDark = lightLevel < LIGHT_LEVEL_THRESHOLD; bool lightningRequired = tooDark && motionDetected; digitalWrite(LED_LAMP_PIN, lightningRequired ? HIGH : LOW); }
Взглянем на loop
и поймём что здесь происходит. С первой строкой всё
понятно: мы считываем значение освещённости с аналогового сенсора с помощью
встроенной функции analogRead
и присваиваем его переменной с именем
lightLevel
.
Далее мы объявляем логическую переменную motionDetected
в качестве
значения которой присваиваем результат вызова встроенной функции
digitalRead
. Функция digitalRead
похожа по своей сути на
analogRead
, но может
возвращать
лишь одно из двух значений: либо истину
(true
), либо ложь (false
). То есть это функция, которая возвращает
логическое значение. Она подходит для считывания показаний разнообразных
бинарных цифровых датчиков. В нашем примере мы как раз использовали такой:
пироэлектрический сенсор, который выдаёт 0 вольт, пока движения в его радиусе
видимости нет и 5 вольт, когда замечено перемещение тёплого объекта: человека,
кошки или кого-то ещё. Нулю вольт микроконтроллер ставит в соответствие
значение false
, а пяти вольтам — true
.
Итак, по итогам исполнения второй строки, в переменной motionDetected
будет храниться либо истина, либо ложь в зависимости от того замечено ли
движение в коридоре.
Далее мы видим определение булевой переменной tooDark
, которой в качестве
значения присваивается значение выражения lightLevel < LIGHT_LEVEL_THRESHOLD
.
Символ <
, как можно догадаться, в C++ означает оператор «меньше, чем».
Из двух численных операндов, этот оператор делает один логический результат.
Значение всего выражения считается истинным, если то, что записано слева от
знака (lightLevel
в нашем случае) меньше, чем то, что записано справа от
знака (LIGHT_LEVEL_THRESHOLD
). Довольно логично и интуитивно понятно, не
правда ли?!
Таким образом в переменной tooDark
окажется значение true
, только если
значение уровня освещённости lightLevel
, полученное ранее, окажется меньше
600. В противном случае, в переменной окажется false
.
Конкретное значение, вроде 600 в нашем случае, в подобных случаях часто получают
экспериментально: в зависимости от используемого сенсора и конфигурации помещения,
где стоит устройство.
Идём дальше. Мы видим объявление булевой переменной lightningRequired
, в
качестве значения которой присваивается значение выражения
tooDark && motionDetected
. Символ &&
в C++ означает оператор
логического «и». Как можно догадаться, всё выражение считается истинным
тогда и только тогда, когда и то, что справа, и то что слева от оператора
истинно. Таким образом, переменная lightningRequired
примет значение
true
, если в коридоре одновременно: и слишком темно, и замечено движение
человека. Если не выполнено хоть одно из условий, результатом будет false
.
Как раз то, что нужно.
И наконец, последним выражением идёт вызов функции digitalWrite
для пина
Arduino, к которому подключена лампа. Нам нужно включить лампу, если
переменная lightningRequired
содержит истинное значение и выключить, если
в ней хранится ложь. Чтобы сделать это, мы используем тернарный условный
оператор ? :
. Где в качестве условия используем просто значение переменной
lightningRequired
. Помните? В тернарном операторе условие считается
выполненным, если его значение — не ноль; и не выполненным если его значение —
ноль.
На самом деле для процессора не существует понятия логических значений. Всё
что он умеет — оперировать над целыми числами. Поэтому в C++ существует
автоматическое преобразование типов. Там, где ожидается int
, а мы
используем bool
, за кадром происходит автоматическое преобразование:
true
превращается в целое число 1
, а false
— в целое число 0
.
Так что, булевы выражения и переменные — ни что иное, как удобство для
программистов, синтаксический сахар, который позволяет писать программы более
понятно и выразительно.
Возвращаясь к нашему примеру, в выражении:
digitalWrite(LED_LAMP_PIN, lightningRequired ? HIGH : LOW);
второй аргумент примет значение HIGH
, если lightningRequired
— это
true
; и LOW
, если lightningRequired
— это false
. Таким
образом, мы добились чего хотели: включения света, если он нужен и выключения,
если он не уместен.
О краткости записи
Если вспомнить об автоматическом преобразовании переменных разных типов, а ещё
о том, что HIGH
— это ничто иное, как
макроопределение
числа 1
, а LOW
— макроопределение числа 0
, последнюю строку в нашем примере
можно сократить до лаконичного выражения:
digitalWrite(LED_LAMP_PIN, lightningRequired);
Мы обошлись без тернарного оператора: ведь всё равно функция digitalWrite
получит:
-
единицу: то же самое, что и
HIGH
, еслиlightningRequired
будетtrue
-
ноль: то же самое, что и
LOW
, еслиlightningRequired
будетfalse
Кроме того, логические выражения — это самые обычные выражения в C++, к
которым применяются общие правила встраивания.
Поэтому весь код loop
в примере с экологичным освещением на самом деле мог бы быть записан в одну
строку:
void loop() { digitalWrite(LED_LAMP_PIN, analogRead(LIGHT_SENSOR_PIN) < LIGHT_LEVEL_THRESHOLD && digitalRead(MOTION_SENSOR_PIN)); }
Да, код теперь находится на грани читаемости, но это всего лишь демонстрация
возможностей. В реальной жизни лучше использовать пару промежуточных
переменных:
void loop() { bool tooDark = analogRead(LIGHT_SENSOR_PIN) < LIGHT_LEVEL_THRESHOLD; bool motionDetected = digitalRead(MOTION_SENSOR_PIN); digitalWrite(LED_LAMP_PIN, tooDark && motionDetected); }
Или хотя бы использовать перенос строк для наглядности:
void loop() { digitalWrite(LED_LAMP_PIN, analogRead(LIGHT_SENSOR_PIN) < LIGHT_LEVEL_THRESHOLD && digitalRead(MOTION_SENSOR_PIN) ); }
А если свет таки нужен: условное выражение if
Внимательный читатель мог заметить, что приведённый скетч едва ли может
работать в реальных условиях. Если мы включим свет, когда слишком темно и
зафиксировано движение, при следующем же прогоне loop
датчик «увидит» свет
от нашей же лампы, программа посчитает, что и так достаточно светло и выключит лампу.
Процесс постоянного выключения и включения так и будет продолжаться пока
пироэлектрический датчик будет фиксировать движение. В лучшем случае мы
получим свечение лампы в пол силы, в худшем — раздражающее мерцание. Как быть?
Можно после включения лампы усыплять программу на, скажем, 30 секунд. Вполне
достаточно, чтобы дать постояльцу отеля пройти коридор и не так уж
расточительно с точки зрения экономии электроэнергии.
Как сделать так, чтобы задержка на 30 секунд производилась только после
включения лампы, но не после выключения? Для этого в C++ существует условное
выражение «if». Используя его, скетч может выглядеть следующим образом:
#define LIGHT_SENSOR_PIN A0 #define MOTION_SENSOR_PIN 2 #define LED_LAMP_PIN 5 #define LIGHT_LEVEL_THRESHOLD 600 void setup() { pinMode(LIGHT_SENSOR_PIN, INPUT); pinMode(MOTION_SENSOR_PIN, INPUT); pinMode(LED_LAMP_PIN, OUTPUT); } void loop() { bool tooDark = analogRead(LIGHT_SENSOR_PIN) < LIGHT_LEVEL_THRESHOLD; bool motionDetected = digitalRead(MOTION_SENSOR_PIN); if (tooDark && motionDetected) { digitalWrite(LED_LAMP_PIN, HIGH); delay(30000); // спать 30 секунд } else { digitalWrite(LED_LAMP_PIN, LOW); } }
Начало программы прежнее, но в loop
появляется новое составное выражение,
обозначаемое словами if
, else
и фигурными скобками. Давайте поймём в
чём его суть.
Сразу после слова if
компилятор C++ ожидает в круглых скобках увидеть
логическое выражение, которое в этом случае называется условием. Оно имеет
тот же смысл, что и для тернарного оператора. Если его значение — не ноль или
истинно, выполняется блок кода, который следует сразу после условия в
фигурных скобках. В нашем случае, если tooDark
и motionDetected
истины, будет выполнен блок кода из двух строк:
digitalWrite(LED_LAMP_PIN, HIGH); delay(30000); // спать 30 секунд
Если же условие было нулём, или что то же самое — ложно, блок кода следующий
за условием пропускается и не выполняется вовсе. Зато, если после после этого
пропущенного блока следует слово else
, выполняется блок кода в фигурных
скобках, следующий после этого слова. В нашем случае, если либо tooDark
,
либо motionDetected
были false
, выполнится блок кода из одной строки:
digitalWrite(LED_LAMP_PIN, LOW);
Стоит отметить, что в случае выполнения условия, блок кода следующий после
else
не выполняется, а пропускается. То есть, на самом деле, выражение
if
— это отображение в языке программирования простого и понятного
утверждения: «если что-то, делай то-то, а иначе делай сё-то».
Возвращаясь к нашему примеру, код можно интерпретировать так: «если требуется
освещение, включить свет и уснуть на 30 секунд, а если свет не нужен —
выключить его». Довольно просто и логично.
Использование выражений, которые меняют ход программы в зависимости от каких-то
условий называется ветвлением программы, а блоки кода после if
и
else
называются ветками.
Краткая версия if
При использовании выражения if
совершенно не обязательно использовать
ветку else
. Если что-то должно произойти при выполнении условия, а при
невыполнении не должно происходить ничего, ветку else
можно просто
опустить. Например, мы можем изменить loop
нашей программы следующим образом:
void loop() { bool tooDark = analogRead(LIGHT_SENSOR_PIN) < LIGHT_LEVEL_THRESHOLD; bool motionDetected = digitalRead(MOTION_SENSOR_PIN); bool lightningRequired = tooDark && motionDetected; digitalWrite(LED_LAMP_PIN, lightningRequired); if (lightningRequired) { delay(30000); // спать 30 секунд } }
Эта вариация кода делает абсолютно то же, что и раньше. Просто теперь код
организован иначе. Мы в любом случае делаем указание лампе на включение или
выключение, вызывая digitalWrite
с булевым значением
lightningRequired
, но засыпаем на 30 секунд только если мы включаем свет,
т.е. если переменная lightningRequired
истинна. Если мы только что
выключали свет, спать не нужно: нужно пропустить delay
и сразу оказаться в
конце функции loop
. Поэтому мы не писали ветку else
вовсе.
Более того, если в блоке кода после if
или else
содержится всего одно
выражение, фигурные скобки можно не писать:
if (lightningRequired) delay(30000); // спать 30 секунд
Вложенные выражения if
Блоки кода, используемые в условных выражениях — это самые обычные блоки,
которые следуют общим правилам C++. Поэтому в ветку одного if
можно запросто
вкладывать другой if
.
Для примера рассмотрим скетч устройства, которое контролирует открывание двери
холодильника, автомобиля или чего-то такого. Мы хотим, чтобы при открытии
двери включалась подсветка, а если дверь остаётся открытой более 20 секунд,
раздавался бы предупреждающий писк. При этом, хочется, чтобы писк можно было
отключить переключателем, на случай когда производится длительная
загрузка/разгрузка: так он не будет нервировать.
Допустим на дверце установлен постоянный магнит, а на раме бинарный цифровой датчик
магнитного поля. Если магнитное поле фиксируется — дверца закрыта; иначе магнит отходит от
датчика слишком далеко, магнитного поля нет — можно понять, что дверца открыта.
Также мы подключим к Arduino лампу подсветки, пьезо-пищалку (buzzer) и рокерный выключатель.
В этом случае код работающего устройства может выглядеть так:
#define DOOR_SENSOR_PIN 2 #define BUZZER_PIN 3 #define SWITCH_PIN 4 #define LAMP_PIN 5 #define BUZZ_TIMEOUT 20 #define BUZZ_FREQUENCY 4000 void setup() { pinMode(DOOR_SENSOR_PIN, INPUT); pinMode(SWITCH_PIN, INPUT); pinMode(BUZZER_PIN, OUTPUT); pinMode(LAMP_PIN, OUTPUT); } void loop() { bool doorOpened = !digitalRead(DOOR_SENSOR_PIN); if (doorOpened) { digitalWrite(LAMP_PIN, HIGH); delay(BUZZ_TIMEOUT * 1000); bool buzzEnabled = digitalRead(SWITCH_PIN); if (buzzEnabled) { tone(BUZZER_PIN, BUZZ_FREQUENCY); } } else { digitalWrite(LAMP_PIN, LOW); noTone(BUZZER_PIN); } }
Давайте разберём происходящее в loop
. Первым делом мы определяем
переменную doorOpened
и присваиваем ей значение выражения
!digitalRead(DOOR_SENSOR_PIN)
. Как уже говорилось, дверь стоит считать
открытой, если магнитного поля нет, т.е. digitalRead
для нашего сенсора
возвращает false
. Символ !
перед логическим выражением в C++ означает
оператор логического «не» или просто оператор отрицания. Он действует
на значение, записанное после него, и из true
делает false
, а из
false
делает true
. Как раз то, что нам нужно в этом случае.
Далее следует выражение if
, проверяющее открыта ли дверь. Если да,
начинается исполнение блока кода, следующего непосредственно за условием. В
нём мы первым делом включаем подсветку. Затем засыпаем на 20 секунд (20×1000
мс).
Далее мы должны включить пищалку если только она не была умышленно выключена.
Для этого мы проверяем состояние переключателя, который за это отвечает. Мы
объявляем переменную buzzEnabled
, которой присваиваем логическое значение,
считанное с выключателя.
Обратите внимание, переменная buzzEnabled
объявлена прямо внутри блока
кода, следующего за if
. Так делать можно и нужно: хорошей практикой
является объявление переменных как можно ближе к тому месту, где они впервые
используются.
Напомним о понятии
области
видимости переменных: переменные доступны для использования только внутри
того блока, где они объявлены. В нашем случае buzzEnabled
может быть
использована в выражениях внутри блока кода, следующего за if
, но попытка обращения к ней откуда-то ещё: например, из блока
(doorOpened)
кода ветки else
или непосредственно в loop
вне ветки if
, приведёт
к ошибке на этапе компиляции программы. И это хорошо: нам не стоит впутывать
переменную, которая нужна сию секунду в другие происходящие процессы; это
делает программу чище и нагляднее.
Вслед за чтением переключателя следует вложенное условное выражение. Его
суть абсолютно та же, что и ранее: в зависимости от условия выполнить или не
выполнить код. Одно лишь отличие: оно расположено прямо в блоке кода ветки
другого if
. Это распространённая практика, которая встречается в
программировании довольно часто. На самом деле, во вложенный if
можно
вкладывать другие if
, в них ещё одни и т.д. до бесконечности: язык C++ вас
в этом не ограничивает.
Возвращаясь к примеру, если переключатель находится в положении «включён»,
т.е. buzzEnabled
содержит true
, мы включаем писк. Это делается с
помощью встроенной функции tone
. Она принимает 2 аргумента: номер пина
Arduino, куда подключён пьезоизлучатель и частоту писка. В данном случае, мы
выбрали частоту 4000 Гц, т.е. 4 КГц.
Наконец, если дверь была закрыта, т.е. doorOpened
содержит ложь, мы
убеждаемся, что подсветка и пищалка выключены. Это делается в ветке else
первого условия if
. Как выключить лампу вы понимаете, а функция
noTone
, как можно догадаться, отключает писк на указанном пине.
Вспоминая о правилах короткой записи и встраивании выражений, мы можем чуть
упростить код loop
. Он может выглядеть так:
void loop() { if (!digitalRead(DOOR_SENSOR_PIN)) { digitalWrite(LAMP_PIN, HIGH); delay(BUZZ_TIMEOUT * 1000); if (digitalRead(SWITCH_PIN)) tone(BUZZER_PIN, BUZZ_FREQUENCY); } else { digitalWrite(LAMP_PIN, LOW); noTone(BUZZER_PIN); } }
Мы убрали фигурные скобки для внутреннего if
и встроили вызовы
digitalRead
прямо в условные выражения.
Обратите внимание, как правильное использование отступов в блоках кода,
помогает легко понять к какому if
относится else
и что за чем и при
каких условиях следует. Никогда не пишите без отступов несмотря на то, что
компилятору всё равно. Это делает код не читаемым и для других людей, и для вас
самих:
void loop() { if (!digitalRead(DOOR_SENSOR_PIN)) { digitalWrite(LAMP_PIN, HIGH); delay(BUZZ_TIMEOUT * 1000); if (digitalRead(SWITCH_PIN)) tone(BUZZER_PIN, BUZZ_FREQUENCY); } else { digitalWrite(LAMP_PIN, LOW); noTone(BUZZER_PIN); } }
Клики кнопок
Одной из типичных задач при создании устройств на микроконтроллерах является
определение момента клика тактовой кнопки, чтобы на это можно было как-то
отреагировать. Проблема в том, что кнопка, как сенсор, может лишь сообщить
зажата ли она сейчас или отпущена. Она не может сообщить, что была «нажата
только что».
Но эта проблема легко решается с помощью небольшого приёма в программной части
проекта. Давайте сделаем примитивное устройство с лампой и кнопкой, которое бы
включало и выключало лампу, поочерёдно, при нажатии кнопки: клик — включили,
клик — выключили:
#define BUTTON_PIN 2 #define LAMP_PIN 5 bool lampState = false; bool wasButtonDown = false; void setup() { pinMode(BUTTON_PIN, INPUT); pinMode(LAMP_PIN, OUTPUT); } void loop() { bool isButtonDown = digitalRead(BUTTON_PIN); if (isButtonDown && !wasButtonDown) { lampState = !lampState; delay(10); } wasButtonDown = isButtonDown; digitalWrite(LAMP_PIN, lampState); }
В скетче мы можем видеть 2 булевы переменные:
-
lampState
содержитtrue
, если лампа должна быть сейчас включена иfalse
в противном случае -
wasButtonDown
хранит состояние кнопки на момент последнего прогонаloop
: если кнопка была зажата, значением будетtrue
Теперь взглянем на сам loop
. Первым делом мы считываем состояние кнопки в
логическую переменную isButtonDown
. Если кнопка зажата isButtonDown
будет содержать true
.
Далее следует условное выражение if
с проверкой условия, суть которого в
том, чтобы понять была ли нажата кнопка только что, или она зажата уже давно.
Для этого и используется значение wasButtonDown
.
Таким образом, условие стоит воспринимать как «кнопка зажата сейчас
и
не
была зажата ранее
». Это и есть условие сиемоментного «клика»
кнопкой.
Если условие клика выполнено — действуем. Переворачиваем значение
lampState
с ног на голову:
lampState = !lampState;
Далее, в ветке if
следует delay(10)
. Вызов delay
здесь сделан исключительно
из-за несовершенства механических
кнопок. При нажатии, за микросекунды, когда соприкасаются пластины, кнопка может
зафиксировать замыкание и размыкание десятки раз. Добавив delay(10)
мы просто
пережидаем шторм. Десять миллисекунд — более чем достаточно для успокоения
кнопки, но достаточно мало, чтобы человек заметил это.
Далее мы присваиваем переменной wasButtonDown
недавно считанное значение
isButtonDown
, говорящее о том нажата ли кнопка сейчас. Если кнопка была
зажата только что, произойдёт переключение состояния лампы lampState
, а
переменная wasButtonDown
в итоге примет значение true
и будет
оставаться с ним пока кнопку не отпустят. Таким образом, при следующем вызове
loop
состояние не будет изменено снова.
Если бы мы не прибегали к этому трюку, а использовали только текущее состояние
кнопки без оглядки на предысторию, состояния бы менялись пока кнопка зажата,
десятки тысяч раз в секунду. С точки зрения человека это выглядело бы как
явная, назойливая проблема с устройством.
Цепочки if
Напоследок давайте запрограммируем устройство, которое является простым
климат-контролем для, например, террариума. Допустим к Arduino подключён
термистор, кондиционер, обогреватель и зелёный светодиод. Если
получаемая температура слишком высокая, мы должны включать кондиционер, если
слишком низкая — обогреватель, а если в пределах нормы — включать светодиод.
Тогда программа может выглядеть так:
#define CONDITIONER_PIN 4 #define HEATER_PIN 5 #define OK_LED_PIN 6 #define THERMISTOR_PIN A0 #define TEMP_MIN 400 #define TEMP_MAX 500 void setup() { pinMode(CONDITIONER_PIN, OUTPUT); pinMode(HEATER_PIN, OUTPUT); pinMode(OK_LED_PIN, OUTPUT); } void loop() { int temp = analogRead(THERMISTOR_PIN); if (temp < TEMP_MIN) { digitalWrite(CONDITIONER_PIN, LOW); digitalWrite(HEATER_PIN, HIGH); digitalWrite(OK_LED_PIN, LOW); } else if (temp > TEMP_MAX) { digitalWrite(CONDITIONER_PIN, HIGH); digitalWrite(HEATER_PIN, LOW); digitalWrite(OK_LED_PIN, LOW); } else { digitalWrite(CONDITIONER_PIN, LOW); digitalWrite(HEATER_PIN, LOW); digitalWrite(OK_LED_PIN, HIGH); } }
В этой программе всё должно быть знакомым. Вопрос может вызвать лишь
использование else
и if
рядом, на одной строке. На самом деле — это всего лишь иной
способ записать несколько вложенных условных выражений. Мы могли бы написать
то же самое таким образом:
if (temp < TEMP_MIN) { digitalWrite(CONDITIONER_PIN, LOW); digitalWrite(HEATER_PIN, HIGH); digitalWrite(OK_LED_PIN, LOW); } else { if (temp > TEMP_MAX) { digitalWrite(CONDITIONER_PIN, HIGH); digitalWrite(HEATER_PIN, LOW); digitalWrite(OK_LED_PIN, LOW); } else { digitalWrite(CONDITIONER_PIN, LOW); digitalWrite(HEATER_PIN, LOW); digitalWrite(OK_LED_PIN, HIGH); } }
Но вспомним, что условное выражение — это полноправное выражение в C++. В
внешней ветке else
оно одно, поэтому фигурные скобки можно опустить:
if (temp < TEMP_MIN) { digitalWrite(CONDITIONER_PIN, LOW); digitalWrite(HEATER_PIN, HIGH); digitalWrite(OK_LED_PIN, LOW); } else if (temp > TEMP_MAX) { digitalWrite(CONDITIONER_PIN, HIGH); digitalWrite(HEATER_PIN, LOW); digitalWrite(OK_LED_PIN, LOW); } else { digitalWrite(CONDITIONER_PIN, LOW); digitalWrite(HEATER_PIN, LOW); digitalWrite(OK_LED_PIN, HIGH); }
А вспомнив о том, что пустое пространство для компилятора ничего не значит,
можем убрать несколько отступов и перенос строки, чтобы получить:
if (temp < TEMP_MIN) { digitalWrite(CONDITIONER_PIN, LOW); digitalWrite(HEATER_PIN, HIGH); digitalWrite(OK_LED_PIN, LOW); } else if (temp > TEMP_MAX) { digitalWrite(CONDITIONER_PIN, HIGH); digitalWrite(HEATER_PIN, LOW); digitalWrite(OK_LED_PIN, LOW); } else { digitalWrite(CONDITIONER_PIN, LOW); digitalWrite(HEATER_PIN, LOW); digitalWrite(OK_LED_PIN, HIGH); }
Подобные цепочки из выражений if
— довольно частое явление. Если бы
вариантов было больше трёх, вложенные if
смотрелись бы громоздко и
уродливо, в то время как такая цепочка выглядела бы всё равно понятно и хорошо.
На самом деле, программу климат-контроля можно написать компактнее. В этом
виде она приведена просто для демонстрации if - else if - else
цепочки.
Для полноты картины приведём краткую версию:
#define CONDITIONER_PIN 4 #define HEATER_PIN 5 #define OK_LED_PIN 6 #define THERMISTOR_PIN A0 #define TEMP_MIN 400 #define TEMP_MAX 500 void setup() { pinMode(CONDITIONER_PIN, OUTPUT); pinMode(HEATER_PIN, OUTPUT); pinMode(OK_LED_PIN, OUTPUT); } void loop() { int temp = analogRead(THERMISTOR_PIN); bool tooCold = temp < TEMP_MIN; bool tooHot = temp > TEMP_MAX; digitalWrite(CONDITIONER_PIN, tooHot); digitalWrite(HEATER_PIN, tooCold); digitalWrite(OK_LED_PIN, !(tooCold || tooHot)); }
Опять же, всё знакомо за исключением, быть может значения выражения
!(tooCold || tooHot)
. Символ ||
в C++ — это оператор логического
«или». Значение выражения истинно если хотя бы одно из значений: слева или
справа от оператора истинны.
Логические выражения — ни что иное, как арифметические выражения. Здесь
действуют всё те же правила: в одном выражении может быть сколько угодно
операторов, а очерёдность их применения можно обозначить скобками. В данном
случае, мы сначала вычисляем логическое значение tooCold || tooHot
, а
затем оператором логического «не» (!
) инвертируем полученное значение.
Итак, вы познакомились с логическими переменными, выражениями и сопутствующими
операторами. Это уже позволяет делать устройства, которые выглядят умными.
Включайте фантазию, дерзайте!