Булева логика для программиста: операции, таблицы истинности и код

Булева логика для программиста: операции, таблицы истинности и код Полезное

Булева логика (алгебра логики, булева алгебра) — это раздел математики, который работает с высказываниями, принимающими ровно два значения: истина (True, 1) и ложь (False, 0). Из простых высказываний с помощью логических операций собирают сложные, а результат любого выражения тоже равен истине или лжи. На этой алгебре стоят условия if, циклы, фильтры запросов и вся цифровая схемотехника.

Разберем базовые операции (И, ИЛИ, НЕ, XOR, импликация, эквивалентность), их таблицы истинности, законы де Моргана и то, как все это выглядит в коде. Отдельно разведем логическое И и битовое И — их часто путают, и эта путаница дает реальные баги.

Мини-словарь: что с чем не путать

Тема состоит из близких понятий, поэтому сначала короткий словарь.

  • Высказывание — утверждение, про которое можно сказать, истинно оно или ложно («5 > 3»).
  • Логическая операция — действие над высказываниями (И, ИЛИ, НЕ и т.д.).
  • Таблица истинности — таблица, где для всех комбинаций входов выписан результат операции.
  • Логический оператор (and, or, not) — работает с истинностью выражения целиком.
  • Битовый оператор (&, |, ^, ~) — работает отдельно с каждым битом числа.

Последние два — разные вещи, к ним вернемся в отдельном разделе.

Базовые логические операции

Пусть A и B — высказывания, каждое равно 0 (ложь) или 1 (истина). Основные операции:

Операция Названия Обозначения в коде Когда истинна
Конъюнкция логическое И, AND and, && оба операнда истинны
Дизъюнкция логическое ИЛИ, OR or, \|\| хотя бы один операнд истинен
Инверсия отрицание, НЕ, NOT not, ! исходное значение ложно
Исключающее ИЛИ XOR, сложение по модулю 2 ^ операнды различны
Импликация следование нет прямого оператора ложна только когда из истины следует ложь
Эквивалентность равнозначность == для булевых операнды равны

Импликацию и эквивалентность в языках чаще выражают через базовые операции, а не отдельным знаком: импликация A -> B равна (not A) or B, эквивалентность — обычному сравнению A == B.

Таблицы истинности

Таблица истинности перечисляет результат для всех комбинаций входов. Для двух переменных строк всегда четыре.

И, ИЛИ, XOR:

A B A И B A ИЛИ B A XOR B
0 0 0 0 0
0 1 0 1 1
1 0 0 1 1
1 1 1 1 0

Импликация и эквивалентность:

A B A -> B A эквив. B
0 0 1 1
0 1 1 0
1 0 0 0
1 1 1 1

Отрицание проще, для одной переменной:

A НЕ A
0 1
1 0

Обратите внимание на импликацию: единственный ложный случай — когда условие A истинно, а следствие B ложно. Из ложной посылки следует что угодно, поэтому первые две строки дают истину.

Логические операторы в коде

В Python логические операторы — and, or, not. Проверим базовые случаи и один важный нюанс — что именно возвращают операторы.

print(True and False)   # False
print(True or False)    # True
print(not True)         # False

# and/or возвращают ОПЕРАНД, а не всегда bool:
print("" and 5)         # "" (первый ложный операнд)
print(0 or "hello")     # hello (первый истинный операнд)
print(3 and 4)          # 4 (оба истинны -> последний)

Вывод:

False
True
False

hello
4

Пустая строка выводится как пустая строка — это и есть значение "" and 5. Оператор and возвращает первый ложный операнд, а or — первый истинный; если таких нет, возвращается последний. Здесь работает понятие «истинность»: пустая строка, 0, None, пустой список считаются ложными, остальное — истинным.

Второе важное свойство — короткое замыкание (short-circuit): вычисление обрывается, как только результат ясен.

def side():
    print("side called")
    return True

print(False and side())   # side НЕ вызывается: и так False
print(True or side())     # side НЕ вызывается: и так True

Вывод:

False
True

Строки side called в выводе нет: до вызова side() дело не дошло. Это удобно для проверок вида if user is not None and user.is_active.

Логическое И (and) и битовое И (&) — не одно и то же

and смотрит на истинность всего выражения. & работает с каждым битом числа по отдельности. Сравним на числах 5 и 3:

print(bin(5), bin(3))       # 0b101 0b11
print(5 & 3)   # 1  -> побитовое И: 0b101 & 0b011 = 0b001
print(5 | 3)   # 7  -> побитовое ИЛИ: 0b111
print(5 ^ 3)   # 6  -> побитовое XOR: 0b110
print(~5)      # -6 -> инверсия битов

Вывод:

0b101 0b11
1
7
6
-6

Битовое И берет каждую пару битов: 1&1=1, 0&1=0, 1&1=1, что дает 0b001 = 1. А логическое 5 and 3 вернуло бы 3, потому что оба числа истинны.

Путаница операторов дает тихий баг. Классика — написать & вместо and между сравнениями:

x = 20
# намерение: (x > 0) and (x < 10) -> должно быть False
print(x > 0 & x < 10)

Вывод:

True

Результат неверный: ожидали False, получили True. Причина в приоритете: у & он выше, чем у сравнений, поэтому Python читает выражение как x > (0 & x) < 10, то есть как цепочку (x > 0) and (0 < 10), а это всегда истина. Исправление — использовать логический оператор и скобки:

x = 20
print((x > 0) and (x < 10))   # False - как и ожидалось

Вывод:

False

Правило простое: для условий (True/False) берите and, or, not; для работы с битами чисел — &, |, ^, ~.

Битовые операции и флаги

Битовые операторы удобны, когда несколько признаков упакованы в одно число. Каждый бит — отдельный флаг. Разберем права доступа:

READ, WRITE, EXEC = 0b001, 0b010, 0b100

perm = READ | WRITE      # включаем два флага
print(bin(perm))         # 0b11
print(bool(perm & WRITE))# True  - бит WRITE установлен
print(bool(perm & EXEC)) # False - бита EXEC нет

perm |= EXEC             # добавить флаг
print(bin(perm))         # 0b111
perm &= ~WRITE           # снять флаг
print(bin(perm))         # 0b101

Вывод:

0b11
True
False
0b111
0b101

Здесь | включает бит, & флаг проверяет его наличие, & ~флаг сбрасывает бит. XOR (^) переключает бит на противоположный. Такой прием встречается в правах файловой системы, настройках и сетевых масках.

Законы алгебры логики: де Моргана

Законы позволяют упрощать выражения. Самые ходовые в коде — законы де Моргана: отрицание конъюнкции равно дизъюнкции отрицаний, и наоборот.

  • НЕ (A И B) = (НЕ A) ИЛИ (НЕ B)
  • НЕ (A ИЛИ B) = (НЕ A) И (НЕ B)

Проверим первый закон полным перебором:

for a in (False, True):
    for b in (False, True):
        left = not (a and b)
        right = (not a) or (not b)
        print(a, b, left, right, left == right)

Вывод:

False False True True True
False True True True True
True False True True True
True True False False True

Последняя колонка везде True: обе части равны на всех входах, закон выполняется. На практике это помогает переписать not (x and y) в читаемое (not x) or (not y) и убрать лишние отрицания в условиях. Другие полезные тождества: коммутативность (a and b = b and a), а также a or (a and b) = a (закон поглощения).

Выводы

  • Булева логика оперирует двумя значениями — истина и ложь; из простых высказываний операции собирают сложные, результат тоже булев.
  • Базовый набор: И, ИЛИ, НЕ, XOR, импликация ((not A) or B), эквивалентность (A == B); таблица истинности для двух входов — четыре строки.
  • Логические and/or/not работают с истинностью выражения (и умеют короткое замыкание), битовые &/|/^/~ — с отдельными битами числа; путать их нельзя.
  • У & приоритет выше, чем у сравнений, — в условиях используйте and/or и скобки.
  • Битовые операции удобны для флагов и масок; законы де Моргана упрощают отрицания в условиях.

Где применяется / связь с практикой

Булева логика — это фундамент условий, циклов и фильтров в любом коде, а также баз данных (WHERE a AND b), проверок форм и цифровой схемотехники. Без уверенного владения таблицами истинности и операторами сложно писать корректные ветвления и отлаживать «неожиданные» результаты условий.

Освойте тему на практике

Логика тесно связана с остальной прикладной математикой для разработчика — множествами, комбинаторикой, дискретными структурами. Собрать эту базу системно помогает курс Прикладная математика для разработчиков. Посмотреть формат и темы вживую можно на бесплатных открытых уроках Otus — там разбирают задачи и отвечают на вопросы.

FAQ

Чем импликация отличается от обычного «если»?
Импликация A -> B ложна ровно в одном случае: истинная посылка и ложное следствие. Из ложной посылки формально следует что угодно, поэтому строки с A=0 дают истину — это отличается от бытового «если».

Почему and иногда возвращает не True/False, а само значение?
В Python and возвращает первый ложный операнд, а or — первый истинный; если таких нет, возвращается последний вычисленный операнд. Поэтому 0 or "hello" дает строку hello, а не True.

XOR — это то же самое, что «не равно»?
Для булевых значений да: a ^ b истинно, когда операнды различны, что совпадает с a != b. Но ^ еще и битовый оператор для чисел, где он работает по каждому биту отдельно.

OTUS Журнал
Скидка 5% 14-20 сентября на курсы (popup)