D , также известный как dlang , — это мультипарадигмальный язык системного программирования , созданный Уолтером Брайтом из Digital Mars и выпущенный в 2001 году. Андрей Александреску присоединился к проектированию и разработке в 2007 году. Хотя он возник как реинжиниринг C++ , D теперь это совершенно другой язык, черпающий вдохновение из других языков программирования высокого уровня, в частности Java , Python , Ruby , C# и Eiffel .
Справочник по языку D описывает это следующим образом:
D — это язык системного программирования общего назначения с синтаксисом, подобным C, который компилируется в собственный код. Он статически типизирован и поддерживает как автоматическое (сбор мусора), так и ручное управление памятью. Программы D структурированы как модули, которые можно компилировать отдельно и связывать с внешними библиотеками для создания собственных библиотек или исполняемых файлов.
D не совместим с исходным кодом C и C++ в целом. Однако любой код, который допустим как для C, так и для D, должен вести себя одинаково.
Как и в C++, в D есть замыкания , анонимные функции , выполнение функций во время компиляции , диапазоны, встроенные концепции итерации контейнеров и вывод типов . В отличие от C++, D также реализует проектирование по контракту , модули , сборку мусора , массивы первого класса , нарезку массива , вложенные функции и отложенное вычисление . D использует одиночное наследование в стиле Java с интерфейсами и примесями , а не множественное наследование в стиле C++ . С другой стороны, синтаксис объявлений, операторов и выражений D очень похож на синтаксис C++.
D — язык системного программирования. Как и C++, D поддерживает низкоуровневое программирование , включая встроенный ассемблер , который символизирует различия между D и языками приложений, такими как Java и C# . Встроенный ассемблер позволяет программистам вводить ассемблерный код, специфичный для машины , в стандартный код D — метод, используемый системными программистами для доступа к низкоуровневым функциям процессора, необходимым для запуска программ, которые напрямую взаимодействуют с базовым оборудованием , например, с операционными системами и драйверами устройств. , а также написание высокопроизводительного кода (т. е. с использованием векторных расширений, SIMD ), который сложно сгенерировать компилятору автоматически.
D поддерживает перегрузку функций и перегрузку операторов . Символы (функции, переменные, классы) можно объявлять в любом порядке — форвардные объявления не требуются.
В D строки текстовых символов представляют собой массивы символов, а массивы в D проверяются по границам. [11] D имеет типы первого класса для комплексных и мнимых чисел. [12]
D поддерживает пять основных парадигм программирования :
Императивное программирование на D почти идентично программированию на C. Функции, данные, операторы, объявления и выражения работают так же, как и в C, и к библиотеке времени выполнения C можно получить прямой доступ. С другой стороны, некоторые заметные различия между D и C в области императивного программирования включают foreach
конструкцию цикла D, которая позволяет выполнять цикл по коллекции, и вложенные функции , которые представляют собой функции, которые объявляются внутри других функций и могут обращаться к локальным переменным охватывающей функции. .
импорт стандартный . стдио ; void main () { int multiplier = 10 ; int Scaled ( int x ) { return x * multiplier ; } foreach ( i ; 0 .. 10 ) { writefln ( «Привет, мир %d! Scaled = %d» , i , Scaled ( i )); } }
Объектно-ориентированное программирование в D основано на единой иерархии наследования, все классы которой являются производными от класса Object. D не поддерживает множественное наследование; вместо этого он использует интерфейсы в стиле Java , которые сравнимы с чистыми абстрактными классами C++, и миксины , которые отделяют общую функциональность от иерархии наследования. D также позволяет определять статические и финальные (не виртуальные) методы в интерфейсах.
Интерфейсы и наследование в D поддерживают ковариантные типы для возвращаемых типов переопределенных методов.
D поддерживает пересылку типов, а также дополнительную пользовательскую динамическую отправку .
Классы (и интерфейсы) в D могут содержать инварианты , которые автоматически проверяются до и после доступа к общедоступным методам в соответствии с методологией проектирования по контракту .
Многие аспекты классов (и структур) могут автоматически анализироваться во время компиляции (форма отражения с использованием type traits
) и во время выполнения (RTTI/ TypeInfo
), чтобы облегчить общий код или автоматическую генерацию кода (обычно с использованием методов времени компиляции).
D поддерживает такие функции функционального программирования , как функциональные литералы , замыкания , рекурсивно-неизменяемые объекты и использование функций высшего порядка . Существует два синтаксиса анонимных функций, включая форму с несколькими операторами и «сокращенную» запись с одним выражением: [13]
функция int ( int ) g ; г знак равно ( Икс ) { вернуть Икс * Икс ; }; // от руки g = ( x ) => x * x ; // сокращение
Существует два встроенных типа для функциональных литералов: function
, который является просто указателем на функцию, выделенную в стеке, и delegate
, который также включает указатель на соответствующий кадр стека , окружающую «среду», которая содержит текущие локальные переменные. Вывод типа может использоваться с анонимной функцией, и в этом случае компилятор создает a, delegate
если только он не может доказать, что указатель среды не является необходимым. Аналогично, для реализации замыкания компилятор помещает вложенные локальные переменные в кучу только в случае необходимости (например, если замыкание возвращается другой функцией и выходит из области действия этой функции). При использовании вывода типа компилятор также добавит атрибуты, такие как pure
и, nothrow
к типу функции, если сможет доказать их применимость.
Другие функциональные возможности, такие как каррирование и общие функции высшего порядка, такие как Map , Filter и Reduc , доступны через модули стандартной библиотеки std.functional
и std.algorithm
.
импорт стандартный . стдио , станд . алгоритм , стандарт . диапазон ; void main () { int [] a1 = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ]; int [] a2 = [ 6 , 7 , 8 , 9 ]; // должен быть неизменяемым, чтобы обеспечить доступ изнутри чистой функции immutable Pivot = 5 ; int mySum ( int a , int b ) pure nothrow // чистая функция { if ( b <= Pivot ) // ссылка на охватывающую область return a + b ; иначе вернуть ; } // передача делегата (закрытие) автоматический результат = сокращение ! mySum ( цепочка ( a1 , a2 )); writeln ( "Результат:" , результат ); // Результат: 15 // передача литерала делегата result = сокращение !(( a , b ) => ( b <= Pivot ) ? a + b : a )( Chain ( a1 , a2 )); writeln ( "Результат: " , результат ); // Результат: 15 }
Альтернативно, приведенные выше композиции функций могут быть выражены с использованием унифицированного синтаксиса вызова функций (UFCS) для более естественного чтения слева направо:
автоматический результат = a1 . цепочка ( а2 ). уменьшать ! мояСумма (); writeln ( "Результат:" , результат ); результат = а1 . цепочка ( а2 ). уменьшить !(( a , b ) => ( b <= пивот ) ? a + b : a )(); writeln ( "Результат:" , результат );
Концепции параллельного программирования реализованы в библиотеке и не требуют дополнительной поддержки со стороны компилятора. Однако система типа D и компилятор гарантируют, что совместное использование данных может быть обнаружено и прозрачно управляемо.
импорт стандартный . stdio : writeln ; импорт стандартный . диапазон : йота ; импорт стандартный . параллелизм : параллельный ; void main () { foreach ( i ; iota ( 11 ) .parallel ) { // Тело цикла foreach выполняется параллельно для каждого i writeln ( "processing" , i ); } }
iota(11).parallel
эквивалентно std.parallelism.parallel(iota(11))
использованию UFCS.
Тот же модуль также поддерживает операции taskPool
, которые можно использовать для динамического создания параллельных задач, а также операции отображения-фильтрации-уменьшения и свертывания стилей над диапазонами (и массивами), что полезно в сочетании с функциональными операциями. std.algorithm.map
возвращает лениво вычисляемый диапазон, а не массив. Таким образом, элементы автоматически вычисляются каждой рабочей задачей параллельно.
импорт стандартный . stdio : writeln ; импорт стандартный . алгоритм : карта ; импорт стандартный . диапазон : йота ; импорт стандартный . параллелизм : TaskPool ; /* На Intel i7-3930X и gdc 9.3.0: * 5140 мс при использовании std.algorithm.reduce * 888 мс при использовании std.parallelism.taskPool.reduce * * На AMD Threadripper 2950X и gdc 9.3.0: * 2864 мс при использовании std.algorithm .reduce * 95 мс с использованием std.parallelism.taskPool.reduce */ void main () { auto nums = iota ( 1.0 , 1_000_000_000.0 ); авто x = TaskPool . уменьшать ! "a + b" ( 0.0 , карта ! "1.0 / (a * a)" ( nums ) ); writeln ( "Сумма: " , x ); }
Параллелизм полностью реализован в библиотеке и не требует поддержки со стороны компилятора. Возможны альтернативные реализации и методологии написания параллельного кода. Использование системы типизации D действительно помогает обеспечить безопасность памяти.
импорт стандартный . стдио , станд . параллелизм , стандарт . вариант ; void foo () { bool cont = true ; while ( cont ) { receive ( //Делегаты используются для соответствия типу сообщения. ( int msg ) = > writeln ( "int got: " , msg ), ( Tid sender ) { cont = false ; sender.send (- 1 ) ); }, ( Variant v ) => writeln ( "huh?" ) // Вариант соответствует любому типу ); } } void main () { auto tid = spawn (& foo ); // создаем новый поток, выполняющий foo() foreach ( i ; 0 .. 10 ) tid . отправить ( я ); // отправляем несколько целых чисел время . отправить ( 1.0f ); // отправляем плавающий tid . отправить ( «привет» ); // отправляем строку tid . отправить ( этотид ); // отправляем структуру (Tid) получить (( int x ) => writeln ( «Основной поток получил сообщение: « , x )); }
Метапрограммирование поддерживается посредством шаблонов, выполнения функций во время компиляции, кортежей и строковых миксинов. Следующие примеры демонстрируют некоторые возможности D во время компиляции.
Шаблоны в D могут быть написаны в более императивном стиле по сравнению с функциональным стилем шаблонов C++. Это обычная функция, вычисляющая факториал числа:
ulong факториал ( ulong n ) { if ( n < 2 ) вернуть 1 ; иначе вернуть n * факториал ( n - 1 ); }
Здесь показано использование static if
условной конструкции D во время компиляции для создания шаблона, который выполняет те же вычисления с использованием кода, аналогичного коду функции выше:
шаблон Factorial ( ulong n ) { static if ( n < 2 ) enum Factorial = 1 ; else enum Factorial = n * Факториал !( n - 1 ); }
В следующих двух примерах шаблон и функция, определенные выше, используются для вычисления факториалов. Типы констант не обязательно указывать явно, поскольку компилятор определяет их типы из правых частей присваивания:
enum fact_7 = Факториал !( 7 );
Это пример выполнения функции во время компиляции (CTFE). Обычные функции могут использоваться в константных выражениях времени компиляции при условии, что они соответствуют определенным критериям:
перечисление факт_9 = факториал ( 9 );
Функция std.string.format
выполняет printf
форматирование данных в формате -like (также во время компиляции, через CTFE), а прагма «msg» отображает результат во время компиляции:
импорт стандартный . строка : формат ; прагма ( msg , format ( «7! = %s» , fact_7 )); прагма ( сообщение , формат ( «9! = %s» , fact_9 ));
Строковые миксины в сочетании с выполнением функций во время компиляции позволяют генерировать D-код с использованием строковых операций во время компиляции. Это можно использовать для анализа предметно-ориентированных языков , которые будут скомпилированы как часть программы:
импортировать ФуТоД ; // гипотетический модуль, содержащий функцию, которая анализирует исходный код Foo // и возвращает эквивалентный код D void main () { mixin ( fooToD ( import ( "example.foo" ))); }
Память обычно управляется с помощью сборки мусора , но определенные объекты могут быть финализированы немедленно, когда они выходят за пределы области действия. Именно это использует большинство программ и библиотек, написанных на D.
В случае, если требуется больший контроль над расположением памяти и более высокая производительность, возможно явное управление памятью с помощью перегруженного оператора new
, прямым вызовом C malloc и free или реализацией пользовательских схем распределения (т. е. в стеке с резервным вариантом, распределение в стиле RAII, ссылка подсчет, общий подсчет ссылок). Сборкой мусора можно управлять: программисты могут добавлять и исключать диапазоны памяти из наблюдения сборщика, могут отключать и включать сборщик, а также принудительно запускать либо генерационный, либо полный цикл сбора. [14] В руководстве приведено множество примеров того, как реализовать различные высокооптимизированные схемы управления памятью, когда сборка мусора в программе неадекватна. [15]
В функциях struct
экземпляры по умолчанию размещаются в стеке, а class
экземпляры по умолчанию размещаются в куче (с ссылкой только на экземпляр класса, находящийся в стеке). Однако это можно изменить для классов, например, используя шаблон стандартной библиотеки std.typecons.scoped
или используя new
структуры и присваивая указатель вместо переменной, основанной на значении. [16]
В функциях статические массивы (известного размера) размещаются в стеке. Для динамических массивов можно использовать core.stdc.stdlib.alloca
функцию (аналогично alloca
C) для выделения памяти в стеке. Возвращенный указатель может быть использован (переведен) в (типизированный) динамический массив посредством среза (однако следует избегать изменения размера массива, включая добавление; и по очевидным причинам они не должны возвращаться из функции). [16]
Ключевое scope
слово можно использовать как для аннотирования частей кода, так и для переменных и классов/структур, чтобы указать, что они должны быть уничтожены (вызов деструктора) немедленно при выходе из области видимости. Независимо от того, какая память будет освобождена, также зависит от реализации и различий между классами и структурами. [17]
std.experimental.allocator
содержит модульные и компонуемые шаблоны распределителей для создания пользовательских высокопроизводительных распределителей для особых случаев использования. [18]
SafeD [19]
— это имя, данное подмножеству D, которое может быть гарантированно безопасным для памяти (нет записи в память, которая не была выделена или была переработана). Отмеченные функции @safe
проверяются во время компиляции, чтобы гарантировать, что они не используют какие-либо функции, которые могут привести к повреждению памяти, такие как арифметика указателей и непроверяемые приведения, а любые другие вызываемые функции также должны быть помечены как @safe
или @trusted
. Функции могут быть отмечены @trusted
для случаев, когда компилятор не может отличить безопасное использование функции, отключенной в SafeD, от потенциального случая повреждения памяти. [20]
Первоначально под названиями DIP1000 [21] и DIP25 [22] (теперь часть спецификации языка [23] ) D обеспечивает защиту от некоторых некорректных конструкций, включающих время жизни данных.
Существующие в настоящее время механизмы в основном работают с параметрами функций и стековой памятью, однако лидеры языка программирования заявляют о стремлении обеспечить более тщательную обработку времени жизни в языке программирования D [24] (под влиянием идей языка программирования Rust). ).
В коде @safe проверяется время существования назначения, включающего ссылочный тип , чтобы убедиться, что время жизни получателя больше, чем время жизни назначенного.
Например:
@safe void test () { int tmp = 0 ; // #1 int * рад ; // #2 рад = & tmp ; // Если порядок объявлений №1 и №2 обратный, это не удастся. { инт плохо = 45 ; // Время жизни "плохого" распространяется только на ту область, в которой оно определено. * рад = плохо ; // Это действительно. рад = & плохо ; // Срок жизни рад больше, чем плохой, поэтому это неверно. } }
При применении к параметру функции, который имеет тип указателя или ссылку, ключевые слова return и область действия ограничивают время существования и использование этого параметра.
Стандарт языка диктует следующее поведение: [25]
Аннотированный пример приведен ниже.
@безопасный :интервал * гп ; void Thorin ( scope int *); пустота глоин ( int *); int * balin ( возвращает область действия int * p , область действия int * q , int * r ) { gp = p ; // Ошибка, p переходит в глобальную переменную gp. гп = q ; // Ошибка, q переходит в глобальную переменную gp. гп = р ; // ХОРОШО. Торин ( р ); // Хорошо, p не экранирует функцию Thorin(). Торин ( q ); // ХОРОШО. Торин ( р ); // ХОРОШО. глоин ( р ); // Ошибка, p выходит за пределы globin(). глоин ( q ); // Ошибка, q выходит из globin(). глоин ( р ); // Хорошо, что r экранирует glein(). вернуть п ; // ХОРОШО. вернуть д ; // Ошибка, невозможно вернуть 'scope' q. вернуть р ; // ХОРОШО. }
Поддерживается бинарный интерфейс приложений C (ABI) , а также все фундаментальные и производные типы C, что обеспечивает прямой доступ к существующему коду и библиотекам C. Привязки D доступны для многих популярных библиотек C. Кроме того, стандартная библиотека C является частью стандарта D.
В Microsoft Windows D может получить доступ к коду модели компонентных объектов (COM).
Если об управлении памятью позаботиться должным образом, многие другие языки могут быть смешаны с D в одном двоичном файле. Например, компилятор GDC позволяет связывать и смешивать C, C++ и другие поддерживаемые языковые коды, такие как Objective-C. Код D (функции) также может быть помечен как использующий ABI C, C++, Pascal и, таким образом, передаваться в библиотеки, написанные на этих языках, в качестве обратных вызовов . Аналогичным образом данные могут обмениваться кодами, написанными на этих языках, обоими способами. Обычно это ограничивает использование примитивных типов, указателей, некоторых форм массивов, объединений , структур и только некоторых типов указателей на функции.
Поскольку многие другие языки программирования часто предоставляют API C для написания расширений или запуска интерпретатора языков, D также может напрямую взаимодействовать с этими языками, используя стандартные привязки C (с тонким файлом интерфейса D). Например, существуют двунаправленные привязки для таких языков, как Python , [26] Lua [27] [28] и других языков, часто использующие генерацию кода во время компиляции и методы отражения типов во время компиляции.
Для кода D, отмеченного как extern(C++)
, указаны следующие характеристики:
Пространства имен C++ используются через синтаксис, extern(C++, namespace)
где пространство имен — это имя пространства имен C++.
Сторона C++
#include <iostream> с использованием пространства имен std ; класс Base { public : virtual void print3i ( int a , int b , int c ) = 0 ; }; класс Произведенный : public Base { public : int field ; Производное ( целое поле ) : поле ( поле ) {} void print3i ( int a , int b , int c ) { cout << "a = " << a << endl ; cout << "b = " << b << endl ; cout << "c = " << c << endl ; } int mul ( int коэффициент ); }; int Derived::mul ( int Factor ) { возвращаемое поле * Factor ; } Derived * createInstance ( int i ) { return new Derived ( i ); } void deleteInstance ( Derived *& d ) { delete d ; д = 0 ; }
Сторона D
extern ( C ++) { абстрактный класс Base { void print3i ( int a , int b , int c ); } Производный класс : Base { int field ; @отключить это (); переопределить void print3i ( int a , int b , int c ); окончательный int mul ( int коэффициент ); } Производный createInstance ( int i ); void deleteInstance ( ref Derived d ); } void main () { import std . стдио ; авто d1 = createInstance ( 5 ); writeln ( d1 . поле ); writeln ( d1 . mul ( 4 )); База b1 = d1 ; б1 . print3i ( 1 , 2 , 3 ); удалитьэкземпляр ( d1 ); утверждать ( d1 имеет значение null ); авто d2 = createInstance ( 42 ); writeln ( d2 . поле ); удалить экземпляр ( d2 ); утверждать ( d2 имеет значение null ); }
Язык программирования D имеет официальное подмножество, известное как Better C. [29] Этот подмножество запрещает доступ к функциям D, требующим использования библиотек времени выполнения, отличных от библиотеки C.
Включенный с помощью флагов компилятора «-betterC» в DMD и LDC и «-fno-druntime» в GDC, Better C может вызывать только код D, скомпилированный под тем же флагом (и связанный код, отличный от D), но код, скомпилированный без Вариант Better C может вызывать код, скомпилированный с его помощью: однако это приведет к несколько разному поведению из-за различий в том, как C и D обрабатывают утверждения.
core.thread
)core.sync
Уолтер Брайт начал работу над новым языком в 1999 году. D был впервые выпущен в декабре 2001 года [1] и достиг версии 1.0 в январе 2007 года. [30] Первая версия языка (D1) была сосредоточена на императивном, объектно-ориентированном и метапрограммировании. парадигмы, [31] аналогичные C++.
Некоторые члены сообщества D, недовольные Phobos, официальной средой выполнения и стандартной библиотекой D , создали альтернативную среду выполнения и стандартную библиотеку под названием Tango. Первое публичное объявление о Tango произошло через несколько дней после выпуска D 1.0. [32] Tango приняла другой стиль программирования, включающий ООП и высокую модульность. Будучи проектом под руководством сообщества, Tango был более открыт для вкладов, что позволяло ему развиваться быстрее, чем официальная стандартная библиотека. В то время Tango и Phobos были несовместимы из-за разных API-интерфейсов поддержки времени выполнения (сборщик мусора, поддержка потоков и т. д.). Это сделало невозможным использование обеих библиотек в одном проекте. Существование двух библиотек, обе широко используемых, привело к серьезным спорам из-за того, что некоторые пакеты используют Phobos, а другие - Tango. [33]
В июне 2007 года была выпущена первая версия D2. [34] Начало развития D2 ознаменовало стабилизацию D1. Первая версия языка была помещена на обслуживание, в нее вносились только исправления и исправления ошибок реализации. D2 внесла в язык критические изменения , начиная со своей первой экспериментальной системы const . Позже в D2 были добавлены многочисленные другие функции языка, такие как замыкания , чистота и поддержка парадигм функционального и параллельного программирования. D2 также решил проблемы стандартной библиотеки, отделив среду выполнения от стандартной библиотеки. О завершении порта D2 Tango было объявлено в феврале 2012 года. [35]
Выпуск книги Андрея Александреску «Язык программирования D» 12 июня 2010 года ознаменовал стабилизацию языка D2, который сегодня обычно называют просто «D».
В январе 2011 года разработка D перешла с системы отслеживания ошибок/отправки исправлений на GitHub . Это привело к значительному увеличению вклада в компилятор, среду выполнения и стандартную библиотеку. [36]
В декабре 2011 года Андрей Александреску объявил, что поддержка D1, первой версии языка, будет прекращена 31 декабря 2012 года. [37] Последний выпуск D1, D v1.076, состоялся 31 декабря 2012 года. [38]
Код официального компилятора D, компилятора Digital Mars D Уолтера Брайта, изначально был выпущен под специальной лицензией , квалифицируясь как доступный исходный код , но не соответствующий определению открытого исходного кода . [39] В 2014 году интерфейс компилятора был повторно лицензирован как открытый исходный код по лицензии Boost Software License . [3] Этот повторно лицензированный код исключал серверную часть, которая была частично разработана в Symantec . 7 апреля 2017 года весь компилятор стал доступен по лицензии Boost после того, как Symantec разрешила также повторно лицензировать серверную часть. [4] [40] [41] [42] 21 июня 2017 года язык D был принят для включения в GCC. [43]
Большинство современных реализаций D компилируются непосредственно в машинный код .
Готовые к производству компиляторы:
Игрушечные и экспериментальные компиляторы:
Используя вышеуказанные компиляторы и наборы инструментов, можно скомпилировать программы D для множества различных архитектур, включая IA-32 , amd64 , AArch64 , PowerPC , MIPS64 , DEC Alpha , Motorola m68k , SPARC , s390 , WebAssembly . Основными поддерживаемыми операционными системами являются Windows и Linux , но различные компиляторы также поддерживают Mac OS X , FreeBSD , NetBSD , AIX , Solaris/OpenSolaris и Android либо в качестве хоста, либо в качестве целевого устройства, либо в качестве обоих. Цель WebAssembly (поддерживается через LDC и LLVM) может работать в любой среде WebAssembly, например в современном веб-браузере ( Google Chrome , Mozilla Firefox , Microsoft Edge , Apple Safari ) или выделенных виртуальных машинах Wasm.
Редакторы и интегрированные среды разработки (IDE), поддерживающие подсветку синтаксиса и частичное завершение кода для языка, включают SlickEdit , Emacs , vim , SciTE , Smultron , Zeus, [55] и Geany среди других. [56]
Существуют IDE D с открытым исходным кодом для Windows , некоторые из которых написаны на D, например Poseidon, [68] D-IDE, [69] и Entice Designer. [70]
Приложения D можно отлаживать с помощью любого отладчика C/C++, например GDB или WinDbg , хотя поддержка различных функций языка, специфичных для D, крайне ограничена. В Windows программы D можно отлаживать с помощью Ddbg или инструментов отладки Microsoft (WinDBG и Visual Studio) после преобразования отладочной информации с помощью cv2pdb. ZeroBUGS, заархивированный 23 декабря 2017 года в отладчике Wayback Machine для Linux, имеет экспериментальную поддержку языка D. Ddbg можно использовать с различными IDE или из командной строки; ZeroBUGS имеет собственный графический интерфейс пользователя (GUI).
DustMite — это инструмент для минимизации исходного кода D, полезный при обнаружении проблем с компилятором или тестированием. [71]
dub — популярный менеджер пакетов и сборки для приложений и библиотек D, который часто интегрируется в поддержку IDE. [72]
Этот пример программы печатает аргументы командной строки. Функция main
является точкой входа в программу D и args
представляет собой массив строк, представляющих аргументы командной строки. A string
в D — это массив символов, представленный immutable(char)[]
.
импорт стандартный . stdio : writefln ; void main ( string [] args ) { Еогеасп ( я , аргумент ; аргументы ) writefln ( "args[%d] = '%s'" , i , arg ); }
Оператор foreach
может перебирать любую коллекцию. В этом случае он создает последовательность индексов ( i
) и значений ( arg
) из массива args
. Типы индекса i
и значения arg
определяются из типа массива args
.
Ниже показаны некоторые возможности D и компромиссы при проектировании D в короткой программе. Он перебирает строки текстового файла с именем words.txt
, который содержит разные слова в каждой строке, и печатает все слова, которые являются анаграммами других слов.
импорт стандартный . стдио , станд . алгоритм , стандарт . диапазон , стандарт . нить ; пустая функция () { dstring [] [ dstring ] подпись2 слова ; foreach ( dchar [] w ; lines ( File ( "words.txt" ))) { ш = ш . чавкать (). понижать (); неизменяемая подпись = w . дуп . Сортировать (). выпускать (). идентификатор ; подпись2слова [ подпись ] ~= w . идентификатор ; } Еогеасп ( слова ; подпись2слова ) { если ( слова . длина > 1 ) { writeln ( words.join ( " " )) ; } }}
signature2words
— это встроенный ассоциативный массив, который сопоставляет ключи dstring (32-битные / символьные) с массивами dstrings. Это похоже defaultdict(list)
на Python .lines(File())
выдает строки лениво, с переводом строки. Затем его необходимо скопировать, idup
чтобы получить строку, которая будет использоваться для значений ассоциативного массива ( idup
свойство arrays возвращает неизменяемый дубликат массива, который необходим, поскольку dstring
на самом деле тип immutable(dchar)[]
). Встроенные ассоциативные массивы требуют неизменяемых ключей.~=
добавляет новую строку значений к значениям связанного динамического массива.toLower
и join
являются chomp
строковыми функциями, которые D позволяет использовать с синтаксисом метода. Имена таких функций часто похожи на строковые методы Python. Преобразует toLower
строку в нижний регистр, join(" ")
объединяет массив строк в одну строку, используя один пробел в качестве разделителя, и chomp
удаляет новую строку из конца строки, если она присутствует. Более w.dup.sort().release().idup
читабелен, но эквивалентен, release(sort(w.dup)).idup
например. Эта функция называется UFCS (унифицированный синтаксис вызова функций) и позволяет расширять любые встроенные или сторонние типы пакетов функциональностью, подобной методу. Подобный стиль написания кода часто называют конвейерным (особенно когда используемые объекты лениво вычисляются, например итераторы/диапазоны) или интерфейсом Fluent .sort
функция std.algorithm, которая сортирует массив на месте, создавая уникальную подпись для слов, которые являются анаграммами друг друга. Метод release()
возвращаемого значения sort()
удобен для хранения кода в виде одного выражения.foreach
перебирает значения ассоциативного массива и может определить тип words
.signature
присваивается неизменяемой переменной, выводится ее тип.dchar[]
используется UTF-32 , иначе сортировка отказывается. Есть более эффективные способы написать эту программу, используя только UTF-8. char[]
sort()
Известные организации, использующие язык программирования D для проектов, включают Facebook , [73] eBay , [74] и Netflix . [75]
D успешно использовался для игр AAA , [76] языковых интерпретаторов, виртуальных машин, [77] [78] ядра операционной системы , [79] программирования графического процессора , [80] веб-разработки , [81] [82] численного анализа , [83] Приложения с графическим пользовательским интерфейсом , [84] [85] Система информации о пассажирах , [86] Машинное обучение, [87] Обработка текста, веб-серверы и серверы приложений и исследования.
Печально известная северокорейская хакерская группа, известная как Lazarus , использовала CVE-2021-44228, также известную как « Log4Shell », для развертывания трех семейств вредоносных программ , написанных на DLang. [88]