D (язык программирования)

Материал из Википедии — свободной энциклопедии
Перейти к: навигация, поиск
D
D Programming Language logo.svg
Семантика:

мультипарадигменный: императивное, объектно-ориентированное, контрактное[1], обобщённое программирование

Тип исполнения:

компилируемый

Появился в:

2001

Автор:

Уолтер Брайт

Выпуск:

2.067.1 (2015-04-25; 95 дней тому назад[2])

Тестовая версия:

2.068.1-b1

Система типов:

строгая, статическая, с выводом типов

Основные реализации:

Digital Mars D (эталонная реализация), LDC, GDC

Испытал влияние:

Си, C++, Python, Ruby, C#, Java, Eiffel

Сайт:

dlang.org

D (Ди) — мультипарадигмальный компилируемый язык программирования, созданный Уолтером Брайтом из компании Digital Mars. Начиная с 2006 г. соавтором также является Андрей Александреску. Изначально D был задуман как реинжиниринг языка C++, однако, несмотря на значительное влияние С++, не является его вариантом. Также язык испытал влияние концепций из языков программирования Python, Ruby, C#, Java, Eiffel.

При создании языка D была сделана попытка соединить производительность компилируемых языков программирования с безопасностью и выразительностью динамических.

Стабильная версия компилятора 1.0 вышла 2 января 2007[3].

Стабильная версия компилятора 2.0 (последняя на сегодняшний день мажорная версия) выпущена 17 июня 2007 года[4].

D доступен для операционных систем Windows, Linux, Mac OS, FreeBSD. Ведётся работа по портированию на Android[5].

Обзор языка[править | править вики-текст]

Синтаксис[править | править вики-текст]

D относится к семейству C-подобных языков с фигурными скобками, в общих чертах его синтаксис похож на C/C++/C#, Java. При разработке языка соблюдается принцип: код, одинаково валидный и в C, и в D, должен вести себя одинаково.

"Hello, world!" на D:

import std.stdio;

void main() {
    writeln ("Hello, world!");
}

Также как в C, функция main() является точкой входа.

Универсальный синтаксис вызова функций (UFCS)[править | править вики-текст]

В D реализован механизм UFCS (Uniform function call syntax), позволяющий вызывать функции для любого объекта так, как будто они являются его методами. Например:

import std.stdio;
import std.algorithm;
import std.array;

void main()
{
    auto a = [2, 4, 1, 3];
 
    // все три следующих варианта корректны и работают одинаково
    writeln(a); // "классический" C-подобный вариант
    a.writeln(); // функция вызывается так, как будто является методом объекта "a", хотя и не является таковой
    a.writeln; // функцию без параметров можно вызывать без скобок
 
    // это позволяет использовать чейнинг, характерный для функциональных языков
    int[] e = a.sort().reverse;

    // многострочный чейнинг также возможен
    stdin
        .byLine(KeepTerminator.yes)
        .map!(a => a.idup)
        .array
        .sort;
}

Атрибуты функций[править | править вики-текст]

Функции в D могут быть вызваны с дополнительными необязательными атрибутами, которые позволяют явно указывать некоторые аспекты поведения этих функций. Например, функция, помеченная атрибутом pure гарантированно является функционально чистой (с некоторыми оговорками)[6]. Функциональная чистота при этом проверяется на этапе компиляции. Пример объявления функции с атрибутом:

pure int sum (int first, int second) 
{
    return first + second;
}

int sum (int first, int second) pure // атрибуты можно указывать и после списка аргументов
{
    return first + second;
}

Примеры атрибутов функций:

  • pure - функциональная чистота
  • @safe - гарантия безопасной работы с памятью
  • nothrow - функция гарантированно не генерирует исключений
  • @nogc - гарантия того, что функция не содержит операций, требующих сборки мусора

Встроенные юнит-тесты[править | править вики-текст]

В D юнит-тесты являются частью языка, их можно использовать без подключения дополнительных библиотек или фреймворков.

import std.stdio;

int first (int[] arr) {
    return arr[0];
}

unittest {
    int[] arr1 = [1, 2, 3];
    int[] arr2 = [10, 15, 20];

    assert(first(arr1) == 1);
    assert(first(arr2) == 10);
}

void main() {
    // ...
}


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

Парадигмы программирования[править | править вики-текст]

D реализует пять основных парадигм программирования—императивное, ООП, метапрограммирование, функциональное программирование и параллельные вычисления (модель акторов).

Управление памятью[править | править вики-текст]

D использует сборщик мусора для управления памятью, однако возможно и ручное управление с помошью перегрузки операторов new и delete, а также с помощью malloc и free, аналогично C. Сборщик мусора можно включать и выключать вручную, можно добавлять и удалять области памяти из его видимости, принудительно запускать частичный или полный процесс сборки. Существует подробное руководство, описывающее различные схемы управления памятью в D для тех случаев, когда стандартный сборщик мусора неприменим.

SafeD[править | править вики-текст]

SafeD - название подмножества языка D, использование которого гарантирует безопасность доступа к памяти.

Типы данных[править | править вики-текст]

Язык имеет богатый набор определённых типов данных и средств для определения новых типов. Типы в языке D разделяются на типы-значения и типы-ссылки.

Базовые типы[править | править вики-текст]

Набор базовых типов можно разделить на следующие категории[7]:

  • void — специальный тип для пустых значений
  • bool — логический тип
  • целочисленные типы: знаковые byte, short, int, long и соответствующие им беззнаковые ubyte, ushort, uint, ulong
  • типы для чисел с плавающей точкой: float, double, real. Для типов с плавающей точкой есть соответствующие им варианты для мнимых и комплексных чисел:
    • мнимые: ifloat, idouble, ireal
    • комплексные: сfloat, сdouble, сreal
  • знаковые (символьные) типы: char, wchar, dchar, обозначающие кодовые единицы кодировок UTF-8, UTF-16 и UTF-32 соответственно.

В отличие от C++ все размеры целочисленных типов определены спецификацией. То есть, тип int будет всегда размером 32 бита. Целочисленные литералы можно записывать в десятичной, двоичной (с префиксом 0b) и шестнадцатеричной (с префиксом 0x) системе счисления. Способ записи литералов в восьмеричной системе в стиле C (то есть с префиксом 0) был убран, так как такую запись легко спутать с десятичной. Если всё-таки нужно использовать восьмеричную систему, можно воспользоваться шаблоном std.conv.octal.

Производные типы[править | править вики-текст]

  • pointer - указатель
  • array - массив
  • associative array - ассоциативный массив
  • function - функция
  • delegate - делегат
  • string, wstring, dstring – удобные псевдонимы для неизменяемых массивов знаковых (символьных) типов immutable(char)[], immutable(wchar)[] и immutable(dchar)[], обозначающие неизменяемые (квалификатор immutable) строки Юникода в одной из кодировок UTF-8, UTF-16 и UTF-32 соответственно.

Пользовательские типы[править | править вики-текст]

  • alias - псевдоним
  • enum - перечисление
  • struct - структура
  • union - объединение
  • class - класс

Вывод типов, ключевые слова "auto", "typeof" и безымянные ("Voldemort") типы[править | править вики-текст]

В D реализован механизм вывода типов. Это значит, что тип, как правило, может быть вычислен на этапе компиляции и его не обязательно указывать явно. Например, выражение: auto myVar = 10 на этапе компиляции будет преобразовано в int myVar = 10. Использование вывода типов дает несколько преимуществ:

  • Более лаконичный и читаемый код, особенно если в нём спользуются длинные имена структур или классов. Например, выражение

VeryLongTypeName var = VeryLongTypeName(/* ... */);

может быть заменено на

auto var = VeryLongTypeName(/* ... */);

  • с помощью ключевого слова typeof можно создать переменную такого же типа, как у существующей переменной, даже если её тип неизвестен. Пример:
// file1.d
int var1;

// file2.d
typeof(var1) var2; // var2 получает тип int
  • использование безымянных типов. Пример:
// Функция фактически возвращает результат типа TheUnnameable, но, поскольку этот тип определен внутри функции, 
// мы не можем явно задать его как тип возвращаемого значения.
// Тем не менее, мы можем задать возвращаемый тип как "auto", предоставив компилятору вычислить его самостоятельно
auto createVoldemortType(int value)
{
    struct TheUnnameable
    {
        int getValue() { return value; }
    }
    return TheUnnameable();
}

Безымянные типы неофициально называются Voldemort-типы по аналогии с Воланом-де-Мортом («Тот-Кого-Нельзя-Называть»), главным антагонистом серии о Гарри Поттере[8]. Вывод типов не следует путать с динамической типизацией, поскольку хотя тип не задается явно, вычисляется он на этапе компиляции, а не во время выполнения.

Реализации[править | править вики-текст]

  • DMD — Digital Mars D, эталонный компилятор, разрабатываемый Уолтером Брайтом. Этот компилятор наиболее полно реализует стандарт языка, поддержка всех нововведений появляется в нём в первую очередь. Фронт-энд распространяется под лицензией Boost, бэк-энд - под проприетарной лицензией с доступом к исходным кодам. Часть кода бэк-энда была разработана в Symantec, и не может быть перелицензирована[9].
  • GDC — DMD-Фронт-энд для компилятора GCC.
  • LDC — DMD-Фронт-энд для LLVM
  • SDC — Экспериментальный компилятор (компилятор как библиотека), использующий LLVM в качестве бэк-энда и не основанный на DMD.

Инструменты и средства разрабоки[править | править вики-текст]

IDE и редакторы[править | править вики-текст]

Поддержка D в различных IDE, реализованная с помошью плагинов:

IDE Плагин Платформы
Eclipse DDT кроссплатформенный
MonoDevelop / Xamarin Mono-D кроссплатформенный
Visual Studio Visual-D Windows
XCode D for Xcode Mac OS X
Zeus IDE D for Zeus IDE Windows

Нативная IDE для языка D - Coedit (Windows, Linux)

D поддерживается во множестве текстовых редакторов: Vim, Emacs, Kate, Notepad++, Sublime Text, TextMate и других[10].

Менеджер пакетов[править | править вики-текст]

DUB - официальный менеджер пакетов D. DUB выполняет функции репозитория пакетов и используется для управления зависимостями, а также в качестве системы сборки. Набор зависимостей, метаданные о проекте и флаги компилятора хранятся в формате JSON. Пример простого файла проекта:

{
    "name": "myproject",
    "description": "A little web service of mine.",
    "authors": ["Peter Parker"],
    "homepage": "http://myproject.example.com",
    "license": "GPL-2.0",
    "dependencies": {
        "vibe-d": "~>0.7.23"
    }
}

Утилиты и инструменты[править | править вики-текст]

rdmd[править | править вики-текст]

rdmd — утилита, идущая в комплекте с компилятором DMD, позволяющая компилировать и запускать файлы с исходным кодом D «на лету». Это позволяет использовать D для небольших программ аналогично bash и perl:

// myprog.d

#!/usr/bin/env rdmd
import std.stdio;
void main()
{
    writeln("Hello, world with automated script running!");
}

Вызов команды ./myprog.dв консоли автоматически скомпилирует и выполнит программу.

DPaste[править | править вики-текст]

Dpaste - онлайн-сервис для запуска программ на D в браузере, похожий на сервисы JSBin и Codepen.

asm.dlang.org[править | править вики-текст]

asm.dlang.org - онлайн-компилятор и дизассемблер

Примеры кода[править | править вики-текст]

Пример 1[править | править вики-текст]

"Hello, world!"

import std.stdio;

void main() {
    writeln("Hello, world!");
}

Пример 2[править | править вики-текст]

Программа, которая выводит аргументы командной строки, с которыми была вызвана

import std.stdio: writefln;

void main(string[] args)
{
    foreach (i, arg; args)
        writefln("args[%d] = '%s'", i, arg);
}

Пример 3[править | править вики-текст]

Программа, которая построчно считывает список слов из файла и выводит все слова, которые являются анаграммами других слов

import std.stdio, std.algorithm, std.range, std.string;

void main()
{
    dstring[][dstring] signs2words;

    foreach(dchar[] w; lines(File("words.txt")))
    {
        w = w.chomp().toLower();
        immutable key = w.dup.sort().release().idup;
        signs2words[key] ~= w.idup;
    }

    foreach (words; signs2words)
    {
        if (words.length > 1) 
        {
            writefln(words.join(" "));
        }
    }
}

См. также[править | править вики-текст]

Примечания[править | править вики-текст]

Литература[править | править вики-текст]

Ссылки[править | править вики-текст]

,