Compare commits

..

41 Commits

Author SHA1 Message Date
Тарас f0f0c72675 11 - fix table 11.2 2025-01-13 09:40:57 +01:00
Тарас 8f7b9d5087 11 - fix text 11.2.2 2025-01-13 00:46:21 +01:00
Тарас 4299579a93 11 - fix table 11.1 2025-01-12 10:48:03 +02:00
Тарас 45190add84 07 - fix md 2025-01-08 12:17:04 +01:00
Тарас 31a3cdc754 06 - fix notes text 2025-01-02 04:42:33 +01:00
Тарас 22708324a5 06 - fix code formatting 2025-01-02 04:34:15 +01:00
Тарас 09fcedd370 06 - fix 6.3.3 list 2025-01-02 04:13:24 +01:00
Тарас 85400768e2 06 - fix image 6.2 2025-01-02 04:02:42 +01:00
Тарас 723bb7dc56 05 - fix 5.10.1 2024-12-31 07:32:10 +01:00
Тарас 902c92d179 05 - fix 5.9 , 2024-12-31 07:22:21 +01:00
Тарас 55e58d277d 05 - fix 5.2.4 example 2024-12-31 05:05:59 +01:00
Тарас 59446ae9e5 05 - fix 5.2.4 2024-12-31 04:59:56 +01:00
Тарас 3c8803c966 05 - fix 5.2.3 , 2024-12-31 04:53:45 +01:00
Тарас 3d1a3ae0b2 13 - fix footer 2024-12-19 04:59:03 +01:00
Тарас ce34e74f04 12 - fix footer 2024-12-19 04:57:49 +01:00
Тарас ca6cd093fa 11 - fix footer 3 2024-12-19 04:56:54 +01:00
Тарас c6967b5418 11 - fix footer 2 2024-12-19 04:56:24 +01:00
Тарас 6ef8226088 11 - fix footer 2024-12-19 04:55:44 +01:00
Тарас e77d981888 08 - fix footer 2024-12-19 04:53:15 +01:00
Тарас bfa6c8d96c 07 - fix footer 2024-12-19 04:52:35 +01:00
Тарас 0f7274bd91 06 - fix footer 2024-12-19 04:51:44 +01:00
Тарас 13d3d50ecd 05 - fix footer 2024-12-19 04:49:56 +01:00
Тарас 4673aaf466 04 - fix table 4.5 2024-12-19 04:47:58 +01:00
Тарас 460bc91771 04 - fix table 4.4 2024-12-19 04:45:01 +01:00
Тарас db270b5c1b 04 - fix table 4.3 2024-12-19 04:42:09 +01:00
Тарас 9536065c69 04 - fix footer 2024-12-19 04:38:45 +01:00
Тарас 8b2176ad8a 03 - fix table 3.1 2024-12-19 04:37:26 +01:00
Тарас d0026ae566 03 - fix footer 2024-12-19 04:32:37 +01:00
Тарас 7057f2ff5d 02 - fix footer 2024-12-19 04:31:27 +01:00
Тарас 727e56a225 01 - fix footer 2024-12-19 04:29:43 +01:00
Тарас 671abb735c 00 fix footer 2024-12-19 04:28:41 +01:00
Тарас 740eda991e 04 - fix table 4.2 2024-12-19 03:55:05 +01:00
Тарас b7c4623669 04 - fix too long comment 2024-12-19 03:39:02 +01:00
Тарас 7c95077668 04 - fix - desc before img 2024-12-19 03:18:24 +01:00
Тарас b8800a6e4a fix 04 - table 4.1 2024-12-19 03:10:33 +01:00
Тарас b3bc331c14 fix md 02 2024-12-09 02:41:48 +01:00
Тарас 6803291dc3 fix md table 2.4 chapter 02 2024-12-09 02:27:35 +01:00
Тарас 3e3e3455fe fix md 00 2024-12-09 02:09:07 +01:00
Тарас 90edfbb040 fix md table 2.5 chapter 02 2024-12-09 02:06:53 +01:00
Тарас d20a62d467 fix md table 2.3 chapter 02 2024-12-09 01:54:12 +01:00
Тарас 8bd685439d fix md table 2.1 chapter 02 2024-12-09 01:50:45 +01:00
12 changed files with 426 additions and 434 deletions

View File

@ -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 как минимум неплохо смотрится на фоне других языков, разработчикам которых приходилось принимать решения того же плана.

View File

@ -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` | Деление «на месте» |

View File

@ -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)) |

View File

@ -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`.
![image-4-1-4-2](images/image-4-1-4-2.png)
***Рис. 4.2.*** *При выполнении инструкции `auto b = a;` содержимое a не копируется: вместо этого создается объект типа «массив», который ссылается на те же данные*
Более того, получение среза массива `b` сокращает область памяти, «видимую» `b`, также без всякого копирования `b`. При условии что исходное состояние массива задано на рис. 4.2, выполнение инструкции
```d
b = b[1 .. $ - 2];
```
![image-4-1-4-2](images/image-4-1-4-2.png)
***Рис. 4.2.*** *При выполнении инструкции `auto b = a;` содержимое a не копируется: вместо этого создается объект типа «массив», который ссылается на те же данные*
приведет лишь к сокращению диапазона, доступного `b`, без какого-либо копирования данных (рис. 4.3).
![image-4-1-4-3](images/image-4-1-4-3.png)
@ -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. Длина представления определяется по контрольным битам, что позволяет выполнять синхронизацию посреди потока, восстановление после ошибок и просмотр строки в обратном направлении*
| Кодовая точка (в шестнадцатиричном представлении) | Бинарное представление |
|-|-|
| --- | --- |
| `000000000000007F` | `0xxxxxxx` |
| `00000080000007FF` | `110xxxxx 10xxxxxx` |
| `000008000000FFFF` | `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-чтение-и-запись-ячеек)) |

View File

@ -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`.

View File

@ -161,13 +161,11 @@ unittest
Вместо трех последних строк можно было бы использовать универсальную вспомогательную функцию `swap` из модуля `std.algorithm`: `swap(a1, a2)`, но явная запись процесса обмена нагляднее. На рис. 6.2 продемонстрированы привязки до и после обмена.
Сами объекты остаются на том же месте, то есть после создания они никогда не перемещаются в памяти. Просто замечательно, объект никогда не исчезнет: можно рассчитывать, что объект навсегда останется там, куда он был помещен при создании. (Сборщик мусора перерабатывает в фоновом режиме те объекты, которые больше не используются.) Ссылки на объекты (в данном случае `a1` и `a2`) можно заставить «смотреть в другую сторону», переназначив их привязку. Когда библиотека времени исполнения обнаруживает, что для какого-то объекта больше нет привязанных к нему ссылок, она может заново использовать выделенную под него память (этот процесс называется сбором мусора).[^1] Такое поведение
![image-6-2-2](images/image-6-2-2.png)
***Рис. 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 */
...
}
```

View File

@ -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), но лучше, когда подобная контекстная семантика инкапсулирована в типе раз и навсегда.

View File

@ -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` | Целое число переменной длины с сильно оптимизированной реализацией |