Pull to refresh

Учебник по языку программирования D. Часть 5

Reading time 9 min
Views 8.5K
Original author: Ali Çehreli
Пятая часть перевода D Programming Language Tutorial от Ali Çehreli. В этой части переведена глава Logical Expressions. Материал главы рассчитан на новичков.
  1. Часть 1
  2. Часть 2
  3. Часть 3
  4. Часть 4


Логические выражения


Фактическая работа, которую выполняет программа заключается в выражениях. Любая часть программы, которая создает значение или побочный эффект, называется выражением. Оно определяется широким понятием, потому что даже константное значение, скажем 42, и строковое, например «hello», это выражения, поскольку они создают эти соответствующие константы 42 и «hello».

Важно: Не путайте создание значения с декларацией переменной. Значениям не нужно привязываться к переменным.

Вызовы функции, например writeln — это выражения, кстати, потому что у них есть побочные эффекты. В случае с writeln, этот эффект накладывается на стандартный поток вывода печатанием символов на нем. Другим примером из программ, что мы написали ранее, может быть операция присвоения, которая изменяет переменную, которая стоит слева.

Из-за создания значений, выражения могут принимать участие в других выражениях. Это позволяет нам формировать более сложные выражения из более простых. Например, предположим, что есть функция названная currentTemperature, которая возвращает значение текущей температуры воздуха, это значение, что она производит, может быть непосредственно использоваться в выражении writeln:

writeln("It's ", currentTemperature()," degrees at the moment.");

Эта строка содержит следующее:
  1. «It's»
  2. currentTemperature()
  3. " degrees at the moment.
  4. 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);
  • Оператор строго меньше (&lt) отвечает на вопрос «Это меньше этого?» (или «При сортировке это будет выше?»)
    writeln("We beat: ", theirScore &lt ourScore);
  • Оператор строго больше (&gt) отвечает на вопрос «Это больше этого?» (или «При сортировке это будет ниже?».)
    writeln("They beat: ", theirScore &gt ourScore);
  • Оператор нестрого меньше (&lt=) отвечает на вопрос «Это меньше или равно этому?» (или «При сортировке это будет выше или на той же поцизии?».)
    writeln("We were not beaten: ", theirScore <= ourScore);
  • Оператор нестрого меньше (&gt=) отвечает на вопрос «Это больше или равно этому?» (или «При сортировке это будет ниже или на той же поцизии?».)
    writeln("We did not beat: ", theirScore &gt= 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;
    }
    Введите различные значения и протестируйте это логическое выражение, которое Вы написали, работает правильно.
… решение
  1. Поскольку компилятор воспринимает 10 < value уже как выражение, он ждет, что после него будет запятая, чтоб принять его как аргумент для writeln. Использование скобок вокруг всего выражения не сработает, потому что в это время закрывающая скобка будет ожидаться в том же выражении.
  2. Группировка этого выражения как (10 < value) < 20 удалит ошибку при компиляции, потому что в этом случае первая часть будет рассчитана и далее его результат будет сравниваться с < 20

    Мы знаем, что значение логического выражения, такого как 10 < value, будет false или true. false и true принимают значения 0 и 1, соответственно, в целочисленных выражениях. Мы рассмотрим автоматическое преобразование типов в следующих главах. В результате, все это выражение будет эквивалентно либо 0 < 20 либо 1 < 20, которые оба вернут true.
  3. Выражение «больше чем минимальное значение и меньше чем максимальное», может быть запрограммировано следующим образом:
    writeln("Is between: ", (value > 10) && (value < 20));
  4. «Есть ли велосипеды для каждого» может быть запрограммировано как personCount <= bicycleCount или так bicycleCount >= personCount. Остальное в этом логическом выражении может быть сразу переведено в программный код из упражнения:
    writeln("We are going to the beach: ",
                ((distance < 10) && (bicycleCount >= personCount))
                ||
                ((personCount <= 5) && existsCar && existsLicense)
                );
    Обратите внимание на расположение данного оператора дизъюнкции (||) делает чтение удобнее, разделяя эти два основных предиката.
Tags:
Hubs:
+16
Comments 15
Comments Comments 15

Articles