Пятая часть перевода D Programming Language Tutorial от Ali Çehreli. В этой части переведена глава Logical Expressions. Материал главы рассчитан на новичков.
Логические выражения
Фактическая работа, которую выполняет программа заключается в выражениях. Любая часть программы, которая создает значение или побочный эффект, называется выражением. Оно определяется широким понятием, потому что даже константное значение, скажем 42, и строковое, например «hello», это выражения, поскольку они создают эти соответствующие константы 42 и «hello».
Важно: Не путайте создание значения с декларацией переменной. Значениям не нужно привязываться к переменным.
Вызовы функции, например writeln — это выражения, кстати, потому что у них есть побочные эффекты. В случае с writeln, этот эффект накладывается на стандартный поток вывода печатанием символов на нем. Другим примером из программ, что мы написали ранее, может быть операция присвоения, которая изменяет переменную, которая стоит слева.
Из-за создания значений, выражения могут принимать участие в других выражениях. Это позволяет нам формировать более сложные выражения из более простых. Например, предположим, что есть функция названная currentTemperature, которая возвращает значение текущей температуры воздуха, это значение, что она производит, может быть непосредственно использоваться в выражении writeln:
writeln("It's ", currentTemperature()," degrees at the moment.");
Эта строка содержит следующее:
- «It's»
- currentTemperature()
- " degrees at the moment.
- writeln() выражение, которое использует предыдущие три.
В этой главе мы обсудим конкретные типы выражений, которые используются в условных высказываниях.
Прежде, чем идти дальше я бы хотел повторить оператор присваивания еще раз, сейчас подчеркивая эти два выражения, что находятся на слева и справа от него. Оператор присваивания (=) присваивает значение указанного выражения справа выражению слева (например переменной).
temperature = 23 // temperature's value becomes 23
Логические выражения
Логические выражения — выражения, которые используются в Булевой алгебре. Логические выражения то, что делают компьютерные программы, принимая решения, вроде «если ответ Да, я сохраню файл».
Логические выражения могут иметь одно из двух значений: false, обозначающее ложь, и true, обозначающее истину.
Я буду использовать writeln выражения в следующих примерах. Если строка напечатала trueв конце, это будет обозначать, что то, что напечатано в этой строке — это правда. Аналогично,false будет обозначать, что то, что напечатано — ложь. Например, если этот вывод программытакой:
There is coffee: true
это будет подразумевать, «там есть кофе». Аналогично:
There is coffee: false
будет подразумевать «там кофе нет». Обратите внимание, что в действительности «is», находящийсяслева не обозначает, что кофе есть. Я использую конструкцию "… is …: false" чтоб обозначить «это нетак» или «это ложь». Логические выражения используются активно в предикатах, циклах, аргументахфункций и т. д. Важно понять, как они работают. К счастью, логические выражения очень простые дляобъяснения и просты в использовании.
Ниже представлены логические операторы, которые используются в логических выражениях:
- Оператор равенства (==) отвечает на вопрос «это равно этому?». Он сравнивает два выражения слева от него справа и возвращает true, если они равны и false если нет. По сути, значение, возвращаемое оператором равенства (==) — это логическое выражение
Как пример, предположим, что у нас есть указанные две переменные:int daysInWeek = 7; int monthsInYear = 12;
Далее, это два логических выражения, которые используют эти значения:
daysInWeek == 7 // true monthsInYear == 11 // false
- Оператор неравенства (!=) отвечает на вопрос «это не равно этому». Он сравнивает эти два выражения с обеих его сторон и возвращает результаты, противоположные тем, что возвращает оператор (==)
daysInWeek != 7 // false monthsInYear != 11 // true
- Оператор дизъюнкции (||) обозначает «или», и возвращает true если любое из логических выражений равно true
Если значение выражения слева это true, то оператор вернет true и даже не посмотрит на выражение справа. Если выражение слева вернет false, то оператор вернет значение выражения справа. Этот оператор похож на союз «или» в русском языке.
Выражение слева Оператор Выражение справа Результат false || false false false || true true true || false (не имеет значения) true true || true (не имеет значения) true import std.stdio; void main() { // false означает "нет", true означает "да" bool existsCoffee = false; bool existsTea = true; writeln("There is warm drink: ", existsCoffee || existsTea); }
Потому что по крайней мере одно из двух выражений истинно логическое выражение выше выдает true.
- Оператор конъюнкции (&&) обозначает «и» и возвращает true, если оба выражения, слева и справа, возвращают true.
Если значение выражения слева это false, то оператор вернет false и даже не посмотрит на выражение справа. Если выражение слева вернет true, то оператор вернет значение выражения справа. Этот оператор похож на союз «и» в русском языке.
Выражение слева Оператор Выражение справа Результат false && false (не имеет значения) false false && true (не имеет значения) false true && false false true && true true - Оператор строгой дизъюнкции (^) отвечает на вопрос «один или другой, но не оба?». Этот оператор возвращает true, если только одно выражение возвращает true, но не оба.
Выражение слева Оператор Выражение справа Результат false ^ false false false ^ true true true ^ false true true ^ true false Например, эта логика, что представляет: «Я поиграю в шахматы, если только один из двух друзей придет». Это можно запрограммировать так:
writeln("I will play chess: ", jimShowedUp ^ bobShowedUp);
- Оператор строго меньше (<) отвечает на вопрос «Это меньше этого?» (или «При сортировке это будет выше?»)
writeln("We beat: ", theirScore < ourScore);
- Оператор строго больше (>) отвечает на вопрос «Это больше этого?» (или «При сортировке это будет ниже?».)
writeln("They beat: ", theirScore > ourScore);
- Оператор нестрого меньше (<=) отвечает на вопрос «Это меньше или равно этому?» (или «При сортировке это будет выше или на той же поцизии?».)
writeln("We were not beaten: ", theirScore <= ourScore);
- Оператор нестрого меньше (>=) отвечает на вопрос «Это больше или равно этому?» (или «При сортировке это будет ниже или на той же поцизии?».)
writeln("We did not beat: ", theirScore >= ourScore);
- Оператор инверсии (!) подразумеват «Это противоложность чего-то». Он отличается от предыдущих операторов тем, что работает только с одним выражением и возвращает true, если выражение возвращает false, и возвращает false, если выражение возвращает true.
writeln("I will walk: ", !existsBicycle);
Группирование выражений
Этот порядок, в котором выражения вычисляются может быть заключен в скобки для того, чтоб изсгруппировать. Когда выражения в скобках попадают в более сложные выражения, значения этих выраженийрасчитвается до того, как они будут использованы в выражениях, в которые они попали. Например, этовыражение «если там есть кофе или чай, а также печенье или булочка, то я счастлив» может бытьзапрограммировано примерно так, как указано ниже:
writeln("I am happy: ", (existsCoffee || existsTea) && (existsCookie || existsScone));
Если эти вложенные выражения не были заключены в скобки, то они будут выполнены с учетом приоритетовоператоров по правилам D (которые были унаследованы из языка C). Поскольку в этих операторконъюнкции && приоритет выше, чем оператор дизъюнкции ||, написание выражения безскобок не будет расчитано, как предполагается.
writeln("I am happy: ", existsCoffee || existsTea && existsCookie || existsScone);
Оператор конъюнкции && выполнится в первую очередь, и все выражение будет семантическиэквивалентно следующему выражению:
writeln("I am happy: ", existsCoffee || (existsTea && existsCookie) || existsScone);
Это в корне имеет другое значение: «Если там есть кофе или чай с печеньем или булочка, то ясчастлив».
Чтение входных данных типа bool
Все значения типа bool, представленные выше, напечатаны как «false» или «true». Но это неработает в обратном направлении: эти строки «false» и «true» не автоматически читаются как этизначения false и true. По этой причине, эти входные данные должны считываться какстроки а затем конвертироваться в значения типа bool.
Поскольку в одном из упражнений ниже нужно, чтоб Вы ввели «false» и «true», я вынуждениспользовать возможности D, что я не объяснил Вам еще. Я описал ниже метод, который конвертируетуказанные строковые входные данные в данные типа bool. Этот метод будет решать эту задачувыполнением to, который продекларирован в модуле std.conv. (Вы можете увидетьConvException ошибки, если введете что-нибудь, кроме «false» или «true».)
Я надеюсь, что все части кода, которые в main() в следующих программах и понятны на данномэтапе. read_bool() это тот метод, в котором есть новые, для Вас, возможности языка. Хотя явставил комментарии для, чтобы объяснить, что он делает, Вы можете не обращать внимания наэтот метод. Все-таки, он должен быть в коде программы для компиляции и корректной работы.
Упражнения
- Мы видели выше, что эти операторы < и > использованы чтоб определить, когда значение больше и когда меньше другого значения, но там не было оператора, который отвечает на вопрос «это между?», чтоб определить когда значение находится между двумя другими значениями.
Давайте предположим, что программист написал следующий код для определения когда value между 10 и 20. Заметьте, что программа не скомпилируется как описано.
import std.stdio; void main() { int value = 15; writeln("Is between: ", 10 < value < 20); // ← ошибка компиляции }
Попробуйте заключить в скобки все это выражение:
writeln("Is between: ", (10 < value < 20)); // ← ошибка компиляции
Заметьте, что эта программа все равно не скомпилируется.
- Пока идет поиск решения этой проблемы, тот же программист обнаруживает, что следующие использование скобок позволяет cкомпилировать код:
writeln("Is between: ", (10 < value) < 20); // ← это пройдет компиляцию, но рабоет неправильно
Заметьте, что программа сейчас работает как предполагалось и выводит «true». К сожалению этот вывод сбивает с толку, потому что в этой программе есть баг. Посмотреть последствия этого бага можно, заменив 15 значением больше 20:
int value = 21;
Заметьте, что программа все равно выводит «true», несмотря на то, что 21 не меньше 20. Подсказка: Помните, что тип логического выражения это bool. У этого не должно быть смысла, когда bool меньше 20.
- Логические выражения, которые отвечают на вопрос «Это между?» должны, вместо этого, отвечать на вопрос: «Это больше чем минимальное значение и меньше чем максимальное?».
Измените выражение в этой программе с учетом этой логики и заметите, что она сейчас выводит «true», как и ожидалось. Также можно протестировать, что это логическое выражение работает правильно с другими значениями. Например, когда value это 50 или 1, эта программа должна вывести «false» и когда value равно 12, эта программа выведет «true».
- Предположим, что мы можем пойти на пляж, когда одно из следующих условий истинно:
- Если расстояние до пляжа меньше 10 км и есть велосипеды для каждого.
- Если нас меньше 6, и у нас есть машина, и у одного из нас есть водительские права.
Как написано, следующая программа всегда выводит «true». Сконструируйте логику так, что программа будет выводить «true», когда одно из условий выше выполнится. (Когда программа спросит, введите «true» или «false» на вопросы, которые начинаются со слов «Is there a».). На забудьте подключить метод read_bool() при тестировании программы:
import std.stdio; import std.conv; import std.string; void main() { write("How many are we? "); int personCount; readf(" %s", &personCount); write("How many bicycles are there? "); int bicycleCount; readf(" %s", &bicycleCount); write("What is the distance to the beach? "); int distance; readf(" %s", &distance); bool existsCar = read_bool("Is there a car? "); bool existsLicense = read_bool("Is there a driver license? "); /* Замените это 'true' ниже логическим выражением, которое вернет 'true', когда будет выполнять одно из условий: */ writeln("We are going to the beach: ", true); } /* Пожалуйста обратите внимание на то, что этот метод включает в себя возможности, которые будут описаны в книге позже. */ bool read_bool(string message) { // Выводит сообщение write(message, "(false or true) "); // Читает линию как строку. string input; while (input.length == 0) { input = chomp(readln()); } // Возвращает булево значение из строки bool result = to!bool(input); // Возвращает результат return result; }
Введите различные значения и протестируйте это логическое выражение, которое Вы написали, работает правильно.
- Поскольку компилятор воспринимает 10 < value уже как выражение, он ждет, что после него будет запятая, чтоб принять его как аргумент для writeln. Использование скобок вокруг всего выражения не сработает, потому что в это время закрывающая скобка будет ожидаться в том же выражении.
- Группировка этого выражения как (10 < value) < 20 удалит ошибку при компиляции, потому что в этом случае первая часть будет рассчитана и далее его результат будет сравниваться с < 20
Мы знаем, что значение логического выражения, такого как 10 < value, будет false или true. false и true принимают значения 0 и 1, соответственно, в целочисленных выражениях. Мы рассмотрим автоматическое преобразование типов в следующих главах. В результате, все это выражение будет эквивалентно либо 0 < 20 либо 1 < 20, которые оба вернут true.
- Выражение «больше чем минимальное значение и меньше чем максимальное», может быть запрограммировано следующим образом:
writeln("Is between: ", (value > 10) && (value < 20));
- «Есть ли велосипеды для каждого» может быть запрограммировано как personCount <= bicycleCount или так bicycleCount >= personCount. Остальное в этом логическом выражении может быть сразу переведено в программный код из упражнения:
writeln("We are going to the beach: ", ((distance < 10) && (bicycleCount >= personCount)) || ((personCount <= 5) && existsCar && existsLicense) );
Обратите внимание на расположение данного оператора дизъюнкции (||) делает чтение удобнее, разделяя эти два основных предиката.
Автор: ANtlord