add sources

This commit is contained in:
Alexander Zhirov 2023-02-26 01:19:12 +03:00
parent fcd25eea52
commit 7ce631a648
21 changed files with 548 additions and 2 deletions

View File

@ -25,7 +25,7 @@
- [5.10.2. Гетерогенные функции с переменным числом аргументов](#5-10-2-гетерогенные-функции-с-переменным-числом-аргументов) - [5.10.2. Гетерогенные функции с переменным числом аргументов](#5-10-2-гетерогенные-функции-с-переменным-числом-аргументов)
- [5.10.2.1. Тип без имени](#5-10-2-1-тип-без-имени) - [5.10.2.1. Тип без имени](#5-10-2-1-тип-без-имени)
- [5.10.2.2. Тип данных Tuple и функция tuple](#5-10-2-2-тип-данных-tuple-и-функция-tuple) - [5.10.2.2. Тип данных Tuple и функция tuple](#5-10-2-2-тип-данных-tuple-и-функция-tuple)
- [5.10.3. Гетерогенные функции с переменным числом аргументов. Альтернативный подход](#5-10-3-гетерогенные-функции-с-переменным-числом-аргументов-альтернативный-подход16) - [5.10.3. Гетерогенные функции с переменным числом аргументов. Альтернативный подход](#5-10-3-гетерогенные-функции-с-переменным-числом-аргументов-альтернативный-подход-16)
- [5.10.3.1. Функции с переменным числом аргументов в стиле C](#5-10-3-1-функции-с-переменным-числом-аргументов-в-стиле-c) - [5.10.3.1. Функции с переменным числом аргументов в стиле C](#5-10-3-1-функции-с-переменным-числом-аргументов-в-стиле-c)
- [5.10.3.2. Функции с переменным числом аргументов в стиле D](#5-10-3-2-функции-с-переменным-числом-аргументов-в-стиле-d) - [5.10.3.2. Функции с переменным числом аргументов в стиле D](#5-10-3-2-функции-с-переменным-числом-аргументов-в-стиле-d)
- [5.11. Атрибуты функций](#5-11-атрибуты-функций) - [5.11. Атрибуты функций](#5-11-атрибуты-функций)
@ -97,6 +97,8 @@ $ rdmd --main -unittest searching.d
Если вы запустите компилятор с флагом `-unittest`, тесты модулей будут скомпилированы и подготовлены к запуску перед исполнением основной программы. Иначе компилятор проигнорирует все блоки `unittest`, что может быть полезно, если требуется запустить уже оттестированный код без задержек на начальном этапе. Флаг `--main` предписывает `rdmd` добавить ничего не делающую функцию `main`. (Если вы забыли написать `--main`, не волнуйтесь; компоновщик тут же витиевато напомнит вам об этом на своем родном языке зашифрованном клингонском.) Заменитель функции `main` нужен нам, так как мы хотим запустить только тест модуля, а не саму программу. Ведь наш маленький файл может заинтересовать массу программистов, и они станут использовать его в своих проектах, в каждом из которых определена своя функция `main`. Если вы запустите компилятор с флагом `-unittest`, тесты модулей будут скомпилированы и подготовлены к запуску перед исполнением основной программы. Иначе компилятор проигнорирует все блоки `unittest`, что может быть полезно, если требуется запустить уже оттестированный код без задержек на начальном этапе. Флаг `--main` предписывает `rdmd` добавить ничего не делающую функцию `main`. (Если вы забыли написать `--main`, не волнуйтесь; компоновщик тут же витиевато напомнит вам об этом на своем родном языке зашифрованном клингонском.) Заменитель функции `main` нужен нам, так как мы хотим запустить только тест модуля, а не саму программу. Ведь наш маленький файл может заинтересовать массу программистов, и они станут использовать его в своих проектах, в каждом из которых определена своя функция `main`.
[Исходный код](src/chapter-5-1/)
[В начало ⮍](#5-1-написание-и-модульное-тестирование-простой-функции) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль) [В начало ⮍](#5-1-написание-и-модульное-тестирование-простой-функции) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль)
## 5.2. Соглашения о передаче аргументов и классы памяти ## 5.2. Соглашения о передаче аргументов и классы памяти
@ -123,6 +125,8 @@ unittest
Что же произошло? В первых двух случаях функции `fun` и `gun` изменили только собственные копии параметров. В частности, во втором случае толстый указатель был перенаправлен на другую область памяти, но исходный массив не был затронут. Однако в третьем случае функция `hun` решила изменить один элемент массива, и это изменение отразилось на исходном массиве. Это легко понять, представив, что срез y находится совсем не в том же месте, что и три целых числа, которыми y управляет. Так что если вы присвоите срез целиком, а-ля `x = [1, 2, 3]`, то срез, который раньше содержала переменная `x`, будет предоставлен самому себе, а `x` начнет новую жизнь; но если вы измените какой-то элемент `x[i]` среза `x`, то другие срезы, которым виден этот элемент (в нашем случае в коде, вызвавшем `fun`), будут видеть и это изменение. Что же произошло? В первых двух случаях функции `fun` и `gun` изменили только собственные копии параметров. В частности, во втором случае толстый указатель был перенаправлен на другую область памяти, но исходный массив не был затронут. Однако в третьем случае функция `hun` решила изменить один элемент массива, и это изменение отразилось на исходном массиве. Это легко понять, представив, что срез y находится совсем не в том же месте, что и три целых числа, которыми y управляет. Так что если вы присвоите срез целиком, а-ля `x = [1, 2, 3]`, то срез, который раньше содержала переменная `x`, будет предоставлен самому себе, а `x` начнет новую жизнь; но если вы измените какой-то элемент `x[i]` среза `x`, то другие срезы, которым виден этот элемент (в нашем случае в коде, вызвавшем `fun`), будут видеть и это изменение.
[Исходный код](src/chapter-5-2/)
[В начало ⮍](#5-2-соглашения-о-передаче-аргументов-и-классы-памяти) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль) [В начало ⮍](#5-2-соглашения-о-передаче-аргументов-и-классы-памяти) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль)
### 5.2.1. Параметры и возвращаемые значения, переданные по ссылке (с ключевым словом ref) ### 5.2.1. Параметры и возвращаемые значения, переданные по ссылке (с ключевым словом ref)
@ -167,6 +171,8 @@ int bump(ref int x) { return ++x; }
то компилятор отверг бы вызов `bump(bump(x))` как незаконную попытку привязать r-значение, возвращенное при вызове `bump(x)`, параметру, передаваемому по ссылке при внешнем вызове `bump`. то компилятор отверг бы вызов `bump(bump(x))` как незаконную попытку привязать r-значение, возвращенное при вызове `bump(x)`, параметру, передаваемому по ссылке при внешнем вызове `bump`.
[Исходный код](src/chapter-5-2-1/)
[В начало ⮍](#5-2-1-параметры-и-возвращаемые-значения-переданные-по-ссылке-с-ключевым-словом-ref) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль) [В начало ⮍](#5-2-1-параметры-и-возвращаемые-значения-переданные-по-ссылке-с-ключевым-словом-ref) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль)
### 5.2.2. Входные параметры (с ключевым словом in) ### 5.2.2. Входные параметры (с ключевым словом in)
@ -233,6 +239,8 @@ unittest
В этом коде можно было бы с тем же успехом вместо ключевого слова `out` использовать `ref`, поскольку выбор `out` всего лишь извещает инициатора вызова, что функция `divrem` не ожидает от параметра `rem` осмысленного значения. В этом коде можно было бы с тем же успехом вместо ключевого слова `out` использовать `ref`, поскольку выбор `out` всего лишь извещает инициатора вызова, что функция `divrem` не ожидает от параметра `rem` осмысленного значения.
[Исходный код](src/chapter-5-2-3/)
[В начало ⮍](#5-2-3-выходные-параметры-с-ключевым-словом-out) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль) [В начало ⮍](#5-2-3-выходные-параметры-с-ключевым-словом-out) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль)
### 5.2.4. Ленивые аргументы (с ключевым словом lazy)[^4] ### 5.2.4. Ленивые аргументы (с ключевым словом lazy)[^4]
@ -358,6 +366,8 @@ D использует гетерогенную трансляцию (внима
Если компилятор не смог сгенерировать функцию `find` для этого конкретного типа, генерируется сообщение об ошибке. Что на самом деле довольно неприятно, поскольку исключение может возникнуть из-за незамеченной ошибки в `find`. Зато теперь у нас есть веский повод прочесть следующий раздел, потому что `find` содержит две ошибки не функциональные, а связанные с обобщенностью: теперь понятно, что функция `find` одновременно и излишне, и недостаточно обобщенна. Посмотрим, как работает этот дзэнский тезис. Если компилятор не смог сгенерировать функцию `find` для этого конкретного типа, генерируется сообщение об ошибке. Что на самом деле довольно неприятно, поскольку исключение может возникнуть из-за незамеченной ошибки в `find`. Зато теперь у нас есть веский повод прочесть следующий раздел, потому что `find` содержит две ошибки не функциональные, а связанные с обобщенностью: теперь понятно, что функция `find` одновременно и излишне, и недостаточно обобщенна. Посмотрим, как работает этот дзэнский тезис.
[Исходный код](src/chapter-5-3/)
[В начало ⮍](#5-3-параметры-типов) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль) [В начало ⮍](#5-3-параметры-типов) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль)
## 5.4. Ограничения сигнатуры ## 5.4. Ограничения сигнатуры
@ -410,6 +420,8 @@ T[] find(T, E)(T[] haystack, E needle)
Заметим, что выражение, к которому применяется оператор `typeof`, никогда не вычисляется во время исполнения программы; оператор лишь определяет тип выражения, если оно скомпилируется. (Если выражение с оператором `typeof` не компилируется, то это не ошибка компиляции, а просто сигнал, что рассматриваемое выражение не имеет никакого типа, а «никакого типа» это не `bool`.) В частности, не стоит беспокоиться о том, что в проверку вовлечено значение `haystack[0]`, даже если длина `haystack` равна нулю. И обратно: в ограничении сигнатуры запрещается использовать условия, не вычислимые во время компиляции программы; например, нельзя ограничить функцию `find` условием `needle > 0`. Заметим, что выражение, к которому применяется оператор `typeof`, никогда не вычисляется во время исполнения программы; оператор лишь определяет тип выражения, если оно скомпилируется. (Если выражение с оператором `typeof` не компилируется, то это не ошибка компиляции, а просто сигнал, что рассматриваемое выражение не имеет никакого типа, а «никакого типа» это не `bool`.) В частности, не стоит беспокоиться о том, что в проверку вовлечено значение `haystack[0]`, даже если длина `haystack` равна нулю. И обратно: в ограничении сигнатуры запрещается использовать условия, не вычислимые во время компиляции программы; например, нельзя ограничить функцию `find` условием `needle > 0`.
[Исходный код](src/chapter-5-4/)
[В начало ⮍](#5-4-ограничения-сигнатуры) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль) [В начало ⮍](#5-4-ограничения-сигнатуры) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль)
## 5.5. Перегрузка ## 5.5. Перегрузка
@ -474,6 +486,8 @@ auto test = find(ints1, ints2); // Корректно или ошибка? Об
Подход D к решению этого вопроса очень прост: выбор всегда падает на более специализированную функцию. Однако в более общем случае понятие «более специализированная» требует некоторого объяснения; оно подразумевает, что существует некоторое отношение порядка специализированности, «меньше или равно» для функций. И оно существует на самом деле; это отношение называется *отношением частичного порядка на множестве функций* (*partial ordering of functions*). Подход D к решению этого вопроса очень прост: выбор всегда падает на более специализированную функцию. Однако в более общем случае понятие «более специализированная» требует некоторого объяснения; оно подразумевает, что существует некоторое отношение порядка специализированности, «меньше или равно» для функций. И оно существует на самом деле; это отношение называется *отношением частичного порядка на множестве функций* (*partial ordering of functions*).
[Исходный код](src/chapter-5-5/)
[В начало ⮍](#5-5-перегрузка) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль) [В начало ⮍](#5-5-перегрузка) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль)
### 5.5.1. Отношение частичного порядка на множестве функций ### 5.5.1. Отношение частичного порядка на множестве функций
@ -549,6 +563,8 @@ unittest
Что же происходит, когда функция `transmogrify(uint)` сравнивается с функцией `transmogrify(T)(T)` на предмет специализированности? Хотя было решено, что `T = int`, во время сравнения `T` не заменяется на `int`, обобщенность сохраняется. Может ли функция `transmogrify(uint)` принять некоторый произвольный тип `T`? Нет, не может. Поэтому можно сделать вывод, что версия `transmogrify(T)(T)` менее специализированна, чем `transmogrify(uint)`, так что обобщенная функция исключается из множества претендентов на вызов. Итак, в общем случае предпочтение отдается необобщенным функциям, даже когда для их применения требуется неявное приведение типов. Что же происходит, когда функция `transmogrify(uint)` сравнивается с функцией `transmogrify(T)(T)` на предмет специализированности? Хотя было решено, что `T = int`, во время сравнения `T` не заменяется на `int`, обобщенность сохраняется. Может ли функция `transmogrify(uint)` принять некоторый произвольный тип `T`? Нет, не может. Поэтому можно сделать вывод, что версия `transmogrify(T)(T)` менее специализированна, чем `transmogrify(uint)`, так что обобщенная функция исключается из множества претендентов на вызов. Итак, в общем случае предпочтение отдается необобщенным функциям, даже когда для их применения требуется неявное приведение типов.
[Исходный код](src/chapter-5-5-1/)
[В начало ⮍](#5-5-1-отношение-частичного-порядка-на-множестве-функций) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль) [В начало ⮍](#5-5-1-отношение-частичного-порядка-на-множестве-функций) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль)
### 5.5.2. Кроссмодульная перегрузка ### 5.5.2. Кроссмодульная перегрузка
@ -671,6 +687,8 @@ auto b = find!((x) { return x < 0; })(a);
Эта запись абсолютно понятна для посвященных, в круг которых вы вошли пару секунд назад. Эта запись абсолютно понятна для посвященных, в круг которых вы вошли пару секунд назад.
[Исходный код](src/chapter-5-6/)
[В начало ⮍](#5-6-функции-высокого-порядка-функциональные-литералы) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль) [В начало ⮍](#5-6-функции-высокого-порядка-функциональные-литералы) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль)
### 5.6.1. Функциональные литералы против литералов делегатов ### 5.6.1. Функциональные литералы против литералов делегатов
@ -763,6 +781,8 @@ void transmogrify(int[] input, int z)
Теперь, с ключевым словом `static` в качестве буксира, функции `isTransmogrifiable` доступны лишь данные, определенные на уровне модуля, и данные внутри `transmogrify`, также помеченные ключевым словом `static` (как показано на примере переменной `w`). Любые данные, которые могут изменяться от вызова к вызову, такие как параметры функций или нестатические переменные, недоступны (но, разумеется, могут быть переданы явно). Теперь, с ключевым словом `static` в качестве буксира, функции `isTransmogrifiable` доступны лишь данные, определенные на уровне модуля, и данные внутри `transmogrify`, также помеченные ключевым словом `static` (как показано на примере переменной `w`). Любые данные, которые могут изменяться от вызова к вызову, такие как параметры функций или нестатические переменные, недоступны (но, разумеется, могут быть переданы явно).
[Исходный код](src/chapter-5-7/)
[В начало ⮍](#5-7-вложенные-функции) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль) [В начало ⮍](#5-7-вложенные-функции) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль)
## 5.8. Замыкания ## 5.8. Замыкания
@ -808,6 +828,8 @@ auto finder(T)(T x) if (is(typeof(x == x) == bool))
Обратите внимание на использование ключевого слова `auto` вместо возвращаемого типа функции, а также на то, что ключевое слово `delegate` опущено; компилятор с радостью позаботится обо всем этом за нас. Тем не менее в литерале делегата запись `T[]` указать необходимо. Ведь компилятор должен за что-то зацепиться, чтобы сотворить волшебство, обещанное ключевым словом `auto`: возвращаемый тип делегата определяется по типу функции `find(a, x)`, который, в свою очередь, определяется по типам `a` и `x`; в результате такой цепочки выводов делегат приобретает тип `T[] delegate(T[])`, этот же тип возвращает функция `finder`. Без знания типа `a` вся эта цепочка рассуждений не может быть осуществима. Обратите внимание на использование ключевого слова `auto` вместо возвращаемого типа функции, а также на то, что ключевое слово `delegate` опущено; компилятор с радостью позаботится обо всем этом за нас. Тем не менее в литерале делегата запись `T[]` указать необходимо. Ведь компилятор должен за что-то зацепиться, чтобы сотворить волшебство, обещанное ключевым словом `auto`: возвращаемый тип делегата определяется по типу функции `find(a, x)`, который, в свою очередь, определяется по типам `a` и `x`; в результате такой цепочки выводов делегат приобретает тип `T[] delegate(T[])`, этот же тип возвращает функция `finder`. Без знания типа `a` вся эта цепочка рассуждений не может быть осуществима.
[Исходный код](src/chapter-5-8/)
[В начало ⮍](#5-8-замыкания) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль) [В начало ⮍](#5-8-замыкания) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль)
### 5.8.1. Так, это работает... Стоп, не должно... Нет, все же работает! ### 5.8.1. Так, это работает... Стоп, не должно... Нет, все же работает!
@ -891,6 +913,8 @@ void popFront(T)(ref T[] a) { a = a[1 .. $]; }
В терминах языка D абстрактный тип данных, позволяющий перемещаться по коллекции элементов, это *диапазон* (*range*). (Название «итератор» тоже подошло бы, но этот термин уже приобрел определенное значение в контексте ранее созданных библиотек, поэтому его использование могло бы вызвать путаницу.) У диапазонов D больше сходства с шаблоном «Итератор», чем с итераторами библиотеки STL (диапазон D можно грубо смоделировать с помощью пары итераторов из STL); тем не менее диапазоны D наследуют разбивку по категориям, определенную для итераторов STL. В частности, тройка `empty-front-popFront` определяет *диапазон ввода* (*input range*); в результате поиск хорошей реализации функции `find` привел нас к открытию сложного отношения между линейным поиском и диапазонами ввода: нельзя реализовать линейный поиск в структуре данных с меньшей функциональностью, чем у диапазона ввода, но было бы ошибкой вдруг потребовать от вашей коллекции большей функциональности, чем у диапазона ввода (например, не стоит требовать массивов с индексированным доступом к элементам). Практически идентичную реализацию функции `find` можно найти в модуле `std.algorithm` стандартной библиотеки. В терминах языка D абстрактный тип данных, позволяющий перемещаться по коллекции элементов, это *диапазон* (*range*). (Название «итератор» тоже подошло бы, но этот термин уже приобрел определенное значение в контексте ранее созданных библиотек, поэтому его использование могло бы вызвать путаницу.) У диапазонов D больше сходства с шаблоном «Итератор», чем с итераторами библиотеки STL (диапазон D можно грубо смоделировать с помощью пары итераторов из STL); тем не менее диапазоны D наследуют разбивку по категориям, определенную для итераторов STL. В частности, тройка `empty-front-popFront` определяет *диапазон ввода* (*input range*); в результате поиск хорошей реализации функции `find` привел нас к открытию сложного отношения между линейным поиском и диапазонами ввода: нельзя реализовать линейный поиск в структуре данных с меньшей функциональностью, чем у диапазона ввода, но было бы ошибкой вдруг потребовать от вашей коллекции большей функциональности, чем у диапазона ввода (например, не стоит требовать массивов с индексированным доступом к элементам). Практически идентичную реализацию функции `find` можно найти в модуле `std.algorithm` стандартной библиотеки.
[Исходный код](src/chapter-5-9-1/)
[В начало ⮍](#5-9-1-псевдочлены-и-атрибут-property) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль) [В начало ⮍](#5-9-1-псевдочлены-и-атрибут-property) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль)
### 5.9.2. Свести но не к абсурду ### 5.9.2. Свести но не к абсурду
@ -944,6 +968,8 @@ V reduce(alias fun, V, R)(V x, R range)
Такой вариант уже гораздо лучше смотрится. Имея в распоряжении функцию `reduce`, можно вычислить не только сумму и минимум, но и множество других агрегирующих функций, таких как число, ближайшее к заданному, наибольшее число по модулю и стандартное отклонение. Функция `reduce` из модуля `std.algorithm` стандартной библиотеки выглядит практически так же, как и наша версия выше, за исключением того, что она принимает в качестве аргументов несколько функций для вычисления; это позволяет очень быстро вычислять значения множества агрегирующих функций, поскольку выполняется всего один проход по входным данным. Такой вариант уже гораздо лучше смотрится. Имея в распоряжении функцию `reduce`, можно вычислить не только сумму и минимум, но и множество других агрегирующих функций, таких как число, ближайшее к заданному, наибольшее число по модулю и стандартное отклонение. Функция `reduce` из модуля `std.algorithm` стандартной библиотеки выглядит практически так же, как и наша версия выше, за исключением того, что она принимает в качестве аргументов несколько функций для вычисления; это позволяет очень быстро вычислять значения множества агрегирующих функций, поскольку выполняется всего один проход по входным данным.
[Исходный код](src/chapter-5-9-2/)
[В начало ⮍](#5-9-2-свести-но-не-к-абсурду) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль) [В начало ⮍](#5-9-2-свести-но-не-к-абсурду) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль)
## 5.10. Функции с переменным числом аргументов ## 5.10. Функции с переменным числом аргументов
@ -1093,6 +1119,8 @@ void main()
2: double 4.2 2: double 4.2
``` ```
[Исходный код](src/chapter-5-10-2/)
[В начало ⮍](#5-10-2-гетерогенные-функции-с-переменным-числом-аргументов) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль) [В начало ⮍](#5-10-2-гетерогенные-функции-с-переменным-числом-аргументов) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль)
#### 5.10.2.1. Тип без имени #### 5.10.2.1. Тип без имени
@ -1166,6 +1194,8 @@ void fun(T...)(T args)
Все объясняется тем, что кортежи в своем роде уникальны: это типы, которые внутренне используются компилятором, но не могут быть выражены в тексте программы. Никаким образом невозможно взять и написать тип кортежа параметров. Потому нет и литерала, порождающего вывод кортежа параметров (если бы был, то необходимость в указании имени типа отпала бы: ведь есть ключевое слово `auto`). Все объясняется тем, что кортежи в своем роде уникальны: это типы, которые внутренне используются компилятором, но не могут быть выражены в тексте программы. Никаким образом невозможно взять и написать тип кортежа параметров. Потому нет и литерала, порождающего вывод кортежа параметров (если бы был, то необходимость в указании имени типа отпала бы: ведь есть ключевое слово `auto`).
[Исходный код](src/chapter-5-10-2-1/)
[В начало ⮍](#5-10-2-1-тип-без-имени) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль) [В начало ⮍](#5-10-2-1-тип-без-имени) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль)
#### 5.10.2.2. Тип данных Tuple и функция tuple #### 5.10.2.2. Тип данных Tuple и функция tuple
@ -1215,13 +1245,15 @@ void main()
В реализации типа `Tuple` есть пара тонких моментов, но она использует средства, доступные любому программисту. Изучение определения типа `Tuple` (которое можно найти в стандартной библиотеке) было бы полезным упражнением. В реализации типа `Tuple` есть пара тонких моментов, но она использует средства, доступные любому программисту. Изучение определения типа `Tuple` (которое можно найти в стандартной библиотеке) было бы полезным упражнением.
[Исходный код](src/chapter-5-10-2-2/)
[В начало ⮍](#5-10-2-2-тип-данных-tuple-и-функция-tuple) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль) [В начало ⮍](#5-10-2-2-тип-данных-tuple-и-функция-tuple) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль)
### 5.10.3. Гетерогенные функции с переменным числом аргументов. Альтернативный подход[^16] ### 5.10.3. Гетерогенные функции с переменным числом аргументов. Альтернативный подход[^16]
Предыдущий подход всем хорош, однако применение шаблонов накладывает на функции ряд ограничений. Поскольку приведенная выше реализация использует шаблоны, для каждого возможного кортежа параметров создается свой экземпляр шаблонной функции. Это не позволяет делать шаблонные функции виртуальными методами класса, объявлять их нефинальными членами интерфейсов, а при невнимательном подходе может приводить к излишнему разрастанию результирующего кода (поэтому шаблонная функция должна быть небольшой, чтобы компилятор счел возможной ее inline-подстановку). Поэтому D предлагает еще два способа объявить функцию с переменным числом аргументов. Оба способа были добавлены в язык до появления шаблонов с переменным числом аргументов, и сегодня считаются небезопасными и устаревшими. Тем не менее они присутствуют и используются в текущих реализациях языка, чаще всего из соображений совместимости. Предыдущий подход всем хорош, однако применение шаблонов накладывает на функции ряд ограничений. Поскольку приведенная выше реализация использует шаблоны, для каждого возможного кортежа параметров создается свой экземпляр шаблонной функции. Это не позволяет делать шаблонные функции виртуальными методами класса, объявлять их нефинальными членами интерфейсов, а при невнимательном подходе может приводить к излишнему разрастанию результирующего кода (поэтому шаблонная функция должна быть небольшой, чтобы компилятор счел возможной ее inline-подстановку). Поэтому D предлагает еще два способа объявить функцию с переменным числом аргументов. Оба способа были добавлены в язык до появления шаблонов с переменным числом аргументов, и сегодня считаются небезопасными и устаревшими. Тем не менее они присутствуют и используются в текущих реализациях языка, чаще всего из соображений совместимости.
[В начало ⮍](#5-10-3-гетерогенные-функции-с-переменным-числом-аргументов-альтернативный-подход16) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль) [В начало ⮍](#5-10-3-гетерогенные-функции-с-переменным-числом-аргументов-альтернативный-подход-16) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль)
#### 5.10.3.1. Функции с переменным числом аргументов в стиле C #### 5.10.3.1. Функции с переменным числом аргументов в стиле C
@ -1307,6 +1339,8 @@ cToString("string", 3.5, 2.7);
результат будет непредсказуемым. Поэтому, например, функция `scanf` может оказаться небезопасной, если строка формата берется из ненадежного источника, ведь с правильно подобранной строкой формата и аргументом можно получить перезапись адреса возврата функции и заставить программу выполнить какой-то свой, наверняка вредоносный код. Поэтому язык D предлагает менее опасный способ создания функций с переменным числом аргументов. результат будет непредсказуемым. Поэтому, например, функция `scanf` может оказаться небезопасной, если строка формата берется из ненадежного источника, ведь с правильно подобранной строкой формата и аргументом можно получить перезапись адреса возврата функции и заставить программу выполнить какой-то свой, наверняка вредоносный код. Поэтому язык D предлагает менее опасный способ создания функций с переменным числом аргументов.
[Исходный код](src/chapter-5-10-3-1/)
[В начало ⮍](#5-10-3-1-функции-с-переменным-числом-аргументов-в-стиле-c) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль) [В начало ⮍](#5-10-3-1-функции-с-переменным-числом-аргументов-в-стиле-c) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль)
#### 5.10.3.2. Функции с переменным числом аргументов в стиле D #### 5.10.3.2. Функции с переменным числом аргументов в стиле D
@ -1394,6 +1428,8 @@ void main()
При этом функция `templatedVariadic`, скорее всего, будет встроена в код путем inline-подстановки, и накладных расходов на лишний вызов функции и разрастание шаблонного кода не будет. При этом функция `templatedVariadic`, скорее всего, будет встроена в код путем inline-подстановки, и накладных расходов на лишний вызов функции и разрастание шаблонного кода не будет.
[Исходный код](src/chapter-5-10-3-2/)
[В начало ⮍](#5-10-3-2-функции-с-переменным-числом-аргументов-в-стиле-d) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль) [В начало ⮍](#5-10-3-2-функции-с-переменным-числом-аргументов-в-стиле-d) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль)
## 5.11. Атрибуты функций ## 5.11. Атрибуты функций
@ -1450,6 +1486,8 @@ pure uint daysInYear(uint y)
} }
``` ```
[Исходный код](src/chapter-5-11-1/)
[В начало ⮍](#5-11-1-чистые-функции) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль) [В начало ⮍](#5-11-1-чистые-функции) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль)
#### 5.11.1.1. «Чист тот, кто чисто поступает» #### 5.11.1.1. «Чист тот, кто чисто поступает»
@ -1513,6 +1551,8 @@ pure ulong fib(uint n)
Принятые в D допущения, смягчающие математическое понятие чистоты, очень полезны, поскольку позволяют взять лучшее из двух миров: железные гарантии функциональной чистоты и удобную реализацию (если код с изменениями более предпочтителен). Принятые в D допущения, смягчающие математическое понятие чистоты, очень полезны, поскольку позволяют взять лучшее из двух миров: железные гарантии функциональной чистоты и удобную реализацию (если код с изменениями более предпочтителен).
[Исходный код](src/chapter-5-11-1-1/)
[В начало ⮍](#5-11-1-1-чист-тот-кто-чисто-поступает) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль) [В начало ⮍](#5-11-1-1-чист-тот-кто-чисто-поступает) [Наверх ⮍](#5-данные-и-функции-функциональный-стиль)
### 5.11.2. Атрибут nothrow ### 5.11.2. Атрибут nothrow

View File

@ -0,0 +1,33 @@
bool find1(int[] haystack, int needle)
{
foreach (v; haystack)
{
if (v == needle)
{
return true;
}
}
return false;
}
int[] find2(int[] haystack, int needle)
{
while (haystack.length > 0 && haystack[0] != needle)
{
haystack = haystack[1 .. $];
}
return haystack;
}
unittest
{
int[] a = [];
assert(find2(a, 5) == []);
a = [ 1, 2, 3 ];
assert(find2(a, 0) == []);
assert(find2(a, 1).length == 3);
assert(find2(a, 2).length == 2);
assert(a[0 .. $ - find2(a, 3).length] == [ 1, 2 ]);
}

View File

@ -0,0 +1,21 @@
import std.conv;
import std.stdio : stdout;
void write(T...)(T args)
{
foreach (arg; args)
{
stdout.rawWrite(to!string(arg));
}
}
void writeln(T...)(T args)
{
write(args, '\n');
stdout.flush();
}
void main()
{
writeln(5, "здравствуй", 4.2);
}

View File

@ -0,0 +1,19 @@
import std.stdio, std.typecons;
void fun(T...)(T args)
{
// Создать кортеж, чтобы "упаковать" все аргументы в одно значение
gun(tuple(args));
}
void gun(T)(T value)
{
// Расширить кортеж и получить исходное множество параметров
writeln(value.expand);
}
void main()
{
fun(1); // Все в порядке
fun(1, 2.2); // Все в порядке
}

View File

@ -0,0 +1,16 @@
import std.stdio;
void testing(T...)(T values)
{
writeln("Переданных аргументов: ", values.length, ".");
// Обращение к каждому индексу и каждому значению
foreach (i, value; values)
{
writeln(i, ": ", typeid(T[i]), " ", value);
}
}
void main()
{
testing(5, "здравствуй", 4.2);
}

View File

@ -0,0 +1,34 @@
import core.stdc.stdarg, std.conv;
extern(C) string cToString(string type, ...)
{
va_list args_list;
va_start(args_list, type);
scope(exit) va_end(args_list);
switch (type)
{
case "int":
auto int_val = va_arg!int(args_list);
return to!string(int_val);
case "double":
auto double_val = va_arg!double(args_list);
return to!string(double_val);
case "complex":
auto re_val = va_arg!double(args_list);
auto im_val = va_arg!double(args_list);
return to!string(re_val) ~ " + " ~ to!string(im_val) ~ "i";
case "string":
return va_arg!string(args_list);
default:
assert(0, "Незнакомый тип");
}
}
unittest
{
assert(cToString("int", 5) == "5");
assert(cToString("double", 2.0) == "2");
assert(cToString("string", "Test string") == "Test string");
assert(cToString("complex", 3.5, 2.7) == "3.5 + 2.7i");
}

View File

@ -0,0 +1,68 @@
import core.stdc.stdarg, std.conv;
string dToString(string type, ...)
{
va_list args_list;
va_copy(args_list, _argptr);
scope(exit) va_end(args_list);
switch (type) {
case "int":
assert(_arguments.length == 1 && _arguments[0] is typeid(int), "Аргумент должен иметь тип int.");
auto int_val = va_arg!int(args_list);
return to!string(int_val);
case "double":
assert(_arguments.length == 1 && _arguments[0] is typeid(double), "Аргумент должен иметь тип double.");
auto double_val = va_arg!double(args_list);
return to!string(double_val);
case "complex":
assert(_arguments.length == 2 && _arguments[0] is typeid(double) && _arguments[1] is typeid(double), "Для типа complex должны быть переданы два аргумента типа double.");
auto re_val = va_arg!double(args_list);
auto im_val = va_arg!double(args_list);
return to!string(re_val) ~ " + " ~ to!string(im_val) ~ "i";
case "string":
assert(_arguments.length == 1 && _arguments[0] is typeid(string), "Аргумент должен иметь тип string.");
return va_arg!string(args_list).idup;
default:
assert(0);
}
}
unittest
{
assert(dToString("int", 5) == "5");
assert(dToString("double", 2.0) == "2");
assert(dToString("string", "Test string") == "Test string");
assert(dToString("complex", 3.5, 2.7) == "3.5 + 2.7i");
}
import std.stdio, std.variant;
void pseudoVariadic(Variant[] vars)
{
foreach (var; vars)
{
if (var.type == typeid(string))
{
writeln("Строка: ", var.get!string);
}
else if (var.type == typeid(int))
{
writeln("Целое число: ", var.get!int);
}
else
{
writeln("Незнакомый тип: ", var.type);
}
}
}
void templatedVariadic(T...)(T args)
{
pseudoVariadic(variantArray(args));
}
void main()
{
templatedVariadic("Здравствуй, мир!", 42);
}

View File

@ -0,0 +1,32 @@
import std.stdio;
ulong fib1(uint n)
{
return n < 2 ? n : fib1(n - 1) + fib1(n - 2);
}
ulong fib2(uint n)
{
ulong iter(uint i, ulong fib_1, ulong fib_2)
{
return i == n ? fib_2 : iter(i + 1, fib_1 + fib_2, fib_1);
}
return iter(0, 1, 0);
}
pure ulong fib3(uint n)
{
ulong fib_1 = 1, fib_2 = 0;
foreach (i; 0 .. n)
{
auto t = fib_1;
fib_1 += fib_2;
fib_2 = t;
}
return fib_2;
}
void main()
{
writeln(fib3(40));
}

View File

@ -0,0 +1,16 @@
import std.stdio;
pure bool leapYear(uint y)
{
return (y % 4) == 0 && (y % 100 || (y % 400) == 0);
}
pure uint daysInYear(uint y)
{
return 365 + leapYear(y);
}
void main()
{
writeln(daysInYear(2022));
}

View File

@ -0,0 +1,11 @@
ref int bump(ref int x)
{
return ++x;
}
unittest
{
int x = 1;
bump(bump(x)); // Два увеличения на 1
assert(x == 3);
}

View File

@ -0,0 +1,16 @@
// Вы­чис­ля­ет ча­ст­ное и ос­та­ток от де­ле­ния для ар­гу­мен­тов a и b.
// Воз­вра­ща­ет ча­ст­ное по зна­че­нию, а ос­та­ток в па­ра­мет­ре rem.
int divrem(int a, int b, out int rem)
{
assert(b != 0);
rem = a % b;
return a / b;
}
unittest
{
int r;
int d = divrem(5, 2, r);
assert(d == 2 && r == 1);
}

View File

@ -0,0 +1,26 @@
void fun(int x)
{
x += 42;
}
void gun(int[] x)
{
x = [ 1, 2, 3 ];
}
void hun(int[] x)
{
x[0] = x[1];
}
unittest
{
int x = 10;
fun(x);
assert(x == 10); // Ничего не изменилось
int[] y = [ 10, 20, 30 ];
gun(y);
assert(y == [ 10, 20, 30 ]); // Ничего не изменилось
hun(y);
assert(y == [ 20, 20, 30 ]); // Изменилось!
}

View File

@ -0,0 +1,18 @@
T[] find(T)(T[] haystack, T needle)
{
while (haystack.length > 0 && haystack[0] != needle)
{
haystack = haystack[1 .. $];
}
return haystack;
}
unittest
{
// Проверка способностей к обобщению
double[] d = [ 1.5, 2.4 ];
assert(find(d, 1.0) == null);
assert(find(d, 1.5) == d);
string[] s = [ "one", "two" ];
assert(find(s, "two") == [ "two" ]);
}

View File

@ -0,0 +1,15 @@
T[] find(T, E)(T[] haystack, E needle)
if (is(typeof(haystack[0] != needle) == bool))
{
while (haystack.length > 0 && haystack[0] != needle)
{
haystack = haystack[1 .. $];
}
return haystack;
}
unittest
{
// assert(find([1, 2, 3], "Hello"));
assert(find([1, 2, 3], 1.0));
}

View File

@ -0,0 +1,27 @@
import std.stdio;
void transmogrify(uint value)
{
writeln("Вызов функции с uint: ", value);
}
void transmogrify(long value)
{
writeln("Вызов функции с long: ", value);
}
void transmogrify(T)(T value)
{
writeln("Вызов функции с T: ", value);
}
unittest
{
transmogrify(42); // Вы­зы­ва­ет transmogrify(uint)
transmogrify("hello"); // Вы­зы­ва­ет transmogrify(T), T=string
transmogrify(1.1); // Вы­зы­ва­ет transmogrify(T), T=double
// Вызов функции с uint: 42
// Вызов функции с T: hello
// Вызов функции с T: 1.1
}

View File

@ -0,0 +1,20 @@
T1[] find(T1, T2)(T1[] longer, T2[] shorter)
if (is(typeof(longer[0 .. 1] == shorter) : bool))
{
while (longer.length >= shorter.length)
{
if (longer[0 .. shorter.length] == shorter)
{
break;
}
longer = longer[1 .. $];
}
return longer;
}
unittest
{
double[] d1 = [ 6.0, 1.5, 2.25, 3 ];
float[] d2 = [ 1.5, 2.25 ];
assert(find(d1, d2) == d1[1 .. $]);
}

View File

@ -0,0 +1,24 @@
import std.stdio;
T[] find(alias pred, T)(T[] input)
if (is(typeof(pred(input[0])) == bool))
{
for (; input.length > 0; input = input[1 .. $])
{
if (pred(input[0])) break;
}
return input;
}
unittest
{
int[] a = [ 1, 2, 3, 4, -5, 3, -4 ];
// Найти первое отрицательное число
auto b = find!(function bool(int x) { return x < 0; })(a).dup;
writeln(b);
auto c = find!((x) { return x > 0; })(b);
writeln(c);
string[] str = ["one", "two", "ab", "three", "four"];
auto d = find!((x) { return x.length == 2; })(str);
writeln(d);
}

View File

@ -0,0 +1,34 @@
import std.stdio;
T[] find(alias pred, T)(T[] input)
if (is(typeof(pred(input[0])) == bool))
{
for (; input.length > 0; input = input[1 .. $])
{
if (pred(input[0])) break;
}
return input;
}
unittest
{
int zero = 0;
bool isTypeLow(int x)
{
return x < zero;
}
static bool isTypeBig(int x)
{
// Из-за static zero не будет видно внутри функции
return x > zero;
}
int[] a = [ 1, 2, 3, 4, -5, 3, -4 ];
// Найти первое отрицательное число
auto b = find!(isTypeLow)(a).dup;
writeln(b);
auto c = find!(isTypeBig)(b);
writeln(c);
}

View File

@ -0,0 +1,15 @@
import std.algorithm;
T[] delegate(T[]) finder(T)(T x)
if (is(typeof(x == x) == bool))
{
return delegate(T[] a) { return find(a, x); };
}
unittest
{
auto d = finder(5);
assert(d([1, 3, 5, 7, 9]) == [ 5, 7, 9 ]);
d = finder(10);
assert(d([1, 3, 5, 7, 9]) == []);
}

View File

@ -0,0 +1,21 @@
import std.stdio;
@property bool empty(T)(T[] a) { return a.length == 0; }
@property ref T front(T)(T[] a) { return a[0]; }
void popFront(T)(ref T[] a) { a = a[1 .. $]; }
R find(R, T)(R haystack, T needle)
if (is(typeof(haystack.front != needle) == bool))
{
while (!haystack.empty && haystack.front != needle)
{
haystack.popFront();
}
return haystack;
}
unittest
{
writeln(find([1, 2, 3], 2.0));
assert(find([1, 2, 3], 1.0));
}

View File

@ -0,0 +1,40 @@
// import std.range;
// V reduce(alias fun, V, R)(V x, R range)
// if (isInputRange!R && is(typeof(x = fun(x, range.front))))
// {
// for (; !range.empty; range.popFront())
// {
// x = fun(x, range.front);
// }
// return x;
// }
@property bool empty(T)(T[] a) { return a.length == 0; }
@property ref T front(T)(T[] a) { return a[0]; }
void popFront(T)(ref T[] a) { a = a[1 .. $]; }
V reduce(alias fun, V, R)(V x, R range)
if (is(typeof(x = fun(x, range.front)))
&& is(typeof(range.empty) == bool)
&& is(typeof(range.popFront())))
{
for (; !range.empty; range.popFront())
{
x = fun(x, range.front);
}
return x;
}
unittest
{
int[] r = [ 10, 14, 3, 5, 23 ];
// Вычислить сумму всех элементов
int sum = reduce!((a, b) { return a + b; })(0, r);
assert(sum == 55);
// Вычислить минимум
int min = reduce!((a, b) { return a < b ? a : b; })(r[0], r);
assert(min == 3);
}