2.3.3.-2.3.5.
This commit is contained in:
parent
ed90f97c42
commit
8ed1cd13fa
|
@ -16,12 +16,12 @@
|
|||
- [2.3.1. l-значения и r-значения](#2-3-1-l-значения-и-r-значения)
|
||||
- [2.3.2. Неявные преобразования чисел](#2-3-2-неявные-преобразования-чисел)
|
||||
- [2.3.2.1. Распространение интервала значений](#2-3-2-1-распространение-интервала-значений)
|
||||
- [2.3.3. Типы числовых операций]()
|
||||
- [2.3.4. Первичные выражения]()
|
||||
- [2.3.4.1. Выражение assert]()
|
||||
- [2.3.4.2. Выражение mixin]()
|
||||
- [2.3.4.3. Выражения is]()
|
||||
- [2.3.4.4. Выражения в круглых скобках]()
|
||||
- [2.3.3. Типы числовых операций](#2-3-3-типы-числовых-операций)
|
||||
- [2.3.4. Первичные выражения](#2-3-4-первичные-выражения)
|
||||
- [2.3.4.1. Выражение assert](#2-3-4-1-выражение-assert)
|
||||
- [2.3.4.2. Выражение mixin](#2-3-4-2-выражение-mixin)
|
||||
- [2.3.4.3. Выражения is](#2-3-4-3-выражения-is)
|
||||
- [2.3.4.4. Выражения в круглых скобках](#2-3-4-4-выражения-в-круглых-скобках)
|
||||
- [2.3.5. Постфиксные операции]()
|
||||
- [2.3.5.1. Доступ ко внутренним элементам]()
|
||||
- [2.3.5.2. Увеличение и уменьшение на единицу]()
|
||||
|
@ -580,6 +580,145 @@ void fun(int x)
|
|||
|
||||
[В начало ⮍](#2-3-2-1-распространение-интервала-значений) [Наверх ⮍](#2-основные-типы-данных-выражения)
|
||||
|
||||
### 2.3.3. Типы числовых операций
|
||||
|
||||
В следующих разделах представлены операторы, применимые к числовым типам. Тип значения как результат различных операций с числами определяется с помощью нескольких правил. Это не лучшие правила, которые можно было бы придумать, но они достаточно просты, единообразны и систематичны.
|
||||
|
||||
В результате унарной операции всегда получается тот же тип, что и у операнда, кроме случая с оператором отрицания `!` (см. раздел 2.3.6.6), применение которого всегда дает значения типа `bool`. Тип результата бинарных операций рассчитывается так:
|
||||
- Если хотя бы один из операндов – число с плавающей запятой, то тип результата – наибольший из задействованных типов с плавающей запятой.
|
||||
- Иначе если хотя бы один из операндов имеет тип `ulong`, то другой операнд до выполнения операции неявно преобразуется к типу `ulong` и результат также имеет тип `ulong`.
|
||||
- Иначе если хотя бы один из операндов имеет тип `long`, то другой операнд до выполнения операции неявно преобразуется к типу `long` и результат также имеет тип `long`.
|
||||
- Иначе если хотя бы один из операндов имеет тип `uint`, то другой операнд до выполнения операции неявно преобразуется к типу `uint` и результат также имеет тип `uint`.
|
||||
- Иначе оба операнда до выполнения операции неявно преобразуются к типу `int` и результат имеет тип `int`.
|
||||
|
||||
Для всех неявных преобразований выбирается кратчайший путь (см. рис. 2.3). Это важная деталь. Например:
|
||||
|
||||
```d
|
||||
ushort x = 60_000;
|
||||
assert(x / 10 == 6000);
|
||||
```
|
||||
|
||||
В операции деления 10 имеет тип `int` и в соответствии с указанными правилами `x` неявно преобразуется к типу `int` до выполнения операции. На рис. 2.3 есть несколько возможных путей, в том числе прямое преобразование `ushort` → `int` и более длинное (на один шаг) `ushort` → `short` → `int`. Второе нежелательно, так как преобразование числа 60 000 к типу `short` породит значение –5536, которое затем будет расширено до `int` и приведет инструкцию `assert` к ошибке. Выбор кратчайшего пути в графе преобразований помогает лучше защитить значение от порчи.
|
||||
|
||||
[В начало ⮍](#2-3-3-типы-числовых-операций) [Наверх ⮍](#2-основные-типы-данных-выражения)
|
||||
|
||||
### 2.3.4. Первичные выражения
|
||||
|
||||
Первичные выражения – элементарные частицы вычислений. Нам уже встречались идентификаторы (см. раздел 2.1), логические литералы `true` и `false` (см. раздел 2.2.1), целые литералы (см. раздел 2.2.2), литералы с плавающей запятой (см. раздел 2.2.3), знаковые литералы (см. раздел 2.2.4), строковые литералы (см. раздел 2.2.5), литералы массивов (см. раздел 2.2.6) и функциональные литералы (см. раздел 2.2.7); все это первичные выражения, так же как и литерал `null`. В следующих разделах описаны другие первичные подвыражения: `assert`, `mixin`, `is` и выражения в круглых скобках.
|
||||
|
||||
[В начало ⮍](#2-3-4-первичные-выражения) [Наверх ⮍](#2-основные-типы-данных-выражения)
|
||||
|
||||
#### 2.3.4.1. Выражение assert
|
||||
|
||||
Некоторые выражения и инструкции, включая `assert`, используют нотацию *ненулевых* значений. Эти значения могут: 1) иметь числовой или знаковый тип (в этом случае смысл термина «ненулевое значение» очевиден), 2) иметь логический тип («ненулевое значение» интерпретируется как `true`) или 3) быть массивом, ссылкой или указателем (и тогда «ненулевым значением» считается не-`null`).
|
||||
|
||||
Выражение `assert(выражение)` вычисляет `выражение`. Если результат ненулевой, ничего не происходит. В противном случае выражение `assert` порождает исключение типа `AssertError`. Форма вызова `assert(выражение, сообщение)` делает `сообщение` (которое должно быть приводимо к типу `string`) частью сообщения об ошибке, хранимого внутри объекта типа `AssertError` (`сообщение` не вычисляется, если `выражение` ненулевое). Во всех случаях собственный тип `assert – void`.
|
||||
|
||||
Для сборки наиболее эффективного варианта программы компилятор D предоставляет специальный флаг (`-release` в случае эталонной реализации `dmd`), позволяющий игнорировать все выражения `assert` в компилируемом модуле (то есть вообще не вычислять `выражение`). Учитывая этот факт, к `assert` следует относиться как к инструменту отладки, а не как к средству проверки условий, поскольку оно может дать законный сбой. По той же причине некорректно использовать внутри выражений `assert` выражения с побочными эффектами, если поведение программы зависит от этих побочных эффектов. Более подробную информацию об итоговых сборках вы найдете в главе 11.
|
||||
|
||||
Ситуации, наподобие `assert(false)`, `assert(0)` и других, когда функция `assert` вызывается с заранее известным статическим нулевым значением, обрабатываются особым образом. Такие проверки всегда в силе (независимо от значений флагов компилятора) и порождают машинный код с инструкцией `HLT`, которая аварийно останавливает выполнение процесса. Такое прерывание может дать операционной системе подсказку сгенерировать дамп памяти или запустить отладчик с пометкой на виновной строке.
|
||||
|
||||
Предвосхищая рассказ о логических выражениях с логическим ИЛИ (см. раздел 2.3.15), упомянем простейшую концептуальную идею – всегда вычислять выражение и гарантировать его результат с помощью конструкции `(выражение) || assert(false)`.
|
||||
|
||||
В главе 10 подробно обсуждаются механизмы обеспечения корректности программы, в том числе выражения `assert`.
|
||||
|
||||
[В начало ⮍](#2-3-4-1-выражение-assert) [Наверх ⮍](#2-основные-типы-данных-выражения)
|
||||
|
||||
#### 2.3.4.2. Выражение mixin
|
||||
|
||||
Если бы выражения были отвертками разных видов, выражение `mixin` было бы электрической отверткой со сменными насадками, регулятором скоростей, адаптером для операций на мозге, встроенной беспроводной камерой и функцией распознавания речи. Оно на самом деле *такое* мощное.
|
||||
|
||||
Короче говоря, выражение `mixin` позволяет вам превратить строку в исполняемый код. Синтаксис выражения выглядит как `mixin(выражение)`, где `выражение` должно быть строкой, известной во время компиляции. Это ограничение исключает возможность динамически создавать программный код, например читать строку с терминала и интерпретировать ее. Нет, D – не интерпретируемый язык, и его компилятор не является частью средств стандартной библиотеки времени исполнения. Хорошие новости заключаются в том, что D на самом деле запускает полноценный интерпретатор *во время компиляции*, а значит, вы можете собирать строки настолько изощренными способами, насколько этого требуют условия вашей задачи.
|
||||
|
||||
Возможность манипулировать строками и преобразовывать их в код во время компиляции позволяет создавать так называемые предметно-ориентированные встроенные языки программирования, которые их фанаты любовно обозначают аббревиатурой DSEL[^18]. Типичный DSEL, реализованный на D, принимал бы инструкции в качестве строковых литералов, обрабатывал их в процессе компиляции, создавал соответствующий код на D в виде строки и с помощью `mixin` преобразовывал ее в готовый к исполнению код на D. Хорошим примером полезных DSEL могут служить SQL-команды, регулярные выражения и спецификации грамматик (а-ля `yacc`). На самом деле, даже вызывая `printf`, вы каждый раз используете DSEL. Спецификатор формата, применяемый функцией `printf`, – это настоящий маленький язык, ориентированный на описание шаблонов для текстовых данных.
|
||||
|
||||
D позволяет вам создать какой угодно DSEL без дополнительных инструментов (таких как синтаксические анализаторы, сборщики, генераторы кода и т. д.); например, функция `bitfields` из стандартной библиотеки (модуль `std.bitmanip`) принимает определения битовых полей и генерирует оптимальный код на D для их чтения и записи, хотя сам язык не поддерживает битовые поля.
|
||||
|
||||
[В начало ⮍](#2-3-4-2-выражение-mixin) [Наверх ⮍](#2-основные-типы-данных-выражения)
|
||||
|
||||
#### 2.3.4.3. Выражения is
|
||||
|
||||
Выражения `is` отвечают на вопросы о типах («Существует ли тип `Widget`?» или «Наследует ли `Widget` от `Gadget`?») и являются важной частью мощного механизма интроспекции во время компиляции, реализованного в D. Все выражения `is` вычисляются во время компиляции и возвращают логическое значение. Как показано ниже, есть несколько видов выражения `is`.
|
||||
|
||||
**1.**
|
||||
|
||||
Выражения `is(Тип)` и `is(Тип Идентификатор)` проверяют, существует ли указанный `Тип`. Тип может быть недопустим или, гораздо чаще, просто не существует. Примеры:
|
||||
|
||||
```d
|
||||
bool
|
||||
a = is(int[]), // True, int[] – допустимый тип
|
||||
b = is(int[5]), // True, int[5] – также допустимый тип
|
||||
c = is(int[-3]), // False, размер массива задан неверно
|
||||
d = is(Blah); // False (если тип с именем Blah не был определен)
|
||||
```
|
||||
|
||||
Во всех случаях `Тип` должен быть записан корректно с точки зрения синтаксиса, даже если запись в целом лишена смысла; например, выражение `is([[]x[]])` породит ошибку во время компиляции, а не вернет значение `false`. Другими словами, вы можете наводить справки только о том, что синтаксически выглядит как тип.
|
||||
|
||||
Если присутствует `Идентификатор`, он становится псевдонимом типа `Тип` в случае истинности выражения `is`. Пока что неизвестная команда `static if` позволяет различать случаи истинности и ложности этого выражения. Подробное описание `static if` вы найдете в главе 3, но на самом деле все просто: `static if` вычисляет свое условие во время компиляции и позволяет компилировать вложенные в него инструкции, только если тестируемое выражение истинно.
|
||||
|
||||
```d
|
||||
static if (is(Widget[100][100] ManyWidgets))
|
||||
{
|
||||
ManyWidgets lotsOfWidgets;
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
**2.**
|
||||
|
||||
Выражения `is(Тип1 == Тип2)` и `is(Тип1 Идентификатор == Тип2)` возвращают `True`, если `Тип1` и `Тип2` идентичны. (Они могут иметь различные имена в результате применения `alias`.)
|
||||
|
||||
```d
|
||||
alias uint UInt;
|
||||
assert(is(uint == UInt));
|
||||
```
|
||||
|
||||
Если присутствует `Идентификатор`, он становится псевдонимом типа `Тип1` в случае истинности выражения `is`.
|
||||
|
||||
**3.**
|
||||
|
||||
Выражения `is(Тип1 : Тип2)` и `is(Тип1 Идентификатор : Тип2)` возвращают `True`, если `Тип1` идентичен или может быть неявно преобразован к типу `Тип2`. Например:
|
||||
|
||||
```d
|
||||
bool
|
||||
a = is(int[5] : int[]), // true, int[5] может быть преобразован к int[]
|
||||
b = is(int[5] == int[]), // false; это разные типы
|
||||
c = is(uint : long), // true
|
||||
d = is(ulong : long); // true
|
||||
```
|
||||
|
||||
Аналогично, если присутствует `Идентификатор`, он становится псевдонимом типа `Тип1` в случае истинности выражения `is`.
|
||||
|
||||
**4.**
|
||||
|
||||
Выражения `is(Тип == Вид)` и `is(Тип Идентификатор == Вид)` проверяют, принадлежит ли `Тип` к категории `Вид`. `Вид` – это одно из следующих ключевых слов: `struct`, `union`, `class`, `interface`, `enum`, `function`, `delegate`, `super`, `const`, `immutable`, `inout`, `shared` и `return`. Выражение `is` истинно, если `Тип` соответствует указанному `Виду`. Если присутствует `Идентификатор`, он должен быть задан в зависимости от значения `Вид` (табл. 2.4).
|
||||
|
||||
*Таблица 2.4. Зависимости для значения `Идентификатор` в выражении `is(Тип Идентификатор == Вид)`*
|
||||
|
||||
|Вид|Идентификатор – псевдоним для...|
|
||||
|-|-|
|
||||
|`struct`|`Тип`|
|
||||
|`union`|`Тип`|
|
||||
|`class`|`Тип`|
|
||||
|`interface`|`Тип`|
|
||||
|`enum`|Базовый тип перечисления|
|
||||
|`function`|Кортеж типов аргументов функции|
|
||||
|`delegate`|Функциональный тип `delegate`|
|
||||
|`super`|Родительский класс|
|
||||
|`const`|`Тип`|
|
||||
|`immutable`|`Тип`|
|
||||
|`inout`|`Тип`|
|
||||
|`shared`|`Тип`|
|
||||
|`return`|Тип, возвращаемый функцией, оператором `delegate` или указателем на функцию|
|
||||
|
||||
[В начало ⮍](#2-3-4-3-выражения-is) [Наверх ⮍](#2-основные-типы-данных-выражения)
|
||||
|
||||
#### 2.3.4.4. Выражения в круглых скобках
|
||||
|
||||
Круглые скобки переопределяют обычный порядок выполнения операций: для любых выражений, `(<выражение>)` обладает более высоким приоритетом, чем `<выражение>`.
|
||||
|
||||
[В начало ⮍](#2-3-4-4-выражения-в-круглых-скобках) [Наверх ⮍](#2-основные-типы-данных-выражения)
|
||||
|
||||
[^1]: Впрочем, использование нелатинских букв является дурным тоном. – *Прим. науч. ред.*
|
||||
[^2]: С99 – обновленная спецификация C, в том числе добавляющая поддержку знаков Юникода. – *Прим. пер.*
|
||||
[^3]: Сам язык не поддерживает восьмеричные литералы, но поскольку они присутствуют в некоторых C-подобных языках, в стандартную библиотеку был добавлен соответствующий шаблон. Теперь запись `std.conv.octal!777` аналогична записи `0777` в C. – *Прим. науч. ред.*
|
||||
|
@ -597,3 +736,4 @@ void fun(int x)
|
|||
[^15]: Заключенное в 1989 году соглашение между коммунистами и демократами, ознаменовавшее собой достижение компромисса между двумя партиями. В данном случае также ищется «компромиссный» тип. – *Прим. пер.*
|
||||
[^16]: In situ (лат.) – на месте. – *Прим. пер.*
|
||||
[^17]: От англ. left-value и right-value. – *Прим. науч. ред.*
|
||||
[^18]: Domain-specific embedded language (DSEL) – предметно-ориентированный встроенный язык. – *Прим. пер.*
|
||||
|
|
Loading…
Reference in New Issue