Compare commits

..

No commits in common. "f0f0c726757ca4e9b5e21999490c82f0478a8dfe" and "b90bbc94c2c9a5670eae2ea7f50d2996ec0a810d" have entirely different histories.

12 changed files with 434 additions and 426 deletions

View file

@ -9,14 +9,22 @@
Обретая силу в простоте, язык программирования порождает красоту. Обретая силу в простоте, язык программирования порождает красоту.
Поиск компромисса при противоречивых требованиях сложная задача, для решения которой создателю языка требуется не только знание теоретических принципов и практической стороны дела, но и хороший вкус. Проектирование языка программирования это последняя ступень мастерства в разработке программ. Поиск компромисса при противоречивых требованиях сложная зада
ча, для решения которой создателю языка требуется не только знание теоретических принципов и практической стороны дела, но и хороший вкус. Проектирование языка программирования это последняя ступень мастерства в разработке программ.
D это язык, который последовательно старается правильно действовать в пределах выбранных им ограничений, таких как доступ системного уровня к вычислительным ресурсам, высокая производительность и синтаксическая простота, к которой стремятся все произошедшие от C языки. Стараясь правильно действовать, D порой поступает традиционно как другие языки, а порой ломает традиции с помощью свежего, инновационного решения. Иногда это приводило к пересмотру принципов, которым, казалось, D никогда не изменит. Например, большие фрагменты программного кода, а то и целые программы, могут быть написаны с помощью хорошо определенного, не допускающего ошибок памяти «безопасного подмножества» D. Ценой небольшого ограничения доступа на системном уровне приобретается огромное преимущество при отладке программ. D заинтересует вас, если для вас важны следующие аспекты: D это язык, который последовательно старается правильно действовать в пределах выбранных им ограничений, таких как доступ систем
- *Производительность*. D это язык для системного программирования. Его модель памяти, несмотря на сильную типизацию, совместима с моделью памяти C. Функции на D могут вызывать функции на C, а функции на C могут использовать функции D без каких-либо промежуточных преобразований. ного уровня к вычислительным ресурсам, высокая производительность и синтаксическая простота, к которой стремятся все произошедшие от C языки. Стараясь правильно действовать, D порой поступает традиционно как другие языки, а порой ломает традиции с помощью свежего, инновационного решения. Иногда это приводило к пересмотру принципов, которым, казалось, D никогда не изменит. Например, большие фрагменты программного кода, а то и целые программы, могут быть написаны с помощью хорошо определенного, не допускающего ошибок памяти «безопасного подмножества» D. Ценой небольшого ограничения доступа на системном уровне приобретается огромное преимущество при отладке программ. D заинтересует вас, если для вас важны следующие аспекты:
- *Выразительность*. D нельзя назвать небольшим, минималистичным языком, но его удельная мощность достаточно велика. Он позволяет определять наглядные, не требующие объяснений инструкции, точно моделирующие сложные реалии. - *Производительность*. D это язык для системного программирования. Его модель памяти, несмотря на сильную типизацию, совмес
- *«Крутящий момент»*. Любой лихач-«самоделкин» скажет вам, что мощность еще не все было бы где ее применить. На одних языках лучше всего пишутся маленькие программы. Синтаксические излишества других оправдываются только начиная с определенного объема программ. D одинаково эффективно помогает справляться и с короткими сценариями, и с большими программами, и для него отнюдь не редкость целый проект, органично вырастающий из простенького скрипта в единственном файле. тима с моделью памяти C. Функции на D могут вызывать функции на C, а функции на C могут использовать функции D без каких-либо
- *Параллельные вычисления*. Подход к параллельным вычислениям несомненное отличие D от похожих языков, отражающее разрыв между современными аппаратными решениями и архитектурой компьютеров прошлого. D покончил с проклятьем неявного разделения памяти (хотя и допускает статически проверенное, явно заданное разделение) и поощряет независимые потоки, которые «общаются» друг с другом посредством сообщений. промежуточных преобразований.
- *Обобщенное программирование*. Идея обобщенного кода, манипули рующего другим кодом, была впервые реализована в мощных макросах Лиспа, затем в шаблонах C++, обобщенных классах Java и схожих конструкциях других языков. D также предлагает невероятно мощные механизмы обобщенного и порождающего программирования. - *Выразительность*. D нельзя назвать небольшим, минималистичным языком, но его удельная мощность достаточно велика. Он по
зволяет определять наглядные, не требующие объяснений инструкции, точно моделирующие сложные реалии.
- *«Крутящий момент»*. Любой лихач-«самоделкин» скажет вам, что мощность еще не все было бы где ее применить. На одних языках
лучше всего пишутся маленькие программы. Синтаксические излишества других оправдываются только начиная с определенного объема программ. D одинаково эффективно помогает справляться и с короткими сценариями, и с большими программами, и для него отнюдь не редкость целый проект, органично вырастающий из простенького скрипта в единственном файле.
- *Параллельные вычисления*. Подход к параллельным вычислениям несомненное отличие D от похожих языков, отражающее разрыв ме
жду современными аппаратными решениями и архитектурой компьютеров прошлого. D покончил с проклятьем неявного разделения памяти (хотя и допускает статически проверенное, явно заданное разделение) и поощряет независимые потоки, которые «общаются» друг с другом посредством сообщений.
- *Обобщенное программирование*. Идея обобщенного кода, манипули рующего другим кодом, была впервые реализована в мощных макро
сах Лиспа, затем в шаблонах C++, обобщенных классах Java и схожих конструкциях других языков. D также предлагает невероятно мощные механизмы обобщенного и порождающего программирования.
- *Эклектизм*. D подразумевает, что каждая парадигма программирования ориентирована на свою задачу разработки. Поэтому он предполагает высокоинтегрированный объединенный стиль программирования, а не Единственно Верный Подход. - *Эклектизм*. D подразумевает, что каждая парадигма программирования ориентирована на свою задачу разработки. Поэтому он предполагает высокоинтегрированный объединенный стиль программирования, а не Единственно Верный Подход.
- *«Это мои принципы. А если они вам не нравятся, то у меня есть и другие»*[^1]. D старается всегда следовать своим принципам устройства языка. Иногда они идут вразрез с соображениями сложности реализации и трудностей использования и, главное, с человеческой природой, которая не всегда находит скрытую логику здравой и интуитивно понятной. В таких случаях все языки полагаются на собственное бесконечно субъективное понимание баланса, гибкости и особенно хорошего вкуса. На мой взгляд, D как минимум неплохо смотрится на фоне других языков, разработчикам которых приходилось принимать решения того же плана. - *«Это мои принципы. А если они вам не нравятся, то у меня есть и другие»*[^1]. D старается всегда следовать своим принципам устройства языка. Иногда они идут вразрез с соображениями сложности реализации и трудностей использования и, главное, с человеческой природой, которая не всегда находит скрытую логику здравой и интуитивно понятной. В таких случаях все языки полагаются на собственное бесконечно субъективное понимание баланса, гибкости и особенно хорошего вкуса. На мой взгляд, D как минимум неплохо смотрится на фоне других языков, разработчикам которых приходилось принимать решения того же плана.

View file

@ -72,8 +72,8 @@
*Таблица 2.1. Основные типы данных D* *Таблица 2.1. Основные типы данных D*
| Тип данных | Описание | Начальное<br>значение<br>по умолчанию | |Тип данных|Описание|Начальное значение по умолчанию|
| --- | --- | --- | |-|-|-|
|`void`|Без значения|`n/a`| |`void`|Без значения|`n/a`|
|`typeof(null)`|Тип константы `null`|`n/a`| |`typeof(null)`|Тип константы `null`|`n/a`|
|`bool`|Логическое (булево) значение|`false`| |`bool`|Логическое (булево) значение|`false`|
@ -224,8 +224,8 @@ auto
*Таблица 2.3. Экранирующие последовательности в D* *Таблица 2.3. Экранирующие последовательности в D*
| Escape<br>последовательность | Тип | Описание | |Escape-последовательность|Тип|Описание|
| --- | --- | --- | |-|-|-|
|`\"`|`char`|Двойная кавычка (если двусмысленно)| |`\"`|`char`|Двойная кавычка (если двусмысленно)|
|`\\`|`char`|Обратная косая черта| |`\\`|`char`|Обратная косая черта|
|`\a`|`char`|Звуковой сигнал (Bell, ASCII 7)| |`\a`|`char`|Звуковой сигнал (Bell, ASCII 7)|
@ -708,7 +708,7 @@ bool
*Таблица 2.4. Зависимости для значения `Идентификатор` в выражении `is(Тип Идентификатор == Вид)`* *Таблица 2.4. Зависимости для значения `Идентификатор` в выражении `is(Тип Идентификатор == Вид)`*
|Вид|Идентификатор псевдоним для...| |Вид|Идентификатор псевдоним для...|
| --- | --- | |-|-|
|`struct`|`Тип`| |`struct`|`Тип`|
|`union`|`Тип`| |`union`|`Тип`|
|`class`|`Тип`| |`class`|`Тип`|
@ -934,11 +934,19 @@ foreach (ref row; matrix)
### 2.3.10. Сдвиг ### 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`) на `b` бит, а `a >> b` сдвигает `a` вправо на `b` бит.
Если `a` отрицательное число, знак после сдвига сохраняется. Если `a` отрицательное число, знак после сдвига сохраняется.
`a >>> b` это беззнаковый сдвиг независимо от знаковости `a`. Это означает, что ноль гарантированно займет самый старший разряд `a`. Проиллюстрируем сюрпризы, которые готовит применение операции сдвига к числам со знаком: `a >>> b` это беззнаковый сдвиг независимо от знаковости `a`. Это означа
ет, что ноль гарантированно займет самый старший разряд `a`. Проил
люстрируем сюрпризы, которые готовит применение операции сдвига
к числам со знаком:
```d ```d
int a = -1; // То есть 0xFFFF_FFFF int a = -1; // То есть 0xFFFF_FFFF
@ -1175,7 +1183,7 @@ int c = (a = b, b = 7, 8);
*Таблица 2.5. Выражения D порядке убывания приоритета* *Таблица 2.5. Выражения D порядке убывания приоритета*
|Выражение|Описание| |Выражение|Описание|
| --- | --- | |-|-|
|`<идентификатор>`|Идентификатор (см. раздел [2.1](#2-1-идентификаторы))| |`<идентификатор>`|Идентификатор (см. раздел [2.1](#2-1-идентификаторы))|
|`.<идентификатор>`|Идентификатор, доступный в пространстве имен модуля (в обход всех друг пространств имен) (см. раздел [2.1](#2-1-идентификаторы))| |`.<идентификатор>`|Идентификатор, доступный в пространстве имен модуля (в обход всех друг пространств имен) (см. раздел [2.1](#2-1-идентификаторы))|
|`this`|Текущий объект внутри метода (см. раздел [2.1.1](#2-1-1-ключевые-слова))| |`this`|Текущий объект внутри метода (см. раздел [2.1.1](#2-1-1-ключевые-слова))|
@ -1190,7 +1198,7 @@ int c = (a = b, b = 7, 8);
|`<строка>`|Строковый литерал (см. раздел [2.2.5](#2-2-5-строковые-литералы))| |`<строка>`|Строковый литерал (см. раздел [2.2.5](#2-2-5-строковые-литералы))|
|`<массив>`|Литерал массива (см. раздел [2.2.6](#2-2-6-литералы-массивов-и-ассоциативных-массивов))| |`<массив>`|Литерал массива (см. раздел [2.2.6](#2-2-6-литералы-массивов-и-ассоциативных-массивов))|
|`<функция>`|Функциональный литерал (см. раздел [2.2.7](#2-2-7-функциональные-литералы))| |`<функция>`|Функциональный литерал (см. раздел [2.2.7](#2-2-7-функциональные-литералы))|
| `assert(a)` | В режиме отладки, если a не является ненулевым значением, выполнение программы прерывается;<br>в режиме итоговой сборки (release) ничего не происходит (см. раздел [2.3.4.1](#2-3-4-1-выражение-assert)) | |`assert(a)`|В режиме отладки, если a не является ненулевым значением, выполнение программы прерывается; в режиме итоговой сборки (release) ничего не происходит (см. раздел [2.3.4.1](#2-3-4-1-выражение-assert))|
|`assert(a, b)`|То же, но к сообщению об ошибке добавляется `b` (см. раздел [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))| |`mixin(a)`|Выражение `mixin` (см. раздел [2.3.4.2](#2-3-4-2-выражение-mixin))|
|`<IsExpr>`|Выражение `is` (см. раздел [2.3.4.3](#2-3-4-3-выражения-is))| |`<IsExpr>`|Выражение `is` (см. раздел [2.3.4.3](#2-3-4-3-выражения-is))|
@ -1225,7 +1233,7 @@ int c = (a = b, b = 7, 8);
|`a >> b`|Сдвиг вправо (см. раздел [2.3.10](#2-3-10-сдвиг))| |`a >> b`|Сдвиг вправо (см. раздел [2.3.10](#2-3-10-сдвиг))|
|`a >>> b`|Беззнаковый сдвиг вправо (старший разряд сбрасывается независимо от типа и значения `a`) (см. раздел [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 in b`|Проверка на принадлежность для ассоциативных массивов (см. раздел [2.3.11](#2-3-11-выражения-in))|
| `a == b` | Проверка на равенство;<br>все операторы этой группы неассоциативны;<br>например, выражение `a == b == c` некорректно (см. раздел [2.3.12.1](#2-3-12-1-проверка-на-равенство)) | |`a == b`|Проверка на равенство; все операторы этой группы неассоциативны; например, выражение `a == b == c` некорректно (см. раздел [2.3.12.1](#2-3-12-1-проверка-на-равенство))|
|`a != b`|Проверка на неравенство (см. раздел [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`|Проверка на идентичность (`true`, если и только если `a` и `b` ссылаются на один и тот же объект) (см. раздел [2.3.12.1](#2-3-12-1-проверка-на-равенство))|
|`a !is b`|То же, что `!(a is b)`| |`a !is b`|То же, что `!(a is b)`|
@ -1239,8 +1247,8 @@ int c = (a = b, b = 7, 8);
|`a && b`|Логическое **И** (`b` может иметь тип `void`) (см. раздел [2.3.14](#2-3-14-логическое-и))| |`a && b`|Логическое **И** (`b` может иметь тип `void`) (см. раздел [2.3.14](#2-3-14-логическое-и))|
|`a \|\| b`|Логическое **ИЛИ** (`b` может иметь тип `void`) (см. раздел [2.3.15](#2-3-15-логическое-или))| |`a \|\| b`|Логическое **ИЛИ** (`b` может иметь тип `void`) (см. раздел [2.3.15](#2-3-15-логическое-или))|
|`a ? b : c`|Тернарная условная операция; если операнд `a` имеет ненулевое значение, то `b`, иначе `с` (см. раздел [2.3.16](#2-3-16-тернарная-условная-операция))| |`a ? b : c`|Тернарная условная операция; если операнд `a` имеет ненулевое значение, то `b`, иначе `с` (см. раздел [2.3.16](#2-3-16-тернарная-условная-операция))|
| `a = b` | Присваивание;<br>все операторы присваивания этой группы ассоциативны справа;<br>например `a *= b += c` то же, что и `a *= (b += c)` (см. раздел [2.3.17](#2-3-17-присваивание)) | |`a = b`|Присваивание; все операторы присваивания этой группы ассоциативны справа; например `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`, работающими по принципу «вычислить и присвоить», вычисляются в следующем порядке: **1)** `a` (должно быть l-значением), **2)** `b` и **3)** `al = al ω b`, где `al` l-значение, получившееся в результате вычисления `a`|
|`a -= b`|Вычитание «на месте»| |`a -= b`|Вычитание «на месте»|
|`a *= b`|Умножение «на месте»| |`a *= b`|Умножение «на месте»|
|`a /= b`|Деление «на месте»| |`a /= b`|Деление «на месте»|

View file

@ -1098,7 +1098,7 @@ D предоставляет все ожидаемые обычные инстр
*Таблица 3.1. Справочник по инструкциям (`‹и›` инструкция, `‹в›` выражение, `o` объявление, `х` идентификатор)* *Таблица 3.1. Справочник по инструкциям (`‹и›` инструкция, `‹в›` выражение, `o` объявление, `х` идентификатор)*
|Инструкция|Описание| |Инструкция|Описание|
| --- | --- | |-|-|
|`‹в›;`|Вычисляет `‹в›`. Ничего не изменяющие выражения, включающие лишь встроенные типы и операторы, запрещены (см. раздел [3.1](#3-1-инструкция-выражение))| |`‹в›;`|Вычисляет `‹в›`. Ничего не изменяющие выражения, включающие лишь встроенные типы и операторы, запрещены (см. раздел [3.1](#3-1-инструкция-выражение))|
|`{и1 ... и2}`|Выполняет инструкции от `и1` до `и2` по порядку, пока управление не будет явно передано в другую область видимости (например, инструкцией `return`) (см. раздел [3.2](#3-2-составная-инструкция))| |`{и1 ... и2}`|Выполняет инструкции от `и1` до `и2` по порядку, пока управление не будет явно передано в другую область видимости (например, инструкцией `return`) (см. раздел [3.2](#3-2-составная-инструкция))|
|`asm ‹и›`|Машиннозависимый ассемблерный код (здесь `‹и›` обозначает ассемблерный код, а не инструкцию на языке D). В настоящее время поддерживается ассемблер x86 с единым синтаксисом для всех поддерживаемых операционных систем (см. раздел [3.15](#3-15-конструкция-asm))| |`asm ‹и›`|Машиннозависимый ассемблерный код (здесь `‹и›` обозначает ассемблерный код, а не инструкцию на языке D). В настоящее время поддерживается ассемблер x86 с единым синтаксисом для всех поддерживаемых операционных систем (см. раздел [3.15](#3-15-конструкция-asm))|

View file

@ -202,7 +202,7 @@ D различает «безопасные» (safe) и «системные» (
*Таблица 4.1. Проверка границ в зависимости от вида модуля и режима сборки* *Таблица 4.1. Проверка границ в зависимости от вида модуля и режима сборки*
||Безопасный модуль|Системный модуль| ||Безопасный модуль|Системный модуль|
| --- | :-: | :-: | |-|:-:|:-:|
|Промежуточная сборка|✓|✓| |Промежуточная сборка|✓|✓|
|Итоговая сборка (флаг `-release` для компилятора `dmd`)|✓|☠| |Итоговая сборка (флаг `-release` для компилятора `dmd`)|✓|☠|
@ -263,16 +263,16 @@ auto a = [1, 5, 2, 3, 6];
Инициализация массива другим массивом (`auto b = a`), равно как и присваивание одного массива другому (`int[] b; … b = a;`) не влечет скрытого автоматического копирования данных. Как показано на рис. 4.2, эти действия просто заставляют `b` ссылаться на ту же область памяти, что и `a`. Инициализация массива другим массивом (`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, выполнение инструкции Более того, получение среза массива `b` сокращает область памяти, «видимую» `b`, также без всякого копирования `b`. При условии что исходное состояние массива задано на рис. 4.2, выполнение инструкции
```d ```d
b = b[1 .. $ - 2]; 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). приведет лишь к сокращению диапазона, доступного `b`, без какого-либо копирования данных (рис. 4.3).
![image-4-1-4-3](images/image-4-1-4-3.png) ![image-4-1-4-3](images/image-4-1-4-3.png)
@ -769,9 +769,7 @@ double[5] e = a ~ d; // Все в порядке, явный запрос ма
Поскольку запись `T[]` означает динамический массив элементов типа `T`, а `T[]`, в свою очередь, тоже тип, легко сделать вывод, что `T[][]` это массив элементов типа `T[]`, то есть массив массивов элементов типа `T`. Каждый элемент «внешнего» массива это, в свою очередь, тоже массив, предоставляющий обычную функциональность, присущую массивам. Рассмотрим `T[][]` на практике: Поскольку запись `T[]` означает динамический массив элементов типа `T`, а `T[]`, в свою очередь, тоже тип, легко сделать вывод, что `T[][]` это массив элементов типа `T[]`, то есть массив массивов элементов типа `T`. Каждый элемент «внешнего» массива это, в свою очередь, тоже массив, предоставляющий обычную функциональность, присущую массивам. Рассмотрим `T[][]` на практике:
```d ```d
auto array = new double[][5]; // Массив из пяти массивов, содержащих элементы типа double, auto array = new double[][5]; // Массив из пяти массивов, содержащих элементы типа double, первоначально каждый из них null
// первоначально каждый из них null
// Сделать треугольную матрицу // Сделать треугольную матрицу
foreach (i, ref e; array) foreach (i, ref e; array)
{ {
@ -1052,7 +1050,7 @@ foreach (k; gammaFunc.byKey())
*Таблица 4.2. Битовые представления UTF-8. Длина представления определяется по контрольным битам, что позволяет выполнять синхронизацию посреди потока, восстановление после ошибок и просмотр строки в обратном направлении* *Таблица 4.2. Битовые представления UTF-8. Длина представления определяется по контрольным битам, что позволяет выполнять синхронизацию посреди потока, восстановление после ошибок и просмотр строки в обратном направлении*
|Кодовая точка (в шестнадцатиричном представлении)|Бинарное представление| |Кодовая точка (в шестнадцатиричном представлении)|Бинарное представление|
| --- | --- | |-|-|
|`000000000000007F`|`0xxxxxxx`| |`000000000000007F`|`0xxxxxxx`|
|`00000080000007FF`|`110xxxxx 10xxxxxx`| |`00000080000007FF`|`110xxxxx 10xxxxxx`|
|`000008000000FFFF`|`1110xxxx 10xxxxxx 10xxxxxx`| |`000008000000FFFF`|`1110xxxx 10xxxxxx 10xxxxxx`|
@ -1289,7 +1287,7 @@ y += 100; // Хм...
*Таблица 4.3. Операции над динамическими массивами (`a` и `b` два значения типа `T[]`; `t`, `t1`, ..., `tk` значения типа `T`; `n` значение, приводимое к типу `размер_t`)* *Таблица 4.3. Операции над динамическими массивами (`a` и `b` два значения типа `T[]`; `t`, `t1`, ..., `tk` значения типа `T`; `n` значение, приводимое к типу `размер_t`)*
|Выражение|Тип|Описание| |Выражение|Тип|Описание|
| --- | --- | --- | |-|-|-|
|`new T[n]`|`T[]`|Создает массив (см. раздел [4.1](#4-1-динамические-массивы))| |`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-динамические-массивы))| |`[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-копирование))| |`a = b`|`T[]`|Присваивает один массив другому (см. раздел [4.1.4](#4-1-4-копирование))|
@ -1313,7 +1311,7 @@ y += 100; // Хм...
*Таблица 4.4. Операции над массивами фиксированной длины (`a` и `b` два значения типа `T[]`; `t`, `t1`, ..., `tk` значения типа `T`; `n` значение, приводимое к типу `размер_t`)* *Таблица 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-динамические-массивы))| |`[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 = b`|`ref T[n]`|Копирует содержимое одного массива в другой (см. раздел [4.2.4](#4-2-4-копирование-и-неявные-преобразования))|
|`a[‹в›]`|`ref T`|Предоставляет доступ к элементу по индексу (символ `$` в `‹в›` заменяется на `a.length`, `‹в›` должно быть приводимым к типу `размер_t`; кроме того, должно соблюдаться условие `‹в› < a.length`) (см. раздел [4.1](#4-1-динамические-массивы))| |`a[‹в›]`|`ref T`|Предоставляет доступ к элементу по индексу (символ `$` в `‹в›` заменяется на `a.length`, `‹в›` должно быть приводимым к типу `размер_t`; кроме того, должно соблюдаться условие `‹в› < a.length`) (см. раздел [4.1](#4-1-динамические-массивы))|
@ -1333,7 +1331,7 @@ y += 100; // Хм...
*Таблица 4.5. Операции над ассоциативными массивами (`a` и `b` два значения типа `V[K]`; `k`, `k1`, ..., `ki` значения типа `K`; `v`, `v1`, ..., `vk` значения типа `V`)* *Таблица 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-ассоциативные-массивы))| |`[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 = b`|`V[K]`|Присваивает ассоциативный массив `b` переменной a типа «ассоциативный массив» (см. раздел [4.4.3](#4-4-3-копирование))|
|`a[k]`|`V`|Предоставляет доступ к элементу по индексу (если ключ `k` не найден, возникает исключение) (см. раздел [4.4.2](#4-4-2-чтение-и-запись-ячеек))| |`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) ### 5.2.3. Выходные параметры (с ключевым словом out)
Иногда параметры передаются по ссылке только для того, чтобы функция с их помощью что-то вернула. В таких случаях можно воспользоваться классом памяти `out`, напоминающим `ref` разница лишь в том, что перед входом в функцию `out` инициализирует свой аргумент значением по умолчанию (соответствующим типу аргумента): Иногда параметры передаются по ссылке только для того, чтобы функция с их помощью что-то вернула. В таких случаях можно воспользоваться классом памяти `out`, напоминающим `ref`, разница лишь в том, что перед входом в функцию `out` инициализирует свой аргумент значением по умолчанию (соответствующим типу аргумента):
```d ```d
// Вычисляет частное и остаток от деления для аргументов a и b. // Вычисляет частное и остаток от деления для аргументов a и b.
@ -258,8 +258,7 @@ void log(string message)
writeln(message); writeln(message);
} }
... ...
int result = foo(); int result = foo(); log("foo() returned " ~ to!string(result));
log("foo() returned " ~ to!string(result));
``` ```
Как видим, вычислять выражение `"foo() returned " ~ to!string(result)` нужно, только если переменная `verbose` имеет значение `true`. При этом выражение, передаваемое этой функции в качестве аргумента, будет вычислено в любом случае. В данном примере это конкатенация двух строк, которая потребует выделения памяти и копирования в нее содержимого каждой из них. И все это для того, чтобы узнать, что переменная `verbose` имеет значение `false` и значение аргумента никому не нужно! Можно было бы передавать вместо строки делегат, возвращающий строку (делегаты описаны в разделе 5.6.1): Как видим, вычислять выражение `"foo() returned " ~ to!string(result)` нужно, только если переменная `verbose` имеет значение `true`. При этом выражение, передаваемое этой функции в качестве аргумента, будет вычислено в любом случае. В данном примере это конкатенация двух строк, которая потребует выделения памяти и копирования в нее содержимого каждой из них. И все это для того, чтобы узнать, что переменная `verbose` имеет значение `false` и значение аргумента никому не нужно! Можно было бы передавать вместо строки делегат, возвращающий строку (делегаты описаны в разделе 5.6.1):
@ -270,8 +269,7 @@ void log(string delegate() message)
if (verbose) if (verbose)
writeln(message()); writeln(message());
} }
... ...log({return "foo() returned " ~ to!string(result);});
log({return "foo() returned " ~ to!string(result);});
``` ```
В этом случае аргумент будет вычислен, только если он действительно нужен, но такая форма слишком громоздка. Поэтому D вводит такое понятие, как «ленивые» аргументы. Такие аргументы объявляются с атрибутом `lazy`, выглядят как обычные аргументы, но вычисляются только тогда, когда требуется их значение. В этом случае аргумент будет вычислен, только если он действительно нужен, но такая форма слишком громоздка. Поэтому D вводит такое понятие, как «ленивые» аргументы. Такие аргументы объявляются с атрибутом `lazy`, выглядят как обычные аргументы, но вычисляются только тогда, когда требуется их значение.
@ -871,7 +869,7 @@ T[] find(T)(T[] haystack, T needle)
2. `haystack[0]` осуществляет доступ к первому элементу `haystack`. 2. `haystack[0]` осуществляет доступ к первому элементу `haystack`.
3. `haystack = haystack[1 .. $]` исключает из рассмотрения первый элемент `haystack`. 3. `haystack = haystack[1 .. $]` исключает из рассмотрения первый элемент `haystack`.
Конкретный способ, каким массивы реализуют эти операции, непросто распространить на другие контейнеры. Например, проверять с помощью выражения `haystack.length > 0`, есть ли в односвязном списке элементы подход, достойный премии Дарвина[^9]. Если не обеспечено постоянное кэширование длины списка (что по многим причинам весьма проблематично), то для вычисления длины списка таким способом потребуется время, пропорциональное самой длине списка, а быстрое обращение к началу списка занимает всего лишь несколько машинных инструкций. Применить к спискам индексацию столь же проигрышная идея. Так что выделим сущность рассмотренных операций, представим полученный результат в виде трех именованных функций и оставим их реализацию типу `haystack`. Примерный синтаксис базовых операций, необходимых для реализации алгоритма линейного поиска: Конкретный способ, каким массивы реализуют эти операции, непросто распространить на другие контейнеры. Например, проверять с помощью выражения `haystack.length > 0`, есть ли в односвязном списке элементы, подход, достойный премии Дарвина[^9]. Если не обеспечено постоянное кэширование длины списка (что по многим причинам весьма проблематично), то для вычисления длины списка таким способом потребуется время, пропорциональное самой длине списка, а быстрое обращение к началу списка занимает всего лишь несколько машинных инструкций. Применить к спискам индексацию столь же проигрышная идея. Так что выделим сущность рассмотренных операций, представим полученный результат в виде трех именованных функций и оставим их реализацию типу `haystack`. Примерный синтаксис базовых операций, необходимых для реализации алгоритма линейного поиска:
1. `haystack.empty` для проверки `haystack` на пустоту. 1. `haystack.empty` для проверки `haystack` на пустоту.
2. `haystack.front` для получения первого элемента `haystack`. 2. `haystack.front` для получения первого элемента `haystack`.
@ -1010,7 +1008,7 @@ unittest
} }
``` ```
(Обратите внимание на очередное удачное использование `reduce`.) Интересная деталь функции `average`: многоточие `...` после параметра `values`, который является срезом. (Если бы это было не так или если бы параметр `values` не был последним в списке аргументов функции `average`, компилятор диагностировал бы это многоточие как ошибку.) (Обратите внимание на очередное удачное использование `reduce`.) Интересная деталь функции `average`: многоточие ... после параметра `values`, который является срезом. (Если бы это было не так или если бы параметр `values` не был последним в списке аргументов функции `average`, компилятор диагностировал бы это многоточие как ошибку.)
Вызов функции `average` со срезом массива элементов типа `double` (как показано в последней строке теста модуля) ничем не примечателен. Однако благодаря многоточию эту функцию можно вызывать с любым числом аргументов, при условии что каждый из них можно привести к типу `double`. Компилятор автоматически сформирует из этих аргументов срез и передаст его в `average`. Вызов функции `average` со срезом массива элементов типа `double` (как показано в последней строке теста модуля) ничем не примечателен. Однако благодаря многоточию эту функцию можно вызывать с любым числом аргументов, при условии что каждый из них можно привести к типу `double`. Компилятор автоматически сформирует из этих аргументов срез и передаст его в `average`.

View file

@ -161,11 +161,13 @@ unittest
Вместо трех последних строк можно было бы использовать универсальную вспомогательную функцию `swap` из модуля `std.algorithm`: `swap(a1, a2)`, но явная запись процесса обмена нагляднее. На рис. 6.2 продемонстрированы привязки до и после обмена. Вместо трех последних строк можно было бы использовать универсальную вспомогательную функцию `swap` из модуля `std.algorithm`: `swap(a1, a2)`, но явная запись процесса обмена нагляднее. На рис. 6.2 продемонстрированы привязки до и после обмена.
Сами объекты остаются на том же месте, то есть после создания они никогда не перемещаются в памяти. Просто замечательно, объект никогда не исчезнет: можно рассчитывать, что объект навсегда останется там, куда он был помещен при создании. (Сборщик мусора перерабатывает в фоновом режиме те объекты, которые больше не используются.) Ссылки на объекты (в данном случае `a1` и `a2`) можно заставить «смотреть в другую сторону», переназначив их привязку. Когда библиотека времени исполнения обнаруживает, что для какого-то объекта больше нет привязанных к нему ссылок, она может заново использовать выделенную под него память (этот процесс называется сбором мусора).[^1] Такое поведение
![image-6-2-2](images/image-6-2-2.png) ![image-6-2-2](images/image-6-2-2.png)
***Рис. 6.2.*** *Привязки до и после обмена. В процессе обмена меняются привязки к ссылкам; сами объекты остаются на том же месте* ***Рис. 6.2.*** *Привязки до и после обмена. В процессе обмена меняются привязки к ссылкам; сами объекты остаются на том же месте*
Сами объекты остаются на том же месте, то есть после создания они никогда не перемещаются в памяти. Просто замечательно, объект никогда не исчезнет: можно рассчитывать, что объект навсегда останется там, куда он был помещен при создании. (Сборщик мусора перерабатывает в фоновом режиме те объекты, которые больше не используются.) Ссылки на объекты (в данном случае `a1` и `a2`) можно заставить «смотреть в другую сторону», переназначив их привязку. Когда библиотека времени исполнения обнаруживает, что для какого-то объекта больше нет привязанных к нему ссылок, она может заново использовать выделенную под него память (этот процесс называется сбором мусора).[^1] Такое поведение в корне отличается от семантики *значения* (например, `int`), в случае которого нет никаких косвенных изменений или привязок: каждое имя прочно закреплено за значением, которым манипулируют с помощью этого идентификатора. в корне отличается от семантики *значения* (например, `int`), в случае которого нет никаких косвенных изменений или привязок: каждое имя прочно закреплено за значением, которым манипулируют с помощью этого идентификатора.
Ссылка, не привязанная к какому-либо объекту, это «пустая» ссылка (`null`). При инициализации по умолчанию с помощью свойства `.init` ссылки на классы получают значение `null`. Можно сравнивать ссылку с константой `null` и присваивать ссылке значение `null`. Следующие проверки пройдут успешно: Ссылка, не привязанная к какому-либо объекту, это «пустая» ссылка (`null`). При инициализации по умолчанию с помощью свойства `.init` ссылки на классы получают значение `null`. Можно сравнивать ссылку с константой `null` и присваивать ссылке значение `null`. Следующие проверки пройдут успешно:
@ -399,11 +401,10 @@ this(uint h)
1. *Выделение памяти*. Библиотека времени исполнения выделяет участок «сырой» памяти в куче, достаточный для размещения нестатических полей объекта. Память подо все объекты, основанные на классах, выделяется динамически в отличие от C++, в D нет способа выделить для объекта память в стеке[^2]. Если выделить память не удалось, построение объекта прерывается: порождается исключительная ситуация. 1. *Выделение памяти*. Библиотека времени исполнения выделяет участок «сырой» памяти в куче, достаточный для размещения нестатических полей объекта. Память подо все объекты, основанные на классах, выделяется динамически в отличие от C++, в D нет способа выделить для объекта память в стеке[^2]. Если выделить память не удалось, построение объекта прерывается: порождается исключительная ситуация.
2. *Инициализация полей*. Каждое поле инициализируется своим значением по умолчанию. Как уже говорилось, в качестве значения поля по умолчанию выступает значение, указанное при объявлении поля в виде `= значение`, или при отсутствии такой записи значение свойства `.init` типа поля. *Инициализация полей*. Каждое поле инициализируется своим значением по умолчанию. Как уже говорилось, в качестве значения поля по умолчанию выступает значение, указанное при объявлении поля в виде `= значение`, или при отсутствии такой записи значение свойства `.init` типа поля.
3. *Брендирование*. После завершения инициализации полей значениями по умолчанию объекту присваивается статус полноправного экземпляра класса `T` (объект брендируется) еще *до* того, как будет вызван настоящий конструктор. 2. *Брендирование*. После завершения инициализации полей значениями по умолчанию объекту присваивается статус полноправного экземпляра класса `T` (объект брендируется) еще *до* того, как будет вызван настоящий конструктор.
3. *Вызов конструктора*. Наконец, компилятор инициирует вызов подходящего конструктора. Если класс не определяет собственный конструктор, этот шаг пропускается.
4. *Вызов конструктора*. Наконец, компилятор инициирует вызов подходящего конструктора. Если класс не определяет собственный конструктор, этот шаг пропускается.
Поскольку объект считается «живым» и правильно построенным сразу после инициализации по умолчанию, настоятельно рекомендуется использовать инициализирующие значения, которые всегда приводят объект в осмысленное состояние. Настоящий конструктор внесет затем свои поправки, приведя объект в другое интересное состояние (разумеется, также осмысленное). Поскольку объект считается «живым» и правильно построенным сразу после инициализации по умолчанию, настоятельно рекомендуется использовать инициализирующие значения, которые всегда приводят объект в осмысленное состояние. Настоящий конструктор внесет затем свои поправки, приведя объект в другое интересное состояние (разумеется, также осмысленное).
@ -1374,9 +1375,7 @@ void main()
void widgetize() void widgetize()
{ {
Widget w = new Widget; Widget w = new Widget;
... .../* Использование w */...
/* Использование w */
...
} }
``` ```
@ -1386,9 +1385,7 @@ void widgetize()
void widgetize() void widgetize()
{ {
Widget w = new TextWidget; Widget w = new TextWidget;
... .../* Использование w */...
/* Использование w */
...
} }
``` ```
@ -1398,9 +1395,7 @@ void widgetize()
void widgetize(string widgetClass) void widgetize(string widgetClass)
{ {
Widget w = cast(Widget) Object.factory(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`. чем соответствующая операция присваивания для типа `int`.
Во-вторых, классы живут вечно, а значит, с их помощью трудно моделировать ресурсы с выраженным *конечным* временем жизни (такие как дескрипторы файлов, дескрипторы графического контекста, мьютексы, сокеты и т. д.). Работая с такими ресурсами как с классами, нужно постоянно быть начеку, чтобы не забыть своевременно освободить инкапсулированные ресурсы с помощью метода, вроде `close` или `dispose`. В таких случаях обычно помогает инструкция `scope` (см. раздел 3.13), но лучше, когда подобная контекстная семантика инкапсулирована в типе раз и навсегда. Во-вторых, классы живут вечно, а значит, с их помощью трудно моделировать ресурсы с выраженным *конечным* временем жизни (такие как дескрипторы файлов, дескрипторы графического контекста, мьютексы, сокеты и т. д.). Работая с такими ресурсами как с классами, нужно постоянно быть начеку, чтобы не забыть своевременно освободить инкапсулированные ресурсы с помощью метода, вроде `close` или `dispose`. В таких случаях обычно помогает инструкция `scope` (см. раздел 3.13), но лучше, когда подобная контекстная семантика инкапсулирована в типе раз и навсегда.

View file

@ -54,7 +54,7 @@
*Таблица 11.1. Для различения файлов с исходным кодом на D используют ся метки порядка байтов. Шаблоны проверяются сверху вниз, первое же совпадение при сопоставлении устанавливает кодировку файла. `xx` любое ненулевое значение байта* *Таблица 11.1. Для различения файлов с исходным кодом на D используют ся метки порядка байтов. Шаблоны проверяются сверху вниз, первое же совпадение при сопоставлении устанавливает кодировку файла. `xx` любое ненулевое значение байта*
|Если первые байты...|...то кодировка файла ...|Игнорировать эти байты?| |Если первые байты...|...то кодировка файла ...|Игнорировать эти байты?|
| --- | --- | :-: | |-|-|:-:|
|`00 00 FE FF`|UTF-32 с прямым порядком байтов[^1]|✓| |`00 00 FE FF`|UTF-32 с прямым порядком байтов[^1]|✓|
|`FF FE 00 00`|UTF-32 с обратным порядком байтов[^2]|✓| |`FF FE 00 00`|UTF-32 с обратным порядком байтов[^2]|✓|
|`FE FF`|UTF-16 с прямым порядком байтов|✓| |`FE FF`|UTF-16 с прямым порядком байтов|✓|
@ -642,7 +642,8 @@ module my_widget;
В этом месте определяются атрибуты `@safe`, `@trusted` и `@system`, которые позволяют модулю объявить о своем уровне безопасности. (Такой подход не нов; в языке Модула-3 применяется тот же подход, чтобы отличить небезопасные и безопасные модули.) В этом месте определяются атрибуты `@safe`, `@trusted` и `@system`, которые позволяют модулю объявить о своем уровне безопасности. (Такой подход не нов; в языке Модула-3 применяется тот же подход, чтобы отличить небезопасные и безопасные модули.)
Код, размещенный после атрибута `@safe`, обязуется использовать инструкции лишь из безопасного подмножества D, что означает: Код, размещенный после атрибута `@safe`, обязуется использовать ин
струкции лишь из безопасного подмножества D, что означает:
- никаких преобразований указателей в неуказатели (например, `int`), и наоборот; - никаких преобразований указателей в неуказатели (например, `int`), и наоборот;
- никаких преобразований между указателями, типы которых не имеют отношения друг к другу; - никаких преобразований между указателями, типы которых не имеют отношения друг к другу;
@ -959,7 +960,7 @@ void fun()
*Таблица 11.2. Обзор стандартных модулей* *Таблица 11.2. Обзор стандартных модулей*
|Модуль|Описание| |Модуль|Описание|
| --- | --- | |-|-|
|`std.algorithm`|Этот модуль можно считать основой мощнейшей способности к обобщению, присущей языку. Вдохновлен стандартной библиотекой шаблонов C++ (Standard Template Library, STL). Содержит больше 70 важных алгоритмов, реализованных очень обобщенно. Большинство алгоритмов применяются к структурированным последовательностям идентичных элементов. В STL базовой абстракцией последовательности служит итератор, соответствующий примитив D *диапазон*, для которого краткого обзора явно недостаточно; полное введение в диапазоны D доступно в Интернете| |`std.algorithm`|Этот модуль можно считать основой мощнейшей способности к обобщению, присущей языку. Вдохновлен стандартной библиотекой шаблонов C++ (Standard Template Library, STL). Содержит больше 70 важных алгоритмов, реализованных очень обобщенно. Большинство алгоритмов применяются к структурированным последовательностям идентичных элементов. В STL базовой абстракцией последовательности служит итератор, соответствующий примитив D *диапазон*, для которого краткого обзора явно недостаточно; полное введение в диапазоны D доступно в Интернете|
|`std.array`|Функции для удобства работы с массивами| |`std.array`|Функции для удобства работы с массивами|
|`std.bigint`|Целое число переменной длины с сильно оптимизированной реализацией| |`std.bigint`|Целое число переменной длины с сильно оптимизированной реализацией|