1.1 и 1.2

This commit is contained in:
Alexander Zhirov 2023-01-22 00:25:44 +03:00
parent ea3d7c4485
commit 2b58cd0390
1 changed files with 117 additions and 4 deletions

View File

@ -1,7 +1,7 @@
# Знакомство с языком D # 1. Знакомство с языком D
- [1.1. Числа и выражения]() - [1.1. Числа и выражения](#11-числа-и-выражения)
- [1.2. Инструкции]() - [1.2. Инструкции](#12-инструкции)
- [1.3. Основы работы с функциями]() - [1.3. Основы работы с функциями]()
- 1.4. Массивы и ассоциативные массивы - 1.4. Массивы и ассоциативные массивы
- [1.4.1. Работа со словарем]() - [1.4.1. Работа со словарем]()
@ -55,7 +55,7 @@ $ _
Программа `hello.d` начинается с инструкции Программа `hello.d` начинается с инструкции
```sh ```d
import std.stdio; import std.stdio;
``` ```
@ -65,4 +65,117 @@ import std.stdio;
Следующие разделы это стремительная поездка по Дибургу. Небольшие показательные программы дают общее представление о языке. Основная цель повествования на данном этапе обрисовать общую картину, а не дать ряд педантичных определений. Позже все аспекты языка будут рассмотрены с должным вниманием в деталях. Следующие разделы это стремительная поездка по Дибургу. Небольшие показательные программы дают общее представление о языке. Основная цель повествования на данном этапе обрисовать общую картину, а не дать ряд педантичных определений. Позже все аспекты языка будут рассмотрены с должным вниманием в деталях.
## 1.1. Числа и выражения
Интересовались ли вы когда-нибудь ростом иностранцев? Давайте напишем простую программу, которая переводит наиболее распространенные значения роста в футах и дюймах в сантиметры.
```d
/*
Рассчитать значения роста в сантиметрах для заданного диапазона значений в футах и дюймах
*/
import std.stdio;
void main()
{
// Значения, которые никогда не изменятся
immutable inchesPerFoot = 12;
immutable cmPerInch = 2.54;
// Перебираем и пишем
foreach (feet; 5 .. 7)
{
foreach (inches; 0 .. inchesPerFoot)
{
writeln(feet, "'", inches, "''\t", (feet * inchesPerFoot + inches) * cmPerInch);
}
}
}
```
В результате выполнения программы будет напечатан аккуратный список в две колонки:
```sh
5'0'' 152.4
5'1'' 154.94
5'2'' 157.48
...
6'10'' 208.28
6'11'' 210.82
```
Инструкция `foreach (feet; 5..7) {...}` это цикл, где определена целочисленная переменная `feet`, с которой последовательно связываются значения 5 и 6 (значение 7 она не принимает, так как интервал открыт справа). Как и Java, C++ и C#, D поддерживает `/* многострочные комментарии */` и `// однострочные комментарии` (и, кроме того, документирующие комментарии, о которых позже). Еще одна интересная деталь нашей маленькой программы способ объявления данных. Во-первых, введены две константы:
```d
immutable inchesPerFoot = 12;
immutable cmPerInch = 2.54;
```
Константы, значения которых никогда не изменятся, определяются с помощью ключевого слова `immutable`. Как и переменные, константы не требуют явного задания типа: тип задается значением, которым инициализируется константа или переменная. В данном случае литерал 12 говорит компилятору о том, что `inchesPerFoot` это целочисленная константа (обозначается в D с помощью знакомого `int`); точно так же литерал `2.54` заставляет `cmPerInch` стать константой с плавающей запятой (типа `double`). Далее мы обнаруживаем те же магические способности у определений `feet` и `inches`: они выглядят как «обычные» переменные, но безо всяких «украшений», свидетельствующих о каком-либо типе. Это не делает программу менее безопасной по сравнению с той, где типы переменных и констант заданы явно:
```d
immutable int inchesPerFoot = 12;
immutable double cmPerInch = 2.54;
...
foreach (int feet; 5 .. 7)
{
...
}
```
и так далее только меньше лишнего. Компилятор разрешает не указывать тип явно только в случае, когда можно недвусмысленно определить его по контексту. Раз уж зашла речь о типах, давайте остановимся и посмотрим, какие числовые типы нам доступны.
Целые типы со знаком в порядке возрастания размера: `byte`, `short`, `int` и `long`, занимающие 8, 16, 32 и 64 бита соответственно. У каждого из этих типов есть «двойник» без знака того же размера, названный в соответствии с простым правилом: `ubyte`, `ushort`, `uint` и `ulong`. (Здесь нет модификатора `unsigned`, как в C). Типы с плавающей запятой: `float` (32-битное число одинарной точности в формате IEEE 754), `double` (64-битное в формате IEEE 754) и `real` (занимает столько, сколько позволяют регистры, предназначенные для хранения чисел с плавающей запятой, но не меньше 64 бит; например, на компьютерах фирмы Intel `real` это так называемое расширенное 79-битное число двойной точности в формате IEEE 754).
Вернемся к нашим целым числам. Литералы, такие как `42`, подходят под определение любого числового типа, но заметим, что компилятор проверяет, достаточно ли вместителен «целевой» тип для этого значения. Поэтому определение
```d
immutable byte inchesPerFoot = 12;
```
ничем не хуже аналогичного без `byte`, поскольку `12` можно с таким же успехом представить 8 битами, а не 32. По умолчанию, если вывод о «целевом» типе делается по числу (как в программе-примере), целочисленные константы «воспринимаются» как `int`, а дробные как `double`.
Вы можете построить множество выражений на D, используя эти типы, арифметические операторы и функции. Операторы и их приоритеты сходны с теми, что можно найти в языках-собратьях D: `+`, `-`, `*`, `/` и `%` для базовых арифметических операций, `==`, `!=`, `<`, `>`, `<=`, `>=` для сравнений, `fun(argument1, argument2)` для вызовов функций и т.д.
Вернемся к нашей программе перевода дюймов в сантиметры и отметим две достойные внимания детали вызова функции `writeln`. Первая: во `writeln` передаются 5 аргументов (а не один, как в той программе, что установила контакт между вами и миром D). Функция `writeln` очень похожа на средства ввода-вывода, встречающиеся в языках Паскаль (`writeln`), C (`printf`) и C++ (`cout`). Все они (включая `writeln` из D) принимают переменное число аргументов (так называемые функции с переменным числом аргументов). Однако в D пользователи могут определять собственные функции с переменным числом аргументов (чего нет в Паскале), которые всегда типизированы (в отличие от C), без излишнего переопределения операторов (как это сделано в С++). Вторая деталь: наш вызов `writeln` неуклюже сваливает в кучу информацию о форматировании и форматируемые данные. Обычно желательно отделять данные от представления. Поэтому давайте используем специальную функцию `writefln`, осуществляющую форматированный вывод:
```d
writefln("%s'%s''\t%s", feet, inches, (feet * inchesPerFoot + inches) * cmPerInch);
```
По-новому организованный вызов дает тот же вывод, но первый аргумент функции `writefln` полностью описывает формат представления. Со знака `%` начинаются спецификаторы формата (по аналогии с функцией `printf` из C): например `%d` для целых чисел, `%f` для чисел с плавающей запятой и `%s` для строк.
Если вы использовали `printf` прежде, то могли бы почувствовать себя как дома, когда б не маленькая особенность: мы ведь выводим значения переменных типа `int` и `double` как же получилось, что и те и другие описаны с помощью спецификатора `%s`, обычно применяемого для вывода строк? Ответ прост. Средства D для работы с переменным количеством аргументов дают `writefln` доступ к информации об исходных типах переданных аргументов. Благодаря такому подходу программа получает ряд преимуществ: 1) значение `%s` может быть расширено до «строкового представления по умолчанию для типа переданного аргумента» и 2) если не удалось сопоставить спецификатор формата с типами переданных аргументов, вы получите ошибку в чистом виде, а не загадочное поведение, присущее вызовам `printf` с неверно заданным форматом (не говоря уже о подрыве безопасности, возможном при вызове `printf` с непроверяемыми заранее форматирующими строками).
## 1.2. Инструкции
В языке D, как и в других родственных ему языках, любое выражение, после которого стоит точка с запятой, это инструкция (например в программе «Hello, world!» сразу после вызова `writeln` есть ;). Действие инструкции сводится к вычислению выражения.
D член семейства с фигурными скобками и с блочной областью видимости». Это означает, что вы можете объединять несколько команд в одну, помещая их в `{` и `}`, что порой обязательно, например при желании сделать сразу несколько вещей в цикле `foreach`. В случае единственной команды вы вправе смело опустить фигурные скобки. На самом деле, весь наш двойной цикл, вычисляющий значения роста, можно переписать так:
```d
import std.stdio;
void main()
{
// Значения, которые никогда не изменятся
immutable inchesPerFoot = 12;
immutable cmPerInch = 2.54;
// Перебираем и пишем
foreach (feet; 5 .. 7)
foreach (inches; 0 .. inchesPerFoot)
writeln(feet, "'", inches, "''\t", (feet * inchesPerFoot + inches) * cmPerInch);
}
```
У пропуска фигурных скобок для одиночных инструкций есть как преимущество (более короткий код), так и недостаток редактирование кода становится более утомительным (в процессе отладки придется повозиться с инструкциями, то добавляя, то удаляя скобки). Когда речь заходит о правилах расстановки отступов и фигурных скобок, мнения сильно расходятся. На самом деле, пока вы последовательны в своем выборе, все это не так важно, как может показаться. В качестве доказательства: стиль, предлагаемый в этой книге (обязательное заключение в операторные скобки даже одиночных инструкций, открывающая скобка на одной строке с соответствующим оператором, закрывающие скобки на отдельных строках), по типографским причинам отличается от реально применяемого автором. А раз он мог спокойно это пережить, не превратившись в оборотня, то и любой сможет.
Благодаря языку Python стал популярен иной способ отражения блочной структуры программы с помощью отступов (чудесное воплощение принципа «форма соответствует содержанию»). Для программистов на других языках утверждение, что пробел имеет значение, всего лишь нелепая фраза, но для тех, кто пишет на Python, это зарок. D обычно игнорирует пробелы, но он разработан с прицелом на легкость синтаксического разбора (т. е. чтобы при разборе не приходилось выяснять значения символов). А это подразумевает, что в рамках скромного «комнатного» проекта можно реализовать простой препроцессор, позволяющий использовать для выделения блоков инструкций отступы (как в Python) без каких-либо неудобств во время компиляции, исполнения и отладки программ.
Кроме того, вам должна быть хорошо знакома инструкция if:
```d
if (‹выражение›) инструкция1 else инструкция2
```
Чисто теоретический вывод, известный как принцип структурного программирования, гласит, что все алгоритмы можно реализовать с помощью составных инструкций, `if`-проверок и циклов а-ля `for` и `foreach`. Разумеется, любой адекватный язык (как и D) предлагает гораздо больше, но мы пока постановим, что с нас довольно и этих инструкций, и двинемся дальше.
[^1]: «Shebang» (от shell bang: shell консоль, bang восклицательный знак), или «shabang» (# sharp) обозначение пути к компилятору или интерпретатору в виде `#!/путь/к/программе`. *Прим. пер.* [^1]: «Shebang» (от shell bang: shell консоль, bang восклицательный знак), или «shabang» (# sharp) обозначение пути к компилятору или интерпретатору в виде `#!/путь/к/программе`. *Прим. пер.*