Логики вполне достаточно
Программирование в значительной степени связано с доказуемостью, с выводимостью. Мы должны иметь возможность понимать совсем не простое, проходящее через множество ветвлений поведение программ во время их выполнения. Человек физически не в состоянии проследить за мириадами базисных операций, выполняемых компьютером.
В принципе, все может быть выведено из текста программы простыми рассуждениями. Если бы существовала наука выводимости, она оказала бы нам существенную помощь.
Можно радоваться: есть такая наука – это логика. Логика – это механизм, стоящий за способностью человека делать выводы. Когда нам говорят, что Сократ – человек и все люди смертны, то без раздумья мы заключаем, что Сократ смертен. Сделать этот вывод нам помогли законы логики. Пусть справедливо утверждение: "Если температура в городе поднимается выше 30 градусов, то возникает угроза загрязнений". Кто-то говорит, что поскольку сегодня температура достигла только 28 градусов, угрозы загрязнений нет; мы скажем про такого человека, что его логика "хромает".
Логика – основа математики. Математики доверяют доказательствам в пять строчек или доказательствам, растянутым на 60 страниц, только потому, что каждый шаг доказательства выполнен в соответствии с правилами логики.
Логика – основа разработки ПО. Уже в предыдущей лекции мы познакомились с условиями в контрактах, связанных с нашими классами и методами, например, в предусловии: "i должно быть между 1 и count". Мы будем также использовать условия, выражая действия, выполняемые программой, например: "Если i положительно, то выполни этот оператор".
Мы уже видели при изучении контрактов, что такие условия появляются в наших программах в форме "булевских выражений". Булевское выражение может быть сложным, включающим, например, операции "not", "and", "or", "implies". Это соответствует выводам, характерным для естественного языка: " Если пройдет 20 минут после назначенного срока, и она не позвонит или не пришлет SMS, то следует, что она не появится совсем". Интуитивно мы прекрасно понимаем, что означает это высказывание, и этого понимания вполне достаточно и для условий, применяемых в ПО.
Но до определенных пределов. Разработка ПО требует доказуемости, а точные выводы требуют законов логики. Поэтому, прежде чем вернуться к нашим дорогим объектам и классам, следует более тесно познакомиться с законами логики.
Логика – математическая логика, если быть более точным, – самостоятельная наука, таковой является и ее ветвь "Логика в информатике", которой посвящены многие учебники и отдельные курсы. Я надеюсь, что такой курс вами уже пройден или будет пройден. Эта лекция вводит основные понятия логики, необходимые в программировании. Хотя логика пользуется заслуженной славой как наука о выводимости, нам она нужна для более ограниченных целей – для понимания той части выводимости, которая базируется на условиях. Логика даст нам прочную основу для выражения и понимания условий, появляющихся в контрактах и в операторах программы.
Первая часть лекции вводит булеву алгебру в форме пропозиционального исчисления, которое имеет дело с базисными высказываниями, включающими специфические переменные. Вторая часть расширяет обсуждение до логики предикатов, позволяющей выражать свойства произвольного множества значений.
5.1. Булевские операции
Условие в булевой (булевской) алгебре, так же как и в языках программирования, выражается в виде булевского выражения, построенного из булевских переменных и операций. Результатом вычисления булевского выражения является булевское значение.
Булевские значения, переменные, операции, выражения
Существуют ровно две булевские константы (boolean constants), также называемые "булевскими или истинностными значениями", которые будем записывать в виде True и False для совместимости с нашим языком программирования, хотя логики часто пишут просто T и F, а электротехники предпочитают обозначать их как 1 и 0.
Булевская переменная задается идентификатором, обозначающим булевское значение. Типично мы используем булевскую переменную, чтобы выразить свойство, которое может иметь в качестве значения истину или ложь. Говоря о погоде, мы могли бы ввести переменную rain_today, чтобы задать свойство, говорящее нам, будет ли сегодня идти дождь.
Начав с булевских констант и переменных, мы можем затем использовать булевские операции для получения булевских выражений. Например, если rain_today и cuckoo_sang_last_night ("дождь сегодня" и "кукушка куковала сегодня ночью") являются булевскими переменными, то булевскими выражениями в соответствии с изучаемыми ниже правилами будут:
- rain_today – булевская переменная сама по себе без всяких операций является булевским выражением (простейшая форма наряду с булевскими константами);
- not rain_today – используется булевская операция not;
- (not cuckoo_sang_last_night) implies rain_today – используются операции not и implies, а также скобки для выделения подвыражений.
Каждая булевская операция, такая как not, or, and, =, implies, задает правила вычисления значения результирующего выражения по заданным значениям ее операндов.
В языке программирования булевские операции, подобно булевским константам, задаются ключевыми словами. В математических текстах знаки операций обычно задаются отдельными символами, не все из которых присутствуют на клавиатуре компьютера. Приведем соответствие между ключевыми словами и общеупотребительными символами в математике:
Ключевое слово Eiffel | Общеупотребительные математические символы |
---|---|
not | ¬ или ~ |
or | ∨ или | |
and | ∧ или & |
= | ↔ или = |
implies | => |
В Eiffel булевские константы, переменные и выражения имеют тип BOOLEAN, определенный классом, подобно всем типам. Класс BOOLEAN является библиотечным классом, доступным для просмотра в EiffelStudio; там вы можете увидеть все булевские операции, обсуждаемые в этой лекции.
Отрицание
Рассмотрим первую операцию – not. Для формирования булевского выражения с not укажите эту операцию с последующим булевским выражением. Это выражение может быть булевской переменной, как в not your_variable; или может быть сложным выражением (заключенным в скобки для устранения двусмысленности), как в следующих примерах, где a и b являются булевским переменными:
- not (a or b).
- not (not a)
Для произвольной булевской переменной a значение not a есть False, если значение a есть True, и True, если значение a – False, мы можем выразить свойства операции not следующей таблицей:
Это так называемая таблица истинности (truth table), которая является стандартным способом задания булевских операций – в первых столбцах (для данной операции один столбец) задаются все возможные значения операндов операции, в последнем столбце задается соответствующее данному случаю значение выражения.
Операция not задает отрицание – замену булевского значения его противоположностью, где True и False противоположны друг другу.
Из таблицы истинности следуют важные свойства этой операции.
Теоремы: "Свойства отрицания"
- точно одно из выражений e или not e имеет значение True;
- точно одно из выражений e или not e имеет значение False;
- только одно из выражений e или not e имеет значение True (принцип исключенного третьего);
- Либо e, либо not e имеет значение True (принцип непротиворечивости)
Доказательство: по определению булевского выражения, e может иметь только значение True или False. Таблица истинности показывает, что если e имеет значение True, то not e будет иметь значение False; все четыре свойства являются следствиями этого факта (и два последних утверждения следуют непосредственно из первого).
Дизъюнкция
Операция or использует два операнда в отличие от операции not. Если a и b являются булевскими выражениями, булевское выражение a or b имеет значение True, если и только если либо a, либо b имеют это значение. Соответственно, оно имеет значение False, если и только если оба операнда имеют это значение. Это отражает следующая таблица истинности:
Первые два столбца перечисляют все четыре возможные комбинации значений a и b.
Слово "or" заимствовано из естественного языка в его неисключительном смысле, как в предложении "Тот, кто придумал эту инструкцию, глуп или слеп", что не исключает, что оба случая могут иметь место.
Обычный язык часто использует "или" в исключающем смысле, означающее, что только один результат может быть истинным, но не оба вместе: "Что будем заказывать – красное или белое?". Здесь речь идет о другой булевской операции – "исключающему или" – "xor" в Eiffel, чьи свойства следует изучить самостоятельно.
Неисключающая операция or называется дизъюнкцией. Это не очень удачное имя, поскольку позволяет думать об исключающей операции, но в нем есть свое преимущество, благодаря симметрии с "конъюнкцией" – именем следующей операции and.
Дизъюнкция имеет значение False только в одном из четырех возможных случаев, заданном последней строкой таблицы истинности.
Теорема: "Принцип дизъюнкции"
Таблица истинности показывает, что операция or является коммутативной: для любых a и b значение a or b то же, что и b or a. Это также вытекает из принципа дизъюнкции.