Compare commits
41 Commits
b90bbc94c2
...
f0f0c72675
Author | SHA1 | Date |
---|---|---|
|
f0f0c72675 | |
|
8f7b9d5087 | |
|
4299579a93 | |
|
45190add84 | |
|
31a3cdc754 | |
|
22708324a5 | |
|
09fcedd370 | |
|
85400768e2 | |
|
723bb7dc56 | |
|
902c92d179 | |
|
55e58d277d | |
|
59446ae9e5 | |
|
3c8803c966 | |
|
3d1a3ae0b2 | |
|
ce34e74f04 | |
|
ca6cd093fa | |
|
c6967b5418 | |
|
6ef8226088 | |
|
e77d981888 | |
|
bfa6c8d96c | |
|
0f7274bd91 | |
|
13d3d50ecd | |
|
4673aaf466 | |
|
460bc91771 | |
|
db270b5c1b | |
|
9536065c69 | |
|
8b2176ad8a | |
|
d0026ae566 | |
|
7057f2ff5d | |
|
727e56a225 | |
|
671abb735c | |
|
740eda991e | |
|
b7c4623669 | |
|
7c95077668 | |
|
b8800a6e4a | |
|
b3bc331c14 | |
|
6803291dc3 | |
|
3e3e3455fe | |
|
90edfbb040 | |
|
d20a62d467 | |
|
8bd685439d |
|
@ -9,22 +9,14 @@
|
|||
|
||||
Обретая силу в простоте, язык программирования порождает красоту.
|
||||
|
||||
Поиск компромисса при противоречивых требованиях – сложная зада
|
||||
ча, для решения которой создателю языка требуется не только знание теоретических принципов и практической стороны дела, но и хороший вкус. Проектирование языка программирования – это последняя ступень мастерства в разработке программ.
|
||||
Поиск компромисса при противоречивых требованиях – сложная задача, для решения которой создателю языка требуется не только знание теоретических принципов и практической стороны дела, но и хороший вкус. Проектирование языка программирования – это последняя ступень мастерства в разработке программ.
|
||||
|
||||
D – это язык, который последовательно старается правильно действовать в пределах выбранных им ограничений, таких как доступ систем
|
||||
ного уровня к вычислительным ресурсам, высокая производительность и синтаксическая простота, к которой стремятся все произошедшие от C языки. Стараясь правильно действовать, D порой поступает традиционно – как другие языки, а порой ломает традиции с помощью свежего, инновационного решения. Иногда это приводило к пересмотру принципов, которым, казалось, D никогда не изменит. Например, большие фрагменты программного кода, а то и целые программы, могут быть написаны с помощью хорошо определенного, не допускающего ошибок памяти «безопасного подмножества» D. Ценой небольшого ограничения доступа на системном уровне приобретается огромное преимущество при отладке программ. D заинтересует вас, если для вас важны следующие аспекты:
|
||||
- *Производительность*. D – это язык для системного программирования. Его модель памяти, несмотря на сильную типизацию, совмес
|
||||
тима с моделью памяти C. Функции на D могут вызывать функции на C, а функции на C могут использовать функции D без каких-либо
|
||||
промежуточных преобразований.
|
||||
- *Выразительность*. D нельзя назвать небольшим, минималистичным языком, но его удельная мощность достаточно велика. Он по
|
||||
зволяет определять наглядные, не требующие объяснений инструкции, точно моделирующие сложные реалии.
|
||||
- *«Крутящий момент»*. Любой лихач-«самоделкин» скажет вам, что мощность еще не все – было бы где ее применить. На одних языках
|
||||
лучше всего пишутся маленькие программы. Синтаксические излишества других оправдываются только начиная с определенного объема программ. D одинаково эффективно помогает справляться и с короткими сценариями, и с большими программами, и для него отнюдь не редкость целый проект, органично вырастающий из простенького скрипта в единственном файле.
|
||||
- *Параллельные вычисления*. Подход к параллельным вычислениям – несомненное отличие D от похожих языков, отражающее разрыв ме
|
||||
жду современными аппаратными решениями и архитектурой компьютеров прошлого. D покончил с проклятьем неявного разделения памяти (хотя и допускает статически проверенное, явно заданное разделение) и поощряет независимые потоки, которые «общаются» друг с другом посредством сообщений.
|
||||
- *Обобщенное программирование*. Идея обобщенного кода, манипули рующего другим кодом, была впервые реализована в мощных макро
|
||||
сах Лиспа, затем в шаблонах C++, обобщенных классах Java и схожих конструкциях других языков. D также предлагает невероятно мощные механизмы обобщенного и порождающего программирования.
|
||||
D – это язык, который последовательно старается правильно действовать в пределах выбранных им ограничений, таких как доступ системного уровня к вычислительным ресурсам, высокая производительность и синтаксическая простота, к которой стремятся все произошедшие от C языки. Стараясь правильно действовать, D порой поступает традиционно – как другие языки, а порой ломает традиции с помощью свежего, инновационного решения. Иногда это приводило к пересмотру принципов, которым, казалось, D никогда не изменит. Например, большие фрагменты программного кода, а то и целые программы, могут быть написаны с помощью хорошо определенного, не допускающего ошибок памяти «безопасного подмножества» D. Ценой небольшого ограничения доступа на системном уровне приобретается огромное преимущество при отладке программ. D заинтересует вас, если для вас важны следующие аспекты:
|
||||
- *Производительность*. D – это язык для системного программирования. Его модель памяти, несмотря на сильную типизацию, совместима с моделью памяти C. Функции на D могут вызывать функции на C, а функции на C могут использовать функции D без каких-либо промежуточных преобразований.
|
||||
- *Выразительность*. D нельзя назвать небольшим, минималистичным языком, но его удельная мощность достаточно велика. Он позволяет определять наглядные, не требующие объяснений инструкции, точно моделирующие сложные реалии.
|
||||
- *«Крутящий момент»*. Любой лихач-«самоделкин» скажет вам, что мощность еще не все – было бы где ее применить. На одних языках лучше всего пишутся маленькие программы. Синтаксические излишества других оправдываются только начиная с определенного объема программ. D одинаково эффективно помогает справляться и с короткими сценариями, и с большими программами, и для него отнюдь не редкость целый проект, органично вырастающий из простенького скрипта в единственном файле.
|
||||
- *Параллельные вычисления*. Подход к параллельным вычислениям – несомненное отличие D от похожих языков, отражающее разрыв между современными аппаратными решениями и архитектурой компьютеров прошлого. D покончил с проклятьем неявного разделения памяти (хотя и допускает статически проверенное, явно заданное разделение) и поощряет независимые потоки, которые «общаются» друг с другом посредством сообщений.
|
||||
- *Обобщенное программирование*. Идея обобщенного кода, манипули рующего другим кодом, была впервые реализована в мощных макросах Лиспа, затем в шаблонах C++, обобщенных классах Java и схожих конструкциях других языков. D также предлагает невероятно мощные механизмы обобщенного и порождающего программирования.
|
||||
- *Эклектизм*. D подразумевает, что каждая парадигма программирования ориентирована на свою задачу разработки. Поэтому он предполагает высокоинтегрированный объединенный стиль программирования, а не Единственно Верный Подход.
|
||||
- *«Это мои принципы. А если они вам не нравятся, то у меня есть и другие»*[^1]. D старается всегда следовать своим принципам устройства языка. Иногда они идут вразрез с соображениями сложности реализации и трудностей использования и, главное, с человеческой природой, которая не всегда находит скрытую логику здравой и интуитивно понятной. В таких случаях все языки полагаются на собственное бесконечно субъективное понимание баланса, гибкости и – особенно – хорошего вкуса. На мой взгляд, D как минимум неплохо смотрится на фоне других языков, разработчикам которых приходилось принимать решения того же плана.
|
||||
|
||||
|
|
|
@ -72,8 +72,8 @@
|
|||
|
||||
*Таблица 2.1. Основные типы данных D*
|
||||
|
||||
|Тип данных|Описание|Начальное значение по умолчанию|
|
||||
|-|-|-|
|
||||
| Тип данных | Описание | Начальное<br>значение<br>по умолчанию |
|
||||
| --- | --- | --- |
|
||||
| `void` | Без значения | `n/a` |
|
||||
| `typeof(null)` | Тип константы `null` | `n/a` |
|
||||
| `bool` | Логическое (булево) значение | `false` |
|
||||
|
@ -224,8 +224,8 @@ auto
|
|||
|
||||
*Таблица 2.3. Экранирующие последовательности в D*
|
||||
|
||||
|Escape-последовательность|Тип|Описание|
|
||||
|-|-|-|
|
||||
| Escape<br>последовательность | Тип | Описание |
|
||||
| --- | --- | --- |
|
||||
| `\"` | `char` | Двойная кавычка (если двусмысленно) |
|
||||
| `\\` | `char` | Обратная косая черта |
|
||||
| `\a` | `char` | Звуковой сигнал (Bell, ASCII 7) |
|
||||
|
@ -708,7 +708,7 @@ bool
|
|||
*Таблица 2.4. Зависимости для значения `Идентификатор` в выражении `is(Тип Идентификатор == Вид)`*
|
||||
|
||||
| Вид | Идентификатор – псевдоним для ... |
|
||||
|-|-|
|
||||
| --- | --- |
|
||||
| `struct` | `Тип` |
|
||||
| `union` | `Тип` |
|
||||
| `class` | `Тип` |
|
||||
|
@ -934,19 +934,11 @@ foreach (ref row; matrix)
|
|||
|
||||
### 2.3.10. Сдвиг
|
||||
|
||||
В языке D есть три операции сдвига, в каждой из которых участвуют
|
||||
два целочисленных операнда: `a << b`, `a >> b` и `a >>> b`. Во всех случаях
|
||||
значение `b` должно иметь тип без знака; значение со знаком необходимо
|
||||
привести к значению беззнакового типа (разумеется, предварительно
|
||||
убедившись, что `b >= 0`; результат сдвига на отрицательное количество
|
||||
разрядов непредсказуем). `a << b` сдвигает a влево (то есть в направлении
|
||||
В языке D есть три операции сдвига, в каждой из которых участвуют два целочисленных операнда: `a << b`, `a >> b` и `a >>> b`. Во всех случаях значение `b` должно иметь тип без знака; значение со знаком необходимо привести к значению беззнакового типа (разумеется, предварительно убедившись, что `b >= 0`; результат сдвига на отрицательное количество разрядов непредсказуем). `a << b` сдвигает a влево (то есть в направлении
|
||||
самого старшего разряда `a`) на `b` бит, а `a >> b` сдвигает `a` вправо на `b` бит.
|
||||
Если `a` – отрицательное число, знак после сдвига сохраняется.
|
||||
|
||||
`a >>> b` – это беззнаковый сдвиг независимо от знаковости `a`. Это означа
|
||||
ет, что ноль гарантированно займет самый старший разряд `a`. Проил
|
||||
люстрируем сюрпризы, которые готовит применение операции сдвига
|
||||
к числам со знаком:
|
||||
`a >>> b` – это беззнаковый сдвиг независимо от знаковости `a`. Это означает, что ноль гарантированно займет самый старший разряд `a`. Проиллюстрируем сюрпризы, которые готовит применение операции сдвига к числам со знаком:
|
||||
|
||||
```d
|
||||
int a = -1; // То есть 0xFFFF_FFFF
|
||||
|
@ -1183,7 +1175,7 @@ int c = (a = b, b = 7, 8);
|
|||
*Таблица 2.5. Выражения D порядке убывания приоритета*
|
||||
|
||||
| Выражение | Описание |
|
||||
|-|-|
|
||||
| --- | --- |
|
||||
| `<идентификатор>` | Идентификатор (см. раздел [2.1](#2-1-идентификаторы)) |
|
||||
| `.<идентификатор>` | Идентификатор, доступный в пространстве имен модуля (в обход всех друг пространств имен) (см. раздел [2.1](#2-1-идентификаторы)) |
|
||||
| `this` | Текущий объект внутри метода (см. раздел [2.1.1](#2-1-1-ключевые-слова)) |
|
||||
|
@ -1198,7 +1190,7 @@ int c = (a = b, b = 7, 8);
|
|||
| `<строка>` | Строковый литерал (см. раздел [2.2.5](#2-2-5-строковые-литералы)) |
|
||||
| `<массив>` | Литерал массива (см. раздел [2.2.6](#2-2-6-литералы-массивов-и-ассоциативных-массивов)) |
|
||||
| `<функция>` | Функциональный литерал (см. раздел [2.2.7](#2-2-7-функциональные-литералы)) |
|
||||
|`assert(a)`|В режиме отладки, если a не является ненулевым значением, выполнение программы прерывается; в режиме итоговой сборки (release) ничего не происходит (см. раздел [2.3.4.1](#2-3-4-1-выражение-assert))|
|
||||
| `assert(a)` | В режиме отладки, если a не является ненулевым значением, выполнение программы прерывается;<br>в режиме итоговой сборки (release) ничего не происходит (см. раздел [2.3.4.1](#2-3-4-1-выражение-assert)) |
|
||||
| `assert(a, b)` | То же, но к сообщению об ошибке добавляется `b` (см. раздел [2.3.4.1](#2-3-4-1-выражение-assert)) |
|
||||
| `mixin(a)` | Выражение `mixin` (см. раздел [2.3.4.2](#2-3-4-2-выражение-mixin)) |
|
||||
| `<IsExpr>` | Выражение `is` (см. раздел [2.3.4.3](#2-3-4-3-выражения-is)) |
|
||||
|
@ -1233,7 +1225,7 @@ int c = (a = b, b = 7, 8);
|
|||
| `a >> b` | Сдвиг вправо (см. раздел [2.3.10](#2-3-10-сдвиг)) |
|
||||
| `a >>> b` | Беззнаковый сдвиг вправо (старший разряд сбрасывается независимо от типа и значения `a`) (см. раздел [2.3.10](#2-3-10-сдвиг)) |
|
||||
| `a in b` | Проверка на принадлежность для ассоциативных массивов (см. раздел [2.3.11](#2-3-11-выражения-in)) |
|
||||
|`a == b`|Проверка на равенство; все операторы этой группы неассоциативны; например, выражение `a == b == c` некорректно (см. раздел [2.3.12.1](#2-3-12-1-проверка-на-равенство))|
|
||||
| `a == b` | Проверка на равенство;<br>все операторы этой группы неассоциативны;<br>например, выражение `a == b == c` некорректно (см. раздел [2.3.12.1](#2-3-12-1-проверка-на-равенство)) |
|
||||
| `a != b` | Проверка на неравенство (см. раздел [2.3.12.1](#2-3-12-1-проверка-на-равенство)) |
|
||||
| `a is b` | Проверка на идентичность (`true`, если и только если `a` и `b` ссылаются на один и тот же объект) (см. раздел [2.3.12.1](#2-3-12-1-проверка-на-равенство)) |
|
||||
| `a !is b` | То же, что `!(a is b)` |
|
||||
|
@ -1247,8 +1239,8 @@ int c = (a = b, b = 7, 8);
|
|||
| `a && b` | Логическое **И** (`b` может иметь тип `void`) (см. раздел [2.3.14](#2-3-14-логическое-и)) |
|
||||
| `a \|\| b` | Логическое **ИЛИ** (`b` может иметь тип `void`) (см. раздел [2.3.15](#2-3-15-логическое-или)) |
|
||||
| `a ? b : c` | Тернарная условная операция; если операнд `a` имеет ненулевое значение, то `b`, иначе `с` (см. раздел [2.3.16](#2-3-16-тернарная-условная-операция)) |
|
||||
|`a = b`|Присваивание; все операторы присваивания этой группы ассоциативны справа; например `a *= b += c` – то же, что и `a *= (b += c)` (см. раздел [2.3.17](#2-3-17-присваивание))|
|
||||
|`a += b`|Сложение «на месте»; выражения со всеми операторами вида `a ω= b`, работающими по принципу «вычислить и присвоить», вычисляются в следующем порядке: **1)** `a` (должно быть l-значением), **2)** `b` и **3)** `al = al ω b`, где `al` – l-значение, получившееся в результате вычисления `a`|
|
||||
| `a = b` | Присваивание;<br>все операторы присваивания этой группы ассоциативны справа;<br>например `a *= b += c` – то же, что и `a *= (b += c)` (см. раздел [2.3.17](#2-3-17-присваивание)) |
|
||||
| `a += b` | Сложение «на месте»;<br>выражения со всеми операторами вида `a ω= b`, работающими по принципу «вычислить и присвоить», вычисляются в следующем порядке:<br>**1)** `a` (должно быть l-значением),<br>**2)** `b` и<br>**3)** `al = al ω b`, где `al` – l-значение, получившееся в результате вычисления `a` |
|
||||
| `a -= b` | Вычитание «на месте» |
|
||||
| `a *= b` | Умножение «на месте» |
|
||||
| `a /= b` | Деление «на месте» |
|
||||
|
|
|
@ -1098,7 +1098,7 @@ D предоставляет все ожидаемые обычные инстр
|
|||
*Таблица 3.1. Справочник по инструкциям (`‹и›` – инструкция, `‹в›` – выражение, `‹o›` – объявление, `‹х›` – идентификатор)*
|
||||
|
||||
| Инструкция | Описание |
|
||||
|-|-|
|
||||
| --- | --- |
|
||||
| `‹в›;` | Вычисляет `‹в›`. Ничего не изменяющие выражения, включающие лишь встроенные типы и операторы, запрещены (см. раздел [3.1](#3-1-инструкция-выражение)) |
|
||||
| `{‹и1› ... ‹и2›}` | Выполняет инструкции от `‹и1›` до `‹и2›` по порядку, пока управление не будет явно передано в другую область видимости (например, инструкцией `return`) (см. раздел [3.2](#3-2-составная-инструкция)) |
|
||||
| `asm ‹и›` | Машиннозависимый ассемблерный код (здесь `‹и›` обозначает ассемблерный код, а не инструкцию на языке D). В настоящее время поддерживается ассемблер x86 с единым синтаксисом для всех поддерживаемых операционных систем (см. раздел [3.15](#3-15-конструкция-asm)) |
|
||||
|
|
|
@ -202,7 +202,7 @@ D различает «безопасные» (safe) и «системные» (
|
|||
*Таблица 4.1. Проверка границ в зависимости от вида модуля и режима сборки*
|
||||
|
||||
| | Безопасный модуль | Системный модуль |
|
||||
|-|:-:|:-:|
|
||||
| --- | :-: | :-: |
|
||||
| Промежуточная сборка | ✓ | ✓ |
|
||||
| Итоговая сборка (флаг `-release` для компилятора `dmd`) | ✓ | ☠ |
|
||||
|
||||
|
@ -263,16 +263,16 @@ auto a = [1, 5, 2, 3, 6];
|
|||
|
||||
Инициализация массива другим массивом (`auto b = a`), равно как и присваивание одного массива другому (`int[] b; … b = a;`) не влечет скрытого автоматического копирования данных. Как показано на рис. 4.2, эти действия просто заставляют `b` ссылаться на ту же область памяти, что и `a`.
|
||||
|
||||

|
||||
|
||||
***Рис. 4.2.*** *При выполнении инструкции `auto b = a;` содержимое a не копируется: вместо этого создается объект типа «массив», который ссылается на те же данные*
|
||||
|
||||
Более того, получение среза массива `b` сокращает область памяти, «видимую» `b`, также без всякого копирования `b`. При условии что исходное состояние массива задано на рис. 4.2, выполнение инструкции
|
||||
|
||||
```d
|
||||
b = b[1 .. $ - 2];
|
||||
```
|
||||
|
||||

|
||||
|
||||
***Рис. 4.2.*** *При выполнении инструкции `auto b = a;` содержимое a не копируется: вместо этого создается объект типа «массив», который ссылается на те же данные*
|
||||
|
||||
приведет лишь к сокращению диапазона, доступного `b`, без какого-либо копирования данных (рис. 4.3).
|
||||
|
||||

|
||||
|
@ -769,7 +769,9 @@ double[5] e = a ~ d; // Все в порядке, явный запрос ма
|
|||
Поскольку запись `T[]` означает динамический массив элементов типа `T`, а `T[]`, в свою очередь, – тоже тип, легко сделать вывод, что `T[][]` – это массив элементов типа `T[]`, то есть массив массивов элементов типа `T`. Каждый элемент «внешнего» массива – это, в свою очередь, тоже массив, предоставляющий обычную функциональность, присущую массивам. Рассмотрим `T[][]` на практике:
|
||||
|
||||
```d
|
||||
auto array = new double[][5]; // Массив из пяти массивов, содержащих элементы типа double, первоначально каждый из них – null
|
||||
auto array = new double[][5]; // Массив из пяти массивов, содержащих элементы типа double,
|
||||
// первоначально каждый из них – null
|
||||
|
||||
// Сделать треугольную матрицу
|
||||
foreach (i, ref e; array)
|
||||
{
|
||||
|
@ -1050,7 +1052,7 @@ foreach (k; gammaFunc.byKey())
|
|||
*Таблица 4.2. Битовые представления UTF-8. Длина представления определяется по контрольным битам, что позволяет выполнять синхронизацию посреди потока, восстановление после ошибок и просмотр строки в обратном направлении*
|
||||
|
||||
| Кодовая точка (в шестнадцатиричном представлении) | Бинарное представление |
|
||||
|-|-|
|
||||
| --- | --- |
|
||||
| `00000000–0000007F` | `0xxxxxxx` |
|
||||
| `00000080–000007FF` | `110xxxxx 10xxxxxx` |
|
||||
| `00000800–0000FFFF` | `1110xxxx 10xxxxxx 10xxxxxx` |
|
||||
|
@ -1287,7 +1289,7 @@ y += 100; // Хм...
|
|||
*Таблица 4.3. Операции над динамическими массивами (`a` и `b` – два значения типа `T[]`; `t`, `t1`, ..., `tk` – значения типа `T`; `n` – значение, приводимое к типу `размер_t`)*
|
||||
|
||||
| Выражение | Тип | Описание |
|
||||
|-|-|-|
|
||||
| --- | --- | --- |
|
||||
| `new T[n]` | `T[]` | Создает массив (см. раздел [4.1](#4-1-динамические-массивы)) |
|
||||
| `[t1,t2, ..., tk]` | `T[]` | Литерал массива; `T` определяется по типу `t1` (см. разделы [2.2.6](../02-%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D1%8B%D0%B5-%D1%82%D0%B8%D0%BF%D1%8B-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85-%D0%B2%D1%8B%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F#2-2-6-литералы-массивов-и-ассоциативных-массивов) и [4.1](#4-1-динамические-массивы)) |
|
||||
| `a = b` | `T[]` | Присваивает один массив другому (см. раздел [4.1.4](#4-1-4-копирование)) |
|
||||
|
@ -1311,7 +1313,7 @@ y += 100; // Хм...
|
|||
*Таблица 4.4. Операции над массивами фиксированной длины (`a` и `b` – два значения типа `T[]`; `t`, `t1`, ..., `tk` – значения типа `T`; `n` – значение, приводимое к типу `размер_t`)*
|
||||
|
||||
| Выражение | Тип | Описание |
|
||||
|-|-|-|
|
||||
| --- | --- | --- |
|
||||
| `[t1, ..., tk]` | `T[k]` | Литерал массива, но только если тип `T[k]` запрошен явно; `T` определяется по типу `t1` (см. разделы [2.2.6](../02-%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D1%8B%D0%B5-%D1%82%D0%B8%D0%BF%D1%8B-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85-%D0%B2%D1%8B%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F#2-2-6-литералы-массивов-и-ассоциативных-массивов) и [4.1](#4-1-динамические-массивы)) |
|
||||
| `a = b` | `ref T[n]` | Копирует содержимое одного массива в другой (см. раздел [4.2.4](#4-2-4-копирование-и-неявные-преобразования)) |
|
||||
| `a[‹в›]` | `ref T` | Предоставляет доступ к элементу по индексу (символ `$` в `‹в›` заменяется на `a.length`, `‹в›` должно быть приводимым к типу `размер_t`; кроме того, должно соблюдаться условие `‹в› < a.length`) (см. раздел [4.1](#4-1-динамические-массивы)) |
|
||||
|
@ -1331,7 +1333,7 @@ y += 100; // Хм...
|
|||
*Таблица 4.5. Операции над ассоциативными массивами (`a` и `b` – два значения типа `V[K]`; `k`, `k1`, ..., `ki` – значения типа `K`; `v`, `v1`, ..., `vk` – значения типа `V`)*
|
||||
|
||||
| Выражение | Тип | Описание |
|
||||
|-|-|-|
|
||||
| --- | --- | --- |
|
||||
| `[t1:v1, ..., ti:vi]` | `V[K]` | Литерал ассоциативного массива; `K` определяется по типу `k1`, а `V` – по типу `v1` (см. разделы [2.2.6](../02-%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D1%8B%D0%B5-%D1%82%D0%B8%D0%BF%D1%8B-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85-%D0%B2%D1%8B%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F#2-2-6-литералы-массивов-и-ассоциативных-массивов) и [4.4](#4-4-ассоциативные-массивы)) |
|
||||
| `a = b` | `V[K]` | Присваивает ассоциативный массив `b` переменной a типа «ассоциативный массив» (см. раздел [4.4.3](#4-4-3-копирование)) |
|
||||
| `a[k]` | `V` | Предоставляет доступ к элементу по индексу (если ключ `k` не найден, возникает исключение) (см. раздел [4.4.2](#4-4-2-чтение-и-запись-ячеек)) |
|
||||
|
|
|
@ -219,7 +219,7 @@ void fun(in int[][] data)
|
|||
|
||||
### 5.2.3. Выходные параметры (с ключевым словом out)
|
||||
|
||||
Иногда параметры передаются по ссылке только для того, чтобы функция с их помощью что-то вернула. В таких случаях можно воспользоваться классом памяти `out`, напоминающим `ref`, – разница лишь в том, что перед входом в функцию `out` инициализирует свой аргумент значением по умолчанию (соответствующим типу аргумента):
|
||||
Иногда параметры передаются по ссылке только для того, чтобы функция с их помощью что-то вернула. В таких случаях можно воспользоваться классом памяти `out`, напоминающим `ref` – разница лишь в том, что перед входом в функцию `out` инициализирует свой аргумент значением по умолчанию (соответствующим типу аргумента):
|
||||
|
||||
```d
|
||||
// Вычисляет частное и остаток от деления для аргументов a и b.
|
||||
|
@ -258,7 +258,8 @@ void log(string message)
|
|||
writeln(message);
|
||||
}
|
||||
...
|
||||
int result = foo(); log("foo() returned " ~ to!string(result));
|
||||
int result = foo();
|
||||
log("foo() returned " ~ to!string(result));
|
||||
```
|
||||
|
||||
Как видим, вычислять выражение `"foo() returned " ~ to!string(result)` нужно, только если переменная `verbose` имеет значение `true`. При этом выражение, передаваемое этой функции в качестве аргумента, будет вычислено в любом случае. В данном примере это конкатенация двух строк, которая потребует выделения памяти и копирования в нее содержимого каждой из них. И все это для того, чтобы узнать, что переменная `verbose` имеет значение `false` и значение аргумента никому не нужно! Можно было бы передавать вместо строки делегат, возвращающий строку (делегаты описаны в разделе 5.6.1):
|
||||
|
@ -269,7 +270,8 @@ void log(string delegate() message)
|
|||
if (verbose)
|
||||
writeln(message());
|
||||
}
|
||||
...log({return "foo() returned " ~ to!string(result);});
|
||||
...
|
||||
log({return "foo() returned " ~ to!string(result);});
|
||||
```
|
||||
|
||||
В этом случае аргумент будет вычислен, только если он действительно нужен, но такая форма слишком громоздка. Поэтому D вводит такое понятие, как «ленивые» аргументы. Такие аргументы объявляются с атрибутом `lazy`, выглядят как обычные аргументы, но вычисляются только тогда, когда требуется их значение.
|
||||
|
@ -869,7 +871,7 @@ T[] find(T)(T[] haystack, T needle)
|
|||
2. `haystack[0]` осуществляет доступ к первому элементу `haystack`.
|
||||
3. `haystack = haystack[1 .. $]` исключает из рассмотрения первый элемент `haystack`.
|
||||
|
||||
Конкретный способ, каким массивы реализуют эти операции, непросто распространить на другие контейнеры. Например, проверять с помощью выражения `haystack.length > 0`, есть ли в односвязном списке элементы, – подход, достойный премии Дарвина[^9]. Если не обеспечено постоянное кэширование длины списка (что по многим причинам весьма проблематично), то для вычисления длины списка таким способом потребуется время, пропорциональное самой длине списка, а быстрое обращение к началу списка занимает всего лишь несколько машинных инструкций. Применить к спискам индексацию – столь же проигрышная идея. Так что выделим сущность рассмотренных операций, представим полученный результат в виде трех именованных функций и оставим их реализацию типу `haystack`. Примерный синтаксис базовых операций, необходимых для реализации алгоритма линейного поиска:
|
||||
Конкретный способ, каким массивы реализуют эти операции, непросто распространить на другие контейнеры. Например, проверять с помощью выражения `haystack.length > 0`, есть ли в односвязном списке элементы – подход, достойный премии Дарвина[^9]. Если не обеспечено постоянное кэширование длины списка (что по многим причинам весьма проблематично), то для вычисления длины списка таким способом потребуется время, пропорциональное самой длине списка, а быстрое обращение к началу списка занимает всего лишь несколько машинных инструкций. Применить к спискам индексацию – столь же проигрышная идея. Так что выделим сущность рассмотренных операций, представим полученный результат в виде трех именованных функций и оставим их реализацию типу `haystack`. Примерный синтаксис базовых операций, необходимых для реализации алгоритма линейного поиска:
|
||||
|
||||
1. `haystack.empty` – для проверки `haystack` на пустоту.
|
||||
2. `haystack.front` – для получения первого элемента `haystack`.
|
||||
|
@ -1008,7 +1010,7 @@ unittest
|
|||
}
|
||||
```
|
||||
|
||||
(Обратите внимание на очередное удачное использование `reduce`.) Интересная деталь функции `average`: многоточие ... после параметра `values`, который является срезом. (Если бы это было не так или если бы параметр `values` не был последним в списке аргументов функции `average`, компилятор диагностировал бы это многоточие как ошибку.)
|
||||
(Обратите внимание на очередное удачное использование `reduce`.) Интересная деталь функции `average`: многоточие `...` после параметра `values`, который является срезом. (Если бы это было не так или если бы параметр `values` не был последним в списке аргументов функции `average`, компилятор диагностировал бы это многоточие как ошибку.)
|
||||
|
||||
Вызов функции `average` со срезом массива элементов типа `double` (как показано в последней строке теста модуля) ничем не примечателен. Однако благодаря многоточию эту функцию можно вызывать с любым числом аргументов, при условии что каждый из них можно привести к типу `double`. Компилятор автоматически сформирует из этих аргументов срез и передаст его в `average`.
|
||||
|
||||
|
|
|
@ -161,13 +161,11 @@ unittest
|
|||
|
||||
Вместо трех последних строк можно было бы использовать универсальную вспомогательную функцию `swap` из модуля `std.algorithm`: `swap(a1, a2)`, но явная запись процесса обмена нагляднее. На рис. 6.2 продемонстрированы привязки до и после обмена.
|
||||
|
||||
Сами объекты остаются на том же месте, то есть после создания они никогда не перемещаются в памяти. Просто замечательно, объект никогда не исчезнет: можно рассчитывать, что объект навсегда останется там, куда он был помещен при создании. (Сборщик мусора перерабатывает в фоновом режиме те объекты, которые больше не используются.) Ссылки на объекты (в данном случае `a1` и `a2`) можно заставить «смотреть в другую сторону», переназначив их привязку. Когда библиотека времени исполнения обнаруживает, что для какого-то объекта больше нет привязанных к нему ссылок, она может заново использовать выделенную под него память (этот процесс называется сбором мусора).[^1] Такое поведение
|
||||
|
||||

|
||||
|
||||
***Рис. 6.2.*** *Привязки до и после обмена. В процессе обмена меняются привязки к ссылкам; сами объекты остаются на том же месте*
|
||||
|
||||
в корне отличается от семантики *значения* (например, `int`), в случае которого нет никаких косвенных изменений или привязок: каждое имя прочно закреплено за значением, которым манипулируют с помощью этого идентификатора.
|
||||
Сами объекты остаются на том же месте, то есть после создания они никогда не перемещаются в памяти. Просто замечательно, объект никогда не исчезнет: можно рассчитывать, что объект навсегда останется там, куда он был помещен при создании. (Сборщик мусора перерабатывает в фоновом режиме те объекты, которые больше не используются.) Ссылки на объекты (в данном случае `a1` и `a2`) можно заставить «смотреть в другую сторону», переназначив их привязку. Когда библиотека времени исполнения обнаруживает, что для какого-то объекта больше нет привязанных к нему ссылок, она может заново использовать выделенную под него память (этот процесс называется сбором мусора).[^1] Такое поведение в корне отличается от семантики *значения* (например, `int`), в случае которого нет никаких косвенных изменений или привязок: каждое имя прочно закреплено за значением, которым манипулируют с помощью этого идентификатора.
|
||||
|
||||
Ссылка, не привязанная к какому-либо объекту, – это «пустая» ссылка (`null`). При инициализации по умолчанию с помощью свойства `.init` ссылки на классы получают значение `null`. Можно сравнивать ссылку с константой `null` и присваивать ссылке значение `null`. Следующие проверки пройдут успешно:
|
||||
|
||||
|
@ -401,10 +399,11 @@ this(uint h)
|
|||
|
||||
1. *Выделение памяти*. Библиотека времени исполнения выделяет участок «сырой» памяти в куче, достаточный для размещения нестатических полей объекта. Память подо все объекты, основанные на классах, выделяется динамически – в отличие от C++, в D нет способа выделить для объекта память в стеке[^2]. Если выделить память не удалось, построение объекта прерывается: порождается исключительная ситуация.
|
||||
|
||||
*Инициализация полей*. Каждое поле инициализируется своим значением по умолчанию. Как уже говорилось, в качестве значения поля по умолчанию выступает значение, указанное при объявлении поля в виде `= значение`, или при отсутствии такой записи значение свойства `.init` типа поля.
|
||||
2. *Инициализация полей*. Каждое поле инициализируется своим значением по умолчанию. Как уже говорилось, в качестве значения поля по умолчанию выступает значение, указанное при объявлении поля в виде `= значение`, или при отсутствии такой записи значение свойства `.init` типа поля.
|
||||
|
||||
2. *Брендирование*. После завершения инициализации полей значениями по умолчанию объекту присваивается статус полноправного экземпляра класса `T` (объект брендируется) еще *до* того, как будет вызван настоящий конструктор.
|
||||
3. *Вызов конструктора*. Наконец, компилятор инициирует вызов подходящего конструктора. Если класс не определяет собственный конструктор, этот шаг пропускается.
|
||||
3. *Брендирование*. После завершения инициализации полей значениями по умолчанию объекту присваивается статус полноправного экземпляра класса `T` (объект брендируется) еще *до* того, как будет вызван настоящий конструктор.
|
||||
|
||||
4. *Вызов конструктора*. Наконец, компилятор инициирует вызов подходящего конструктора. Если класс не определяет собственный конструктор, этот шаг пропускается.
|
||||
|
||||
Поскольку объект считается «живым» и правильно построенным сразу после инициализации по умолчанию, настоятельно рекомендуется использовать инициализирующие значения, которые всегда приводят объект в осмысленное состояние. Настоящий конструктор внесет затем свои поправки, приведя объект в другое интересное состояние (разумеется, также осмысленное).
|
||||
|
||||
|
@ -1375,7 +1374,9 @@ void main()
|
|||
void widgetize()
|
||||
{
|
||||
Widget w = new Widget;
|
||||
.../* Использование w */...
|
||||
...
|
||||
/* Использование w */
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -1385,7 +1386,9 @@ void widgetize()
|
|||
void widgetize()
|
||||
{
|
||||
Widget w = new TextWidget;
|
||||
.../* Использование w */...
|
||||
...
|
||||
/* Использование w */
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -1395,7 +1398,9 @@ void widgetize()
|
|||
void widgetize(string widgetClass)
|
||||
{
|
||||
Widget w = cast(Widget) Object.factory(widgetClass);
|
||||
... /* Использование w */...
|
||||
...
|
||||
/* Использование w */
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
|
||||
Применяя классы, основные типы и функции, можно написать много хороших программ. С параметризированными классами и функциями дело идет еще лучше. Но нередко мы с сожалением отмечаем, что по нескольким причинам классы не представляют собой инструмент с максимальной абстракцией типа.
|
||||
|
||||
Во-первых, классы подчиняются ссылочной семантике и из-за этого могут воплощать многие проектные решения не полностью или с ощутимыми накладными расходами. На практике трудно моделировать с помощью класса такую простую сущность, как точка с двумя или тремя координатами, если таких точек больше нескольких миллионов: разработчик оказывается перед непростым выбором – хорошая абстракция или приемлемое быстродействие. Кроме того, для линейной алгебры ссылочная семантика – большая морока. Попробуйте убедить математика или программиста-теоретика, что присваивание `a = b` должно делать из матрицы a лишь псевдоним матрицы `b`, а не отдельную копию! Даже такой простой тип, как массив, довольно накладно моделировать в виде класса в сравнении с мощной и лаконичной абстракцией массива, имеющейся в языке D (см. главу 4). Можно, конечно, сделать массивы «волшебными», но опыт то и дело показывает, что предоставлять множество «волшебных» типов, не воспроизводимых в пользовательском коде, – дурной тон и признак плохо спроектированного языка. Затраты на массив – всего два слова, а выделение памяти под экземпляр класса и использование дополнительного косвенного обращения означают большие накладные расходы по памяти и времени для всех примитивов массива. Даже такой простой тип, как `int`, нельзя выразить в виде класса дешево и элегантно (причем речь не об удобстве оператора). У такого класса, как `BigInt`, та же проблема: `a = b` делает нечто совершенно иное,
|
||||
Во-первых, классы подчиняются ссылочной семантике и из-за этого могут воплощать многие проектные решения не полностью или с ощутимыми накладными расходами. На практике трудно моделировать с помощью класса такую простую сущность, как точка с двумя или тремя координатами, если таких точек больше нескольких миллионов: разработчик оказывается перед непростым выбором – хорошая абстракция или приемлемое быстродействие. Кроме того, для линейной алгебры ссылочная семантика – большая морока. Попробуйте убедить математика или программиста-теоретика, что присваивание `a = b` должно делать из матрицы `a` лишь псевдоним матрицы `b`, а не отдельную копию! Даже такой простой тип, как массив, довольно накладно моделировать в виде класса в сравнении с мощной и лаконичной абстракцией массива, имеющейся в языке D (см. главу 4). Можно, конечно, сделать массивы «волшебными», но опыт то и дело показывает, что предоставлять множество «волшебных» типов, не воспроизводимых в пользовательском коде, – дурной тон и признак плохо спроектированного языка. Затраты на массив – всего два слова, а выделение памяти под экземпляр класса и использование дополнительного косвенного обращения означают большие накладные расходы по памяти и времени для всех примитивов массива. Даже такой простой тип, как `int`, нельзя выразить в виде класса дешево и элегантно (причем речь не об удобстве оператора). У такого класса, как `BigInt`, та же проблема: `a = b` делает нечто совершенно иное,
|
||||
чем соответствующая операция присваивания для типа `int`.
|
||||
|
||||
Во-вторых, классы живут вечно, а значит, с их помощью трудно моделировать ресурсы с выраженным *конечным* временем жизни (такие как дескрипторы файлов, дескрипторы графического контекста, мьютексы, сокеты и т. д.). Работая с такими ресурсами как с классами, нужно постоянно быть начеку, чтобы не забыть своевременно освободить инкапсулированные ресурсы с помощью метода, вроде `close` или `dispose`. В таких случаях обычно помогает инструкция `scope` (см. раздел 3.13), но лучше, когда подобная контекстная семантика инкапсулирована в типе – раз и навсегда.
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
*Таблица 11.1. Для различения файлов с исходным кодом на D используют ся метки порядка байтов. Шаблоны проверяются сверху вниз, первое же совпадение при сопоставлении устанавливает кодировку файла. `xx` – любое ненулевое значение байта*
|
||||
|
||||
| Если первые байты... | ...то кодировка файла – ... | Игнорировать эти байты? |
|
||||
|-|-|:-:|
|
||||
| --- | --- | :-: |
|
||||
| `00 00 FE FF` | UTF-32 с прямым порядком байтов[^1] | ✓ |
|
||||
| `FF FE 00 00` | UTF-32 с обратным порядком байтов[^2] | ✓ |
|
||||
| `FE FF` | UTF-16 с прямым порядком байтов | ✓ |
|
||||
|
@ -642,8 +642,7 @@ module my_widget;
|
|||
|
||||
В этом месте определяются атрибуты `@safe`, `@trusted` и `@system`, которые позволяют модулю объявить о своем уровне безопасности. (Такой подход не нов; в языке Модула-3 применяется тот же подход, чтобы отличить небезопасные и безопасные модули.)
|
||||
|
||||
Код, размещенный после атрибута `@safe`, обязуется использовать ин
|
||||
струкции лишь из безопасного подмножества D, что означает:
|
||||
Код, размещенный после атрибута `@safe`, обязуется использовать инструкции лишь из безопасного подмножества D, что означает:
|
||||
|
||||
- никаких преобразований указателей в неуказатели (например, `int`), и наоборот;
|
||||
- никаких преобразований между указателями, типы которых не имеют отношения друг к другу;
|
||||
|
@ -960,7 +959,7 @@ void fun()
|
|||
*Таблица 11.2. Обзор стандартных модулей*
|
||||
|
||||
| Модуль | Описание |
|
||||
|-|-|
|
||||
| --- | --- |
|
||||
| `std.algorithm` | Этот модуль можно считать основой мощнейшей способности к обобщению, присущей языку. Вдохновлен стандартной библиотекой шаблонов C++ (Standard Template Library, STL). Содержит больше 70 важных алгоритмов, реализованных очень обобщенно. Большинство алгоритмов применяются к структурированным последовательностям идентичных элементов. В STL базовой абстракцией последовательности служит итератор, соответствующий примитив D – *диапазон*, для которого краткого обзора явно недостаточно; полное введение в диапазоны D доступно в Интернете |
|
||||
| `std.array` | Функции для удобства работы с массивами |
|
||||
| `std.bigint` | Целое число переменной длины с сильно оптимизированной реализацией |
|
||||
|
|
Loading…
Reference in New Issue