Skip to content

Latest commit

 

History

History
285 lines (228 loc) · 10.7 KB

xiny.md

File metadata and controls

285 lines (228 loc) · 10.7 KB

Выучить X за Y минут (вольный перевод)

Где X=D

Классика

import std.stdio;

// агрумент 'args' не обязательно должен присутствовать в сигнатуре
void main(string[] args)
{
    writeln("Hello, World!");
}

собираем и запускаем:

dmd helloworld.d
./helloworld

либо одной командой:

rdmd helloworld.d

Если вы интересуетесь ИТ индустрией и современными разработками наверняка вы слышали про язык D. D современный мультипарадигменный язык программирования, который поддерживает всё от низкоуровневых фич до высокоуровневых абстракций.

D активно развивается группой достаточно квалифицированных специалистов в области ИТ, которую возглавляют Уолтер Брайт (Walter Bright) и Андрей Александреску (Andrei Alexandrescu).

Больше примеров богу примеров!)

import std.stdio;

void main()
{

    // условия и циклы как других С-подобных языках
    for (int i = 0; i < 10000; i++)
    {
        writeln(i);
    }

    // 'auto' используется для автоматического вывода типов
    auto n = 1;

    // численные литералы могут разделяться символом '_' для читабельности
    while (n < 10_000)
    {
        n += n;
    }

    do
    {
        n -= (n / 2);
    } while (n > 0);

    // 'for' and 'while' хороши, но в мире D чаще используются 'foreach' циклы
    // '1..1_000' обозначает непрерывный диапазон [1 1_000) (включает начало но не конец).
    foreach (n; 1..1_000)
    {
        if (n % 2 == 0)
            writeln(n);
    }

    // так же есть 'foreach_reverse' когда вам нужно пройтись
    // по диапазону в обратной последовательности
    foreach_reverse (n; 1..int.max)
    {
        if (n % 2 == 1)
            writeln(n); // для одиночных команд можно опускать {}
        else
            writeln("No!");
    }
}

Мы можем объявлять новые типы данных с помощью struct (структуры), class (классы), union (объединения) и enum (перечисления). Структуры передаются по значенияю (т.е. копируются), а классы по ссылке. Так же мы можем использовать шаблоны для параметризации классов и структур.

// здесь 'T' аргумент шаблона, тоже самое что '<T>' в C++/C#/Java.
struct LinkedList(T)
{
    T data = null;

    // используйте '!' для инстанцирования шаблонного типа, снова как '<T>'.
    LinkedList!(T)* next;
}

class BinTree(T)
{
    T data = null;

    // в случае одного аргумента шаблона можно опустить скобки
    BinTree!T left;
    BinTree!T right;
}

enum Day
{
    Sunday,
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
}

// создание псевдонимов типов для более компактной записи
alias IntList = LinkedList!int;
alias NumTree = BinTree!double;

// так же можно объявлять шаблонные функции
T max(T)(T a, T b)
{
    if(a < b)
        return b;

    return a;
}

// используйте 'ref' для передачи по ссылке.
// если даже тип аргументов 'a' и 'b' (тип 'T') это тип, передаваемый по
// значению (структура, число) 'a' и 'b' всегда должны передаваться по ссылке для 'swap()'.
void swap(T)(ref T a, ref T b)
{
    auto temp = a;

    a = b;
    b = temp;
}

// параметрами шаблона так же могут быть значения какого-то типа, а не только как таковые типы
class Matrix(uint m, uint n, T = int)
{
    T[m] rows;
    T[n] columns;
}

auto mat = new Matrix!(3, 3); // по умолчанию 'T' это 'int'.

Если речь зашла о классах, то пара слов о property (свойствах). Свойство это метод стуктуры или класса, который имеет синтаксис поля (foo.x = 7), но семантику метода (foo.setX(7)).

// рассмотрим класс параметризированный типами 'T' и 'U'.
class MyClass(T, U)
{
    T _data;
    U _other;
}

// и с "getter" и "setter" методами это будет выглядеть так:
class MyClass(T, U)
{
    T _data;
    U _other;

    // конструктор всегда имеет имя 'this'
    this(T t, U u)
    {
        // вызываем "setters"
        data = t;
        other = u;
    }

    // атрибуты можно группировать (равносильно что написать `@property` рядом с каждым методом)
    @property
    {
        // getters
        T data() { return _data; }
        U other() { return _other; }
    }

    // setters
    @property void data(T t) { _data = t; }
    @property void other(U u) { _other = u; }
}

void main()
{
    auto mc = new MyClass!(int, string)(7, "seven");

    // импорт модуля 'stdio' из стандартной библиотеки для работы с выводом на консоль
    // локальные импорты ограничены областью видимости
    import std.stdio;

    // вызов "getter"ов для вывода значений
    writefln("Earlier: data = %d, str = %s", mc.data, mc.other);

    // вызов "setter"ов для выставления новых значений
    mc.data = 8;
    mc.other = "eight";

    writefln("Later: data = %d, str = %s", mc.data, mc.other);
}

С помощью @propety (свойств) мы можем добавить логику к getter'ам и setter'ам сохранив чистоту синтаксиса.

Так объектно-ориентированным приемуществам D относятся поддержка концепции интерфейсов, абстрактных классов, перегрузка методов и т.д. Наследование в D схоже с наследованием в Java: наследовать можно от одного класса, а реализовывать сколь угодно интерфейсов (нет поддержки множественного наследования).

Мы видели возможности D в ООП, но давайте рассмотрим другой подход. D предлагает функциональное программирование с первоклассными функциями, чистыми функциями и неизменяемыми данными. Кроме того, все ваши любимые функциональные алгоритмы (map (карта), filter (фильтр), reduce (сокращение) и другие) можно найти в замечательном модуле std.algorithm!

import std.algorithm : map, filter, reduce;
import std.range : iota; // строит диапазоны

void main()
{
    // мы хотим распечатать сумму квадратов всех чётных чисел от 1 до 100. Easy! Easy!

    // просто передавайте lambda-выражения в качестве шаблона
    // вы можете передать любую подходящую функция, но тут будут lambda
    iota(1, 101) // создаём ленивый диапазон
        .filter!(x => x % 2 == 0) // оставляем только чётные
        .map!(y => y ^^ 2) // возводим их в квадрат
        .reduce!((a, b) => a + b) // суммируем
        .writeln; // выводим на экран
}

Обратите внимание на Haskell-подобный pipeline для вычисления и выведения на экран. Так можно писать благодаря D инновации -- Uniform Function Call Syntax (UFCS). С UFCS мы можем выбирать вызывать функцию как метод или как обычную функцию передавая ей аргумент.

Более простой пример:

int mult2(int a)
{
    return a*2;
}

void main()
{
    import std.stdio;
    int x = 5;
    writeln(x.mult2); // 10
}

Это справедливо для любого типа A и любой функции, принимающей один или более аргументов, первый из которых типа A.

Немного параллизма.

// допустим мы хотим заполнить большой массив квадратными корнями всех последовательных
// целых чисел начиная с 1, заканчивая размером массива и мы хотим чтобы это выполнялось
// одновременно на таком количестве ядер, которое нам сейчас доступно

import std.stdio;
import std.parallelism : parallel;
import std.math : sqrt;

void main()
{
    // создадим наш массив
    auto arr = new double[1_000_000];

    // используем индекс, доступ к элементу массива по ссылке (т.к. мы хотим
    // записать вычисления туда) и вызываем 'parallel' для массива
    foreach (i, ref elem; parallel(arr)) {
        elem = sqrt(i + 1.0);
    }
}

Есть предложения улучшения? Нашёл ошибку? Создавай issue!