Выучить X за Y минут (вольный перевод)
Классика
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!