Готовый перевод Diving into D / Погружение в D: All

Внимание! Этот перевод, возможно, ещё не готов, так как модераторы установили для него статус «идёт перевод»

Вы знаете, с чего все начинается, так что без лишних слов:

import std.stdio;
void main() {
writeln("Привет, мир!");
}

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

(Выражения высокого уровня используют(?) глобальные переменные, что быстро усложняет сопровождение программы по мере ее роста; D также предлагает способы выполнения кода вне функции main(), но в более структурированной манере.)

Если вы сторонник точности, вы с облегчением узнаете, что void main это эквивалент int main, который возвращает "успех" (код 0) операционной системе если выполнение завершено успешно.

Но не будем бежать вперед самих себя. Целью традиционной "Hello, world" программы является не обсуждение возможностей языка, а научиться писать и запускать программы, использующие этот язык.

Итак, после того как вы сохранили код, приведенный выше, в файл с именем, скажем, hello.d, запустите оболочку и напечатайте следующие команды:

$ dmd hello.d
$ ./hello
Hello, world!
$ _

где '$' означает ваше любимое приглашение командной строки. Если ваша операционная система поддерживает так называемую "shebang" нотацию для запуска скриптов, вы будете счастливы услышать, что что простое добавление строки:

#!/usr/bin/rdmd

в самое начало вашей hello.d программы сделает ее выполняемой напрямую. После того, как вы внесете данное изменение в программу, вы можете просто напечатать в командной строке:

$ chmod u+x hello.d
$ ./hello.d
Hello, world!
$ _

(Выполнить команду chmod нужно только один раз). Программа rdmd (часть установочного пакета D) достаточна умна, чтобы кешировать создаваемый ею исполняемый файл и таким образом, компиляция в действительности выполняется только после изменения программы, но не каждый раз, когда вы ее запускаете.

Впоследствии, небольшие программы в этой книге не будут указывать на эту деталь, оставляя на ваше усмотрение - использовать ли shebang нотацию '#!' или нет.

Программа начинается с директивы:

import std.stdio;

которая говорит компилятору, что нужно импортировать модуль называемый std.stdio и сделать его символы(?) доступными для использования. Import является родственников директивы C/C++ препроцессора #include, но по семантике ближе к import из Питона: нет никакого включения текста - только получение символьной таблицы. Каждый файл импортируется максимум один раз - сколько бы раз вы не указывали директиву import.

По почтенной традиции установленной C, программа на D состоит из набора деклараций расположенных в множестве файлов.

Декларации вводят, среди других вещей, типы, функции и данные.

Наша первая программа определяет функцию main(), которая не принимает аргументов и возвращает "ничего" - void.

При вызове, функция main() вызывает функцию writeln (которая, конечно же, была хитро определена в модуле std.stdio), передавая ей строку-константу.

Суффикс "ln" показывает, что writeln добавляет перевод строки к печатаемому тексту.

В следующих разделах мы бегло пробежимся по стране Ди. Небольшие, наглядные программы познакомят нас с основными концепциями языка.

В этом месте будет дана возможность больше почувствовать язык, чем прочитать сухие определения.

Если некоторые детали ввергают вас в ступор, спокойно пропускайте сложные фрагменты кода. Последующие главы разъяснят каждую часть языка в мельчайших деталях, после которых имеет смысл еще раз просмотреть эту главу более тщательно.
1.1 Числа и Выражения

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

/*
Compute heights in centimeters for a range of heights
expressed in feet and inches
Высоту, выраженную в футах и дюймах, переводит в
высоту в сантиметрах
*/
import std.stdio;

void main() {
// values unlikely to change soon
// величины, которые вряд ли изменятся в ближайшее время
immutable inchPerFoot = 12;
immutable cmPerInch = 2.54;

// loop'n write
// крутимся и пишем
foreach (feet; 5 .. 7) {
foreach (inches; 0 .. inchPerFoot) {
writeln(feet, "'", inches, "''\t",
(feet * inchPerFoot + inches) * cmPerInch);
}
}
}

Завершив выполнение, программа напечатает отличный список в две колонки:

5'0'' 152.4
5'1'' 154.94
5'2'' 157.48
...
6'10'' 208.28
6'11'' 210.82

Как и Java, C++, и C#, D поддерживает /*многострочные комментарии*/ и //однострочные комментарии (плюс пару других, к которым мы подойдем позже).

Еще одна интересная деталь это как наша маленькая программа вводит данные. Во-первых, существуют две констаны:

immutable inchperfoot = 12;
immutable cmperinch = 2.54;

Константы, которые никогда не изменяются вводятся ключевым словом immutable.

Константы, как и переменные, не обязаны иметь тип; реальный тип может быть выведен из величины, которой инициализируется данный символ(опять символ не привычном для нас значении).

В нашем случае, литерал 12 говорит компилятору, что inchperfoot является 32 битным целым (в D обозначается знакомым int); похожим образом литерал 2.54 определяет, что cmperinch это константа в виде 64 битного числа с плавающей запятой (тип double).

Продолжая дальше, мы заметим, что определения футов и дюймов выполнены тем же магическим способом - как переменные без явного указания типа. Это не делает программу менее безопасной, чем если бы типы были указаны явно:

immutable int inchperfoot = 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 (наибольший тип, который поддерживается процессором машины. Например, на машинах Intel, типом real будет так называемый IEEE 754 двойной расширенный 79-битный формат.)

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

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

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

На практике компилятор гарантирует, что будет использована точность не хуже заданой в коде (т.е. float, double или real).

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

Возвращаясь к целым числам, литерал вроде "42" может быть присвоен любому числовому типу. При этом компилятор проверит, что вмещающий тип будет достаточен для этого числа. Так что объявление:

immutable byte inchperfoot = 12;

будет равноценно объявлению без типа byte, потому что 12 прекрасно уместится как в 8, так и в 32 битах. По-умолчанию, если тип должен быть выведен из числа (как в примере), это будет int для целых и double для вещественных.

Используя эти типы, вы можете построить любые выражения, используя арифметические операторы и функции. Операторы и их приоритет очень похожи на те, которые существуют в родственных Ди языках: '+', '-', '*', '/', и '%' для арифметики, '==', '!=', '<', '>', '<=', '>=' для сравнений, fun(argument1, argument2) для вызова функций и т.д.

Возвращаясь к нашей программе пересчёта дюймов, есть два момента, заслуживающих внимания в вызове writeln. Первый - что она вызвана с пятью аргументами (вместо одного как в программе "Привет, мир!").

Как и функции вывода в других языках (writeln в Паскале, printf в Си, cout в Си++), writeln языка Ди принимает произвольное число аргументов (это "variadic функция").

Однако в Ди программист может: сам определять variadic-функцию (в отличии от Паскаля), которая при этом будет типобезопасная (в отличии от Си), и не потребует ненужного переопределения операторов (в отличии от С++).

Второй момент - что в нашем вызове writeln неуклюже перемешаны как данные, так и их форматирование. Хорошей практикой считается их разделять, поэтому давайте используем функцию writefln, которая это поддерживает:

writefln("%s'%s''\t%s", feet, inches, (feet * inchperfoot + inches) * cmperinch);

Новый, упорядоченный вызов выводит то же самое, с тем отличием, что первый аргумент writefln полностью описывает формат вывода. Знак '%' начинает форматирующую последовательность, как в printf из Си. Например, '%i' для вывода целых, '%f' - для вещественных чисел и '%s' - для строк.

Если вы раньше использовали printf, вам это знакомо за одним исключением: мы же печатали целые и вещественные числа - как же они отформатировались при помощи '%s', если это формат для строк? Ответ прост.

Ди даёт writefln доступ к типам своих аргументов, что выливается в два преимущества: (1) Значение '%s' может быть расценено как "какое угодно представление аргумента в виде строки"

и (2) если тип аргумента не совпадает с форматом вывода, вы получите чёткую ошибку, а не бестолковое поведение как у printf (это ещё не говоря о тех дырах в защите, когда используются форматы для printf из непроверенных источников).

Внимание! Этот перевод, возможно, ещё не готов.
Его статус: идёт перевод

Переведено на Нотабеноиде
http://notabenoid.org/book/35620/126372

Переводчики: drug007, thornik, jauthu, Salabar

Настройки

Готово:

100.00% КП = 1.1