diff options
| -rw-r--r-- | ru-ru/d-ru.html.markdown | 753 | 
1 files changed, 753 insertions, 0 deletions
| diff --git a/ru-ru/d-ru.html.markdown b/ru-ru/d-ru.html.markdown new file mode 100644 index 00000000..8f4233fd --- /dev/null +++ b/ru-ru/d-ru.html.markdown @@ -0,0 +1,753 @@ +--- +language: d +filename: learnd-ru.d +contributors: +    - ["Anton Pastukhov", "http://dprogramming.ru/"] +    - ["Robert Brights-Gray", "http://lhs-blog.info/"] +    - ["Andre Polykanine", "http://oire.me/"] +lang: ru-ru +--- +D - современный компилируемый язык общего назначения с Си-подобным синтаксисом, +который сочетает удобство, продуманный дизайн и высокую производительность. +D - это С++, сделанный правильно. + +```d +// Welcome to D! Это однострочный комментарий + +/* многострочный +   комментарий  */ + +/+ +    // вложенные комментарии + +    /* еще вложенные +       комментарии */ + +    /+ +        // мало уровней вложенности? Их может быть сколько угодно. +    +/ ++/ + +/* +    Имя модуля. Каждый файл с исходным кодом на D — модуль. +    Если имя не указано явно, то предполагается, что оно совпадает с именем +    файла. Например, для файла "test.d" имя модуля будет "test", если явно +    не указать другое + */ +module app; + +// импорт модуля. Std — пространство имен стандартной библиотеки (Phobos) +import std.stdio; + +// можно импортировать только нужные части, не обязательно модуль целиком +import std.exception : enforce; + +// точка входа в программу — функция main, аналогично C/C++ +void main() +{ +    writeln("Hello, world!"); +} + + + +/*** типы и переменные ***/ + +int a; // объявление переменной типа int (32 бита) +float b = 12.34; // тип с плавающей точкой +double c = 56.78; // тип с плавающей точкой (64 бита) + +/* +    Численные типы в D, за исключением типов с плавающей точкой и типов +    комплексных чисел, могут быть беззнаковыми. +    В этом случае название типа начинается с префикса "u" +*/ +uint d = 10; ulong e = 11; +bool b = true; // логический тип +char d = 'd';  // UTF-символ, 8 бит. D поддерживает UTF "из коробки" +wchar e = 'é';   // символ UTF-16 +dchar f;       // и даже UTF-32, если он вам зачем-то понадобится + +string s = "для строк есть отдельный тип, это не просто массив char-ов из Си"; +wstring ws = "поскольку у нас есть wchar, должен быть и  wstring"; +dstring ds = "...и dstring, конечно"; + +string кириллица = "Имена переменных должны быть в Unicode, но не обязательно на латинице."; + +typeof(a) b = 6; // typeof возвращает тип своего выражения. +                 // В результате, b имеет такой же тип, как и a + +// Тип переменной, помеченной ключевым словом auto, +// присваивается компилятором исходя из значения этой переменной +auto x = 1;              // Например, тип этой переменной будет int. +auto y = 1.1;            // этой — double +auto z = "Zed is dead!"; // а этой — string + +int[3] arr = [1, 2, 3]; // простой одномерный массив с фиксированным размером +int[] arr2 = [1, 2, 3, 4]; // динамический массив +int[string] aa = ["key1": 5, "key2": 6]; // ассоциативный массив + +/* +   Строки и массивы в D — встроенные типы. Для их использования не нужно +   подключать ни внешние, ни даже стандартную библиотеку, хотя в последней +   есть множество дополнительных инструментов для работы с ними. + */ +immutable int ia = 10;  // неизменяемый тип, +                        // обозначается ключевым словом immutable +ia += 1;                // — вызовет ошибку на этапе компиляции + +// перечислимый (enumerable) тип, +// более правильный способ работы с константами в D +enum myConsts = { Const1, Const2, Const3 }; + +// свойства типов +writeln("Имя типа               : ", int.stringof); // int +writeln("Размер в байтах        : ", int.sizeof);   // 4 +writeln("Минимальное значение   : ", int.min);      // -2147483648 +writeln("Максимальное значение : ", int.max);      // 2147483647 +writeln("Начальное значение     : ", int.init);     // 0. Это значение, +                                                    // присвоенное по умолчанию + +// На самом деле типов в D больше, но все мы здесь описывать не будем, +// иначе не уложимся в Y минут. + + + +/*** Приведение типов ***/ + +// to!(имя типа)(выражение) - для большинства конверсий +import std.conv : to; // функция "to" - часть стандартной библиотеки, а не языка +double d = -1.75; +short s = to!short(d); // s = -1 + +/* +   cast - если вы знаете, что делаете. Кроме того, это единственный способ +   преобразования типов-указателей в "обычные" и наоборот +*/ +void* v; +int* p = cast(int*)v; + +// Для собственного удобства можно создавать псевдонимы +// для различных встроенных объектов +alias int newInt; // теперь можно обращаться к newInt так, как будто бы это int +newInt a = 5; + +alias newInt = int; // так тоже допустимо +alias uint[2] pair; // дать псевдоним можно даже сложным структурам данных + + + +/*** Операторы ***/ + +int x = 10; // присваивание +x = x + 1;  // 11 +x -= 2;     // 9 +x++;        // 10 +++x;        // 11 +x *= 2;     // 22 +x /= 2;     // 11 +x = x ^^ 2; // 121 (возведение в степень) +x ^^= 2;    // 1331 (то же самое) + +string str1 = "Hello"; +string str2 = ", world!"; +string hw = str1 ~ str2; // Конкатенация строк + +int[] arr = [1, 2, 3]; +arr ~= 4; // [1, 2, 3, 4] - добавление элемента в конец массива + + + +/*** Логика и сравнения ***/ + +int x = 0; int y = 1; + +x == y;         // false +x > y;          // false +x < y;          // true +x >= y;         // false +x != y;         // true. ! — логическое "не" +x > 0 || x < 1; // true. || — логическое "или" +x > 0 && x < 1; // false && — логическое "и" +x ^ y           // true; ^ - xor (исключающее "или") + +// Тернарный оператор +auto y = (x > 10) ? 1 : 0; // если x больше 10, то y равен 1, +                           // в противном случае y равен нулю + + +/***  Управляющие конструкции  ***/ + +// if - абсолютно привычен +if (a == 1) { +    // .. +} else if (a == 2) { +    // .. +} else { +    // .. +} + +// switch +switch (a) { +    case 1: +        // делаем что-нибудь +        break; +    case 2: +        // делаем что-нибудь другое +        break; +    case 3: +        // делаем что-нибудь еще +        break; +    default: +        // default обязателен, без него будет ошибка компиляции +        break; +} + +// в D есть констукция "final switch". Она не может содержать секцию "defaul" +// и применяется, когда все перечисляемые в switch варианты должны быть +// обработаны явным образом + +int dieValue = 1; +final switch (dieValue) { +    case 1: +        writeln("You won"); +        break; + +    case 2, 3, 4, 5: +        writeln("It's a draw"); +        break; + +    case 6: +        writeln("I won"); +        break; +} + +// while +while (a > 10) { +    // .. +    if (number == 42) { +        break; +    } +} + +while (true) { +    // бесконечный цикл +} + +// do-while +do { +    // .. +} while (a == 10); + +// for +for (int number = 1; number < 11; ++number) { +    writeln(number); // все абсолютно стандартно +} + +for ( ; ; ) { +    // секции могут быть пустыми. Это бесконечный цикл в стиле Си +} + +// foreach - универсальный и самый "правильный" цикл в D +foreach (element; array) { +    writeln(element); // для простых массивов +} + +foreach (key, val; aa) { +    writeln(key, ": ", val); // для ассоциативных массивов +} + +foreach (c; "hello") { +    writeln(c); // hello. Поскольку строки - это вариант массива, +                // foreach применим и к ним +} + +foreach (number; 10..15) { +    writeln(number); // численные интервалы можно указывать явным образом +                     // этот цикл выведет значения с 10 по 14, но не 15, +                     // поскольку диапазон не включает в себя верхнюю границу +} + +// foreach_reverse - в обратную сторону +auto container = [1, 2, 3]; +foreach_reverse (element; container) { +    writefln("%s ", element); // 3, 2, 1 +} + +// foreach в массивах и им подобных структурах не меняет сами структуры +int[] a = [1, 2 ,3 ,4 ,5]; +foreach (elem; array) { +    elem *= 2; // сам массив останется неизменным +} + +writeln(a); // вывод: [1, 2, 3, 4, 5] Т.е изменений нет + +// добавление ref приведет к тому, что массив будет изменяться +foreach (ref elem; array) { +    elem *= 2; +} + +writeln(a); // [2, 4, 6, 8, 10] + +// foreach умеет рассчитывать индексы элементов +int[] a = [1, 2, 3, 4, 5]; +foreach (ind, elem; array) { +    writeln(ind, " ", elem); // через ind - доступен индекс элемента, +                             // а через elem - сам элемент +} + + + +/*** Функции ***/ + +test(42); // Что, вот так сразу? Разве мы где-то уже объявили эту функцию? + +// Нет, вот она. Это не Си, здесь объявление функции не обязательно должно быть +// до первого вызова +int test(int argument) { +    return argument * 2; +} + + +// В D используется единый синтаксис вызова функций +// (UFCS, Uniform Function Call Syntax), поэтому так тоже можно: +int var = 42.test(); + +// и даже так, если у функции нет аргументов: +int var2 = 42.test; + +// можно выстраивать цепочки: +int var3 = 42.test.test; + +/* +    Аргументы в функцию передаются по значению (т.е. функция работает не с +    оригинальными значениями, переданными ей, а с их локальными копиями. +    Исключение составляют объекты классов, которые передаются по ссылке. +    Кроме того, любой параметр можно передать в функцию по ссылке с помощью +    ключевого слова "ref" +*/ +int var = 10; + +void fn1(int arg) { +    arg += 1; +} + +void fn2(ref int arg) { +    arg += 1; +} + +fn1(var); // var все еще = 10 +fn2(var); // теперь var = 11 + +// Возвращаемое значение тоже может быть auto, +// если его можно "угадать" из контекста +auto add(int x, int y) { +    return x + y; +} + +auto z = add(x, y); // тип int - компилятор вывел его автоматически + +// Значения аргументов по умолчанию +float linearFunction(float k, float x, float b = 1) +{ +    return k * x + b; +} + +auto linear1 = linearFunction(0.5, 2, 3); // все аргументы используются +auto linear2 = linearFunction(0.5, 2);    // один аргумент пропущен, но в функции +                                          // он все равно использован и равен 1 + +// допускается описание вложенных функций +float quarter(float x) { +    float doubled(float y) { +        return y * y; +    } + +    return doubled(doubled(x)); +} + +// функции с переменным числом аргументов +int sum(int[] a...) +{ +    int s = 0; +    foreach (elem; a) { +        s += elem; +    } +    return s; +} + +auto sum1 = sum(1); +auto sum2 = sum(1,2,3,4); + +/* +   модификатор "in" перед аргументами функций говорит о том, что функция имеет +    право их только просматривать. При попытке модификации такого аргумента +    внутри функции - получите ошибку +*/ +float printFloat(in float a) +{ +    writeln(a); +} +printFloat(a); // использование таких функций - самое обычное + +// модификатор "out" позволяет вернуть из функции несколько результатов +// без посредства глобальных переменных или массивов +uint remMod(uint a, uint b, out uint modulus) +{ +    uint remainder = a / b; +    modulus = a % b; +    return remainder; +} + +uint modulus;                   // пока в этой переменной ноль +uint rem = remMod(5, 2, modulus); // наша "хитрая" функция, и теперь +                                // в modulus - остаток от деления +writeln(rem, " ", modulus);     // вывод: 2 1 + + + +/*** Структуры, классы, базовое ООП ***/ + +// Объявление структуры. Структуры почти как в Си +struct MyStruct { +    int a; +    float b; + +    void multiply() { +        return a * b; +    } +} + +MyStruct str1; // Объявление переменной с типом MyStruct +str1.a = 10;   // Обращение к полю +str1.b = 20; +auto result = str1.multiply(); +MyStruct str2 = {4, 8}          // Объявление + инициализация  в стиле Си +auto str3 = MyStruct(5, 10);    // Объявление + инициализация  в стиле D + + +// области видимости полей и методов - 3 способа задания +struct MyStruct2 { +    public int a; + +    private: +        float b; +        bool c; + +    protected { +        float multiply() { +            return a * b; +        } +    } +    /* +        в дополнение к знакомым public, private и protected, в D есть еще +        область видимости "package". Поля и методы с этим атрибутом будут +        доступны изо всех модулей, включенных в "пакет" (package), но не +        за его пределами. package - это "папка", в которой может храниться +        несколько модулей. Например, в "import.std.stdio", "std" - это +        package, в котором есть модуль stdio (и еще множество других) +    */ +    package: +        string d; + +    /* помимо этого, имеется еще один модификатор - export, который позволяет +    использовать объявленный с ним идентификатор даже вне самой программы ! +    */ +    export: +         string description; +} + +// Конструкторы и деструкторы +struct MyStruct3 { +    this() { // конструктор. Для структур его не обязательно указывать явно, +             // в этом случае пустой конструктор добавляется компилятором +        writeln("Hello, world!"); +    } + + +    // а вот это конструкция - одна из интересных идиом и представляет собой +    // конструктор копирования, т.е конструктор, возвращающий копию структуры. +    // Работает только в структурах. +    this(this) +    { +        return this; +    } + +    ~this() { // деструктор, также необязателен +        writeln("Awww!"); +    } +} + +// Объявление простейшего класса +class MyClass { +    int a;  // в D по умолчанию данные-члены являются public +    float b; +} + +auto mc = new MyClass(); // ...и создание его экземпляра +auto mc2 = new MyClass; // ... тоже сработает + +// Конструктор +class MyClass2 { +    int a; +    float b; + +    this(int a, float b) { +        this.a = a; // ключевое слово "this" - ссылка на объект класса +        this.b = b; +    } +} + +auto mc2 = new MyClass2(1, 2.3); + +// Классы могут быть вложенными +class Outer +{ +    int m; + +    class Inner +    { +        int foo() +        { +            return m; // можно обращаться к полям "внешнего" класса +        } +    } +} + +// наследование +class Base { +    int a = 1; +    float b = 2.34; + + +    // это статический метод, т.е метод который можно вызывать, обращаясь +    // к классу напрямую, а не через создание экземпляра объекта +    static void multiply(int x, int y) +    { +        writeln(x * y); +    } +} + +Base.multiply(2, 5); // используем статический метод. Результат: 10 + +class Derived : Base { +    string c = "Поле класса - наследника"; + + +    // override означает то, что наследник предоставит свою реализацию метода, +    // переопределив метод базового класса +    override static void multiply(int x, int y) +    { +        super.multiply(x, y);  // super - это ссылка на класс-предок, или базовый класс +        writeln(x * y * 2); +    } +} + +auto mc3 = new Derived(); +writeln(mc3.a); // 1 +writeln(mc3.b); // 2.34 +writeln(mc3.c); // Поле класса - наследника + +// Финальный класс, наследовать от него нельзя +// кроме того, модификатор final работает не только для классов, но и для методов +// и даже для модулей ! +final class FC { +    int a; +} + +class Derived : FC { // это вызовет ошибку +    float b; +} + +// Абстрактный класс не может быть истанциирован, но может иметь наследников +abstract class AC { +    int a; +} + +auto ac = new AC(); // это вызовет ошибку + +class Implementation : AC { +    float b; + +     // final перед методом нефинального класса означает запрет возможности +     // переопределения метода +    final void test() +    { +       writeln("test passed !"); +    } +} + +auto impl = new Implementation(); // ОК + + + +/*** Примеси (mixins) ***/ + +// В D можно вставлять код как строку, если эта строка известна на этапе +// компиляции. Например: +void main() { +   mixin(`writeln("Hello World!");`); +} + +// еще пример +string print(string s) { +   return `writeln("` ~ s ~ `");`; +} + +void main() { +   mixin (print("str1")); +   mixin (print("str2")); +} + + + +/*** Шаблоны ***/ + +/* +    Шаблон функции. Эта функция принимает аргументы разных типов, которые +    подставляются вместо T на этапе компиляции. "T" - это не специальный +    символ, а просто буква. Вместо "T" может быть любое слово, кроме ключевого. + */ +void print(T)(T value) { +   writefln("%s", value); +} + +void main() { +   print(42);     // В одну и ту же функцию передается: целое +   print(1.2);    // ...число с плавающей точкой, +   print("test"); // ...строка +} + +// "Шаблонных" параметров может быть сколько угодно +void print(T1, T2)(T1 value1, T2 value2) { +   writefln(" %s %s", value1, value2); +} + +void main() { +   print(42, "Test"); +   print(1.2, 33); +} + +// Шаблон класса +class Stack(T) +{ +    private: +        T[] elements; + +    public: +        void push(T element) { +            elements ~= element; +        } + +        void pop() { +            --elements.length; +        } + +        T top() const @property { +            return elements[$ - 1]; +        } + +        size_t length() const @property { +            return elements.length; +        } +} + +void main() { +    /* +        восклицательный знак - признак шаблона. В данном случае мы создаем +        класс и указываем, что "шаблонное" поле будет иметь тип string +    */ +   auto stack = new Stack!string; + +   stack.push("Test1"); +   stack.push("Test2"); + +   writeln(stack.top); +   writeln(stack.length); + +   stack.pop; +   writeln(stack.top); +   writeln(stack.length); +} + + + +/*** Диапазоны (ranges) ***/ + +/* +   Диапазоны - это абстракция, которая позволяет легко использовать разные +   алгоритмы с разными структурами данных. Вместо того, чтобы определять свои +   уникальные алгоритмы для каждой структуры, мы можем просто указать для нее +   несколько единообразных функций, определяющих, _как_ мы получаем доступ +   к элементам контейнера, вместо того, чтобы описывать внутреннее устройство +   этого контейнера. Сложно? На самом деле не очень. + +    Простейший вид диапазона - Input Range. Для того, чтобы превратить любой +    контейнер в Input Range, достаточно реализовать для него 3 метода: +    - empty - проверяет, пуст ли контейнер +    - front - дает доступ к первому элементу контейнера +    - popFront - удаляет из контейнера первый элемент +*/ +struct Student +{ +    string name; +    int number; +    string toString() { +        return format("%s(%s)", name, number); +    } +} + +struct School +{ +    Student[] students; +} + +struct StudentRange +{ +    Student[] students; + +    this(School school) { +        this.students = school.students; +    } + +    bool empty() { +        return students.length == 0; +    } + +    Student front() { +        return students[0]; +    } + +    void popFront() { +        students = students[1 .. $]; +    } +} + +void main(){ +    auto school = School([ +            Student("Mike", 1), +            Student("John", 2) , +            Student("Dan", 3) +    ]); +    auto range = StudentRange(school); +    writeln(range);                     // [Mike(1), John(2), Dan(3)] +    writeln(school.students.length);    // 3 +    writeln(range.front());             // Mike(1) +    range.popFront(); +    writeln(range.empty());             // false +    writeln(range);                     // [John(2), Dan(3)] +} +/* +   Смысл в том, что нам не так уж важно внутреннее устройство контейнера, если +   у нас есть унифицированные методы доступа к его элементам. +   Кроме Input Range в D есть и другие типы диапазонов, которые требуют +   реализации большего числа методов, зато дают больше контроля. Это большая +   тема и мы не будем в подробностях освещать ее здесь. + +   Диапазоны - это важная часть D, они используются в нем повсеместно. +*/ +``` +## Что дальше? + +- [Официальный сайт](http://dlang.org/) +- [Онлайн-книга](http://ddili.org/ders/d.en/) +- [Официальная вики](http://wiki.dlang.org/) | 
