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

Автор оригинала: Ali Çehreli
  • Перевод
  • Tutorial
Пятая часть перевода 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);
  • Оператор строго меньше (<) отвечает на вопрос «Это меньше этого?» (или «При сортировке это будет выше?»)
    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;
    }
    Введите различные значения и протестируйте это логическое выражение, которое Вы написали, работает правильно.
… решение
  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)
                );
    Обратите внимание на расположение данного оператора дизъюнкции (||) делает чтение удобнее, разделяя эти два основных предиката.
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 15

    +1
    Было бы неплохо увидеть статью «зачем учить очередного убийцу С++», ну да ладно :)
      +3
      Это не очередной, это долго, серьезно и упорно взращиваемый авторитетными людьми «убийца». :)
      Скоро будет от меня статья с практическим применением этого языка.
        0
        с интересом почитаю :)
          0
          В общем решил и я внести свой вклад в популяризацию D habrahabr.ru/post/246623/.
          Тем более, что статья с примером практического применения будет уж очень узкоспецифичной — написание расширения на Python с помощью D.
        –2
        Достаточно посмотреть репозитории на github и сразу становится очевидно, какой язык хорошо бы посмотреть ;)
          0
          вы мне напомнили того автора, у которого вся его статья про «супер-пупер продукт» сводилась к ссылке на гитаб и коротенькому описанию)
            0
            Тут слегка другой случай. Статистика по репозиториям довольно красноречива конкретно для D, особенно по сравнению с другими языками, в некоторой степени являющимися для D альтернативой к изучению.
            Мне это помогло выбрать язык для изучения, вот и делюсь статистикой.
              0
              Не всегда то что модно — удобно.
              С языком D конечно вышел казус, уж очень долго его рожали, и родили не с первой попытки. Но в этом наверно есть и плюсы, имеется основание полагать, что он более продуман, чем скороспелые гибриды «хорошо забытого старого» с мощной корпорацией. Эта шутка :)
        +3
        Но это неработает в обратном направлении: эти строки «false» и «true» не автоматически читаются как этизначения false и true.

        Читать невозможно. Пожалуйста, вычитайте статью перед тем, как ее публиковать.
          0
          Если Вы по поводу пробелов, то прошу прощения, почему-то перенос (\n) не считается пробелом в редакторе хабра. А если насчет перевода, то эта строка для меня — проблема.

          В оригинале это так
          All of the bool values above are automatically printed as «false» or «true». It is not the case in the opposite direction: the strings «false» and «true» are not automatically read as the values false and true.

          Я не знаю как удобоваримей перевести «It is not the case in the opposite direction». Если есть предложения, то поделитесь)
            0
            Пробелы — это мелочь. У вас знаки препинания расставлены «как получилось», а в некоторых предложениях слова вообще никак не согласуются между собой:
            Этот порядок, в котором выражения вычисляются может быть заключен в скобки для того, чтоб их сгруппировать.
            Поскольку в этих оператор конъюнкции && приоритет выше, чем оператор дизъюнкции ||, написание выражения без скобок не будет расчитано, как предполагается.

            Чтобы избежать этого, перечитайте всю статью перед публикацией от и до, не пропуская ни одного слова.

            По поводу вашего предложения, я бы перевел его так:
            Все указанные выше булевские значения автоматически выводятся в виде строк «false» или «true». Алгоритм работает только в одну сторону — считывание строк «true» и «false» вызовет приведение их к соответствующим значениям булевского типа.
              0
              Хотелось бы все таки дискаса по языку, а не по знакам препинания, которые можно и в личке обсудить. Согласен, важная тема, но в личке.
                0
                Ну, тут не тот случай, по-моему, когда можно о чем-то дискутировать кроме качества перевода. Слишком уж все просто. Единственное, что я бы еще добавил (в качестве примечания переводчика), коль уж об этом зашла речь, — это алгоритм работы conv:string->bool. К чему будут приведены строки «TRUE», «0», «1000», например?
                  0
                  Понятно дело, что статья большей частью идет как ознакомления с языком и только. И обсуждать особо по ней нечего.
                  На на мой вкус, раз уж это хабр, то популизирующие статьи лучше писать не с переводов хелпа и хелоуворда. А показать фишки языка. Но проблема в том, что написать такую статью сложнее даже, чем статью о практическом применении языка.

                  Насчет строк «true», «false» — да забавно, зачем так там делается акцент что это не true и false и муссируется дальше, статья вроде не для нулевых совсем. Может быть трудности перевода?
              0
              Так же у вас слишком много (неуместных) повторов слов «это», «этот». По-русски так не говорят. Заменяйте на «он», «она», смотря о чём речь, а в большинстве случаев их вообще можно выкинуть из предложения.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое