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

  • Tutorial
Четвертая часть перевода D Programming Language Tutorial от Ali Çehreli.

Другие части:


Переменные


Конкретные понятия, которые представлены в программе, называются переменными. Значение вроде температура воздуха или более сложный объект, как двигатель машины, могут быть переменными в программе.

Каждая переменная имеет определенный тип и определенное значение. Большинство переменных также имеют имена, но некоторые являются анонимными.

В качестве примера переменной мы можем рассматривать концепцию числа учеников в школе. Так как число студентов является целым числом, int — подходящий тип, и studentCount будет достаточно описывающим именем.

Согласно синтаксическим правилам D переменная начинается с ее типа с последующим именем. Введение переменной в программу называется определением. Как только переменная определена, ее имя начинает представлять ее значение.


import std.stdio;
 
void main()
{
    // Определение переменной; это определение
    // указывает, что типом studentCount является int:
    int studentCount;
 
    // Название переменной становится ее значением:
    writeln("Присутствует ", studentCount, " учеников.");
}


Вывод этой программы:
Присутствует 0 учеников


Как видно из этой строки, значение studentCount равно 0. Согласно таблице фундаментальных типов из прошлой главы: начальное значение int равно 0.

Заметьте, что строка studentCount не появляется в выводе. Другими словами, программа не выводит «Присутствует studentCount учеников».

Значения переменных изменяются с помощью оператора =. Оператор = присваивает новые значения переменным, и по этой причине и называется оператором присвоения:

import std.stdio;
 
void main()
{
    int studentCount;
    writeln("Присутствует ", studentCount, " учеников.");
 
    // Присваивание значения 200 переменной studentCount:
    studentCount = 200;
    writeln("Теперь присутствует ", studentCount, " учеников.");
}


Присутствует 0 учеников.
Теперь присутствует 200 учеников.


Когда значение переменной известно в момент определения, значение переменной может быть присвоено одновременно с ее определением. Это важный принцип; это делает невозможным использование переменной до момента присвоения предназначенного ей значения:


import std.stdio;
 
void main()
{
    // Одноврменное определение и присвоение значения:
    int studentCount = 100;
 
    writeln("Присутствует ", studentCount, " учеников.");
}


Присутствует 100 учеников.


Упражнения


  • Определите две переменные для вывода «Я обменял 20 евро по курсу 2.11». Для значения с плавающей точкой используйте double.

    ... решение
    <--!
    import std.stdio;

    void main()
    {
    int amount = 20;
    double rate = 2.11;

    writeln(«Я обменял », amount,
    " евро по курсу ", rate);
    }
    -->
    import std.stdio;
     
    void main()
    {
        int amount = 20;
        double rate = 2.11;
     
        writeln("Я обменял  ", amount,
                " евро по курсу ", rate);
    }




Стандартные потоки ввода и вывода


До этого момента весь напечатанный вывод наших программ появлялся в окне консоли. Хотя это на самом деле часто используется многими программами, на самом деле символы выводятся в стандартные потоки вывода.

Стандартный вывод основан на символах; все, что печатается, сначала конвертируется в символьное представление и потом последовательно отправляется на вывод как символы. Например, целочисленное значение 100, которое мы выводили в прошлой главе не посылается на вывод как значение 100, а как три символа 1, 0 и 0.

Аналогично то, что мы обычно воспринимаем как клавиатура, на самом деле является стандартным потоком ввода программы, и он также основан на символах. Информация всегда поступает как символы для конвертацию в данные. Для примера целочисленное значение 42 на самом деле приходит через стандартный ввод как символы 4 и 2.

Эти преобразования производятся автоматически.

Это понятие последовательных символов называется символьным потоком. Так как стандартный поток ввода и стандартный поток вывода подходят под это описание, они являются символьными потоками.

Названия стандартных потоков ввода и вывода в D — stdin и stdout соответственно.

Операции над этими потоками обычно требуют имя потока, точку и название операции; например stream.operation(). Так как stdin и stdout используются очень часто, по соглашению, стандартные операции над ними могут быть вызываны без необходимости указывания имени и точки, например operation().

writeln, которую мы использовали в предыдущих главах на самом деле является сокращением от stdout.writeln. Также write — сокращение от stdout.write. Соответственно программа «привет мир» может быть написана так:

import std.stdio;
 
void main()
{
    stdout.writeln("Hello world!");
}


Упражнения


  • Убедитесь, что stdout.write работает также, как write.
    ... решение


    import std.stdio;
     
    void main()
    {
        stdout.writeln(1, ",", 2);
    }




Чтение из стандартного потока ввода


Любые данные, которые были прочитаны программой, должны быть сперва сохранены в переменную. Например, программа, которая читает количество студентов из стандартного потока ввода, должна сохранить эту информацию в переменную. Типом этой конкретной переменной может быть int.

Как мы убедились в предыдущей главе, нет необходимости указывать stdout, когда выводим информацию, так как он предполагается. Далее, то, что выводится, указывается как аргумент. Поэтому write(studentCount) достаточно, чтобы вывести значение studentCount. Подведем итог:
поток:    stdout
операция: write
данные:   значение переменной studentCount
цель:     обычно, окно терминала


Обратная операции write — readf; она читает из стандартного потока ввода. Буква «f» в имени взята от «formatted», так как то, что читает эта функция, должно быть всегда представлено в определенном формате.

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

В случае чтения один кусочек головоломки все еще отсутствует: куда сохранить данные. Подведем итоги:
поток:    stdin
операция: readf
данные:   некоторая информация
цель:     ?


Расположение места, куда необходимо сохранить данные, указывается с помощью адреса переменной. Адресом переменной является точное положение в памяти компьютера, где хранится ее значение.

В D символ '&', напечатанный перед именем, является указанием адреса того, что представляет эта переменная. Например, адресом studentCount является &studentCount. Здесь, &studentCount может быть прочитано как «адрес studentCount», и это недостающий кусок, который заменяет знак вопроса выше:
поток:    stdin
операция: readf
данные:   некоторая информация
цель:     расположение переменной studentCount


Набор символа & перед именем означает взятие адреса того, что представляет это имя. Это понятие является основой ссылок и указателей, которые мы увидим в следующих главах.

Я отложу объяснение одной особенности использования readf; сейчас, примем правило о том, что первым аргументом readf должна быть строка "%s":
readf("%s", &studentCount);


Заметка: Как я объясняю ниже, в большинстве случаев в строке также должен присутствовать пробел: " %s".

"%s" указывает, что данные должны быть автоматически преобразованы таким способом, который подходит под тип переменной. Например, когда символы '4' и '2' читаются в переменную типа int, они должны быть преобразованы в целочисленное значение 42.

Программа ниже просит пользователя ввести число учеников. Необходимо нажать клавишу Enter после окончания ввода:

import std.stdio;
 
void main()
{
    write("Сколько присутствует учеников? ");
 
    /*
     * Объявление переменной, которая будет использована для
     * хранения информации, которая читается из потока ввода
     */

    int studentCount;
 
    // Запись входных данных в эту переменную
    readf("%s", &studentCount);
 
    writeln("Понял: Присутствует ", studentCount, " учеников.");
}


Пропуск пробельных символов


Даже клавиша Enter, которую мы нажимаем после ввода данных, сохраняется как специальный код и помещается в поток stdin. Это помогает программам обнаруживать, была ли информация введена в одной или нескольких строчках.

Хотя это иногда полезно, такие специфические коды в большинстве случаев не важны для программы и должны быть удалены из ввода. Иначе они блокируют ввод и не дают читать другие данные.

Для демонстрации проблемы, давайте читать также число преподавателей из потока ввода:

import std.stdio;
 
void main()
{
    write("Сколько присутствует учеников? ");
    int studentCount;
    readf("%s", &studentCount);
 
    write("Сколько присутствует преподавателей? ");
    int teacherCount;
    readf("%s", &teacherCount);
 
    writeln("Понял: Присутствует ", studentCount, " учеников",
            " и", teacherCount, " преподавателей.");
}


К сожалению, теперь программа застряла при чтении второго int:
Сколько присутствует учеников?  100
Сколько присутствует преподавателей?  20
<- Программа зависает здесь


Хотя пользователь ввел число преподавателей равным 20, специальные коды, которые представляют клавишу Enter при чтении предыдущего значения 100, все еще находятся в потоке ввода и блокируют его. Символы, которые хранятся в потоке ввода аналогичны следующим:
100[EnterCode]20[EnterCode]


Первый символ [EnterCode] блокирует ввод.

Решением этой проблемы является добавление пробела перед %s для обозначения того, что код клавиши Enter, который может быть встречен перед чтением числа преподавателей, не важен: " %s". Пробелы в строке форматирования используются для чтения и игнорирования нуля или более невидимых символов, которые могут предшествовать вводу. Такие символы включают в себя: сам символ пробела, коды, которые представляют клавишу Enter, символ клавиши Tab и другие, называемые пробельными символами.

В общем случае можно использовать " %s" для любых данных, которые читаются из потока ввода. Программа выше работает как ожидается со следующими изменениями:

// ...
    readf(" %s", &studentCount);
// ...
    readf(" %s", &teacherCount);
// ...


Вывод:
Сколько присутствует учеников? 100
Сколько присутствует преподавателей? 20
Понял: Присутствует 100 учеников и 20 преподавателей.


Дополнительная информация


  • Строки, которые начинаются с // обозначают однострочные комментарии. Чтобы написать многострочный комментарий, заключайте строки в метки /* и */.

    Чтобы закомментировать другие комментарии, используйте /+ и +/:

        /+
         // Однострочный комментарий
     
         /*
           Комментарий, который занимает
           несколько строк
          */
     
          Блок комментария, который включает в себя другие комментарии
         +/

  • Большинство пробелов в исходном коде не имеют значения. Является хорошей практикой писать длинные выражения на нескольких строчках или добавлять дополнительные пробелы, чтобы сделать код более читаемым. Тем не менее, до тех пор, пока соблюдаются правила синтаксиса языка, программы могут быть написаны без каких-либо дополнительных пробелов:

    import std.stdio;void main(){writeln("Тяжело читаемо!");}

    Тяжело читать код, в котором столько же пробелов, сколько в этом.


Упражнения


  • Введите не числовые символы, когда программа ожидает целочисленное значение и пронаблюдайте, как программа работает некорректно.
    ... решение
    Когда символы не могут быть преобразованы в желаемый тип, stdin переводится в состояние непригодное к использованию. Например, ввод «abc», когда ожидается int, сделает stdin непригодным к использованию.
  • +14
  • 8,1k
  • 7
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

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

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

    +2
    Подача информации как для студентов или обучающихся первому языку программирования, но в случае D это, как минимум, рискованно!
    Хотя по моему скромному мнению все равно в неограниченное количество раз лучше, чем изучение того же Паскаля в школе.
      +1
      Чем Вам паскаль не угодил?
        +4
        Потому что его всё-таки трудно применять для реальных проектов, волей-неволей потом приходится переучиваться на что-нибудь типа Явы. Грустное это зрелище — программисты, по историческим причинам привыкшие к Дельфи и отказывающиеся переходить на другие языки.
      –2
      Хотелось бы почитать о многопоточности в этом языке. Внешне, от того же JavaScript отличается только статической типизацией.
      Не знаю, зачем она (статическая типизация) нужна в высокоуровневом языке прикладного характера.
        0
        Хотя бы затем, чтобы можно было делать автоматические безопасные рефакторинги в IDE. Если попробовать сравнить число поддерживаемых рефакторингов для Java и для Javascript в средах разработки от Jetbrains, то результат будет в пользу статически типизированных языков. Зачем делать работу, которую может за вас сделать компилятор?
          0
          Статическая типизация помогает находить ошибки во время компиляции, чем лучше продумана система типов, тем больше ошибок отсекается на этапе компиляции.
          +2
          Конкретные понятия, которые представлены в программе, называются переменными.


          Вот уж объяснили…

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

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