This commit is contained in:
Alexander Zhirov 2023-01-22 15:48:09 +03:00
parent 4be7b9c4ce
commit 50df530401
1 changed files with 12 additions and 1 deletions

View File

@ -67,7 +67,8 @@ import std.stdio;
Следующие разделы это стремительная поездка по Дибургу. Небольшие показательные программы дают общее представление о языке. Основная цель повествования на данном этапе обрисовать общую картину, а не дать ряд педантичных определений. Позже все аспекты языка будут рассмотрены с должным вниманием в деталях. Следующие разделы это стремительная поездка по Дибургу. Небольшие показательные программы дают общее представление о языке. Основная цель повествования на данном этапе обрисовать общую картину, а не дать ряд педантичных определений. Позже все аспекты языка будут рассмотрены с должным вниманием в деталях.
[Исходный код](src/chapter-1/) [Исходный код](src/chapter-1/)
[В начало ⮍](#1-знакомство-с-языком-d) [Наверх ⮍](#1-знакомство-с-языком-d)
[В начало ⮍](#1-знакомство-с-языком-d)
## 1.1. Числа и выражения ## 1.1. Числа и выражения
@ -150,6 +151,7 @@ writefln("%s'%s''\t%s", feet, inches, (feet * inchesPerFoot + inches) * cmPerInc
Если вы использовали `printf` прежде, то могли бы почувствовать себя как дома, когда б не маленькая особенность: мы ведь выводим значения переменных типа `int` и `double` как же получилось, что и те и другие описаны с помощью спецификатора `%s`, обычно применяемого для вывода строк? Ответ прост. Средства D для работы с переменным количеством аргументов дают `writefln` доступ к информации об исходных типах переданных аргументов. Благодаря такому подходу программа получает ряд преимуществ: 1) значение `%s` может быть расширено до «строкового представления по умолчанию для типа переданного аргумента» и 2) если не удалось сопоставить спецификатор формата с типами переданных аргументов, вы получите ошибку в чистом виде, а не загадочное поведение, присущее вызовам `printf` с неверно заданным форматом (не говоря уже о подрыве безопасности, возможном при вызове `printf` с непроверяемыми заранее форматирующими строками). Если вы использовали `printf` прежде, то могли бы почувствовать себя как дома, когда б не маленькая особенность: мы ведь выводим значения переменных типа `int` и `double` как же получилось, что и те и другие описаны с помощью спецификатора `%s`, обычно применяемого для вывода строк? Ответ прост. Средства D для работы с переменным количеством аргументов дают `writefln` доступ к информации об исходных типах переданных аргументов. Благодаря такому подходу программа получает ряд преимуществ: 1) значение `%s` может быть расширено до «строкового представления по умолчанию для типа переданного аргумента» и 2) если не удалось сопоставить спецификатор формата с типами переданных аргументов, вы получите ошибку в чистом виде, а не загадочное поведение, присущее вызовам `printf` с неверно заданным форматом (не говоря уже о подрыве безопасности, возможном при вызове `printf` с непроверяемыми заранее форматирующими строками).
[Исходный код](src/chapter-1-1/) [Исходный код](src/chapter-1-1/)
[В начало ⮍](#1-1-числа-и-выражения) [Наверх ⮍](#1-знакомство-с-языком-d) [В начало ⮍](#1-1-числа-и-выражения) [Наверх ⮍](#1-знакомство-с-языком-d)
## 1.2. Инструкции ## 1.2. Инструкции
@ -186,6 +188,7 @@ if (‹выражение›) инструкция1 else ‹инструк
Чисто теоретический вывод, известный как принцип структурного программирования, гласит, что все алгоритмы можно реализовать с помощью составных инструкций, `if`-проверок и циклов а-ля `for` и `foreach`. Разумеется, любой адекватный язык (как и D) предлагает гораздо больше, но мы пока постановим, что с нас довольно и этих инструкций, и двинемся дальше. Чисто теоретический вывод, известный как принцип структурного программирования, гласит, что все алгоритмы можно реализовать с помощью составных инструкций, `if`-проверок и циклов а-ля `for` и `foreach`. Разумеется, любой адекватный язык (как и D) предлагает гораздо больше, но мы пока постановим, что с нас довольно и этих инструкций, и двинемся дальше.
[Исходный код](src/chapter-1-2/) [Исходный код](src/chapter-1-2/)
[В начало ⮍](#1-2-инструкции) [Наверх ⮍](#1-знакомство-с-языком-d) [В начало ⮍](#1-2-инструкции) [Наверх ⮍](#1-знакомство-с-языком-d)
## 1.3. Основы работы с функциями ## 1.3. Основы работы с функциями
@ -229,6 +232,7 @@ void main()
О функциях можно еще долго рассказывать. Можно передавать функции другим функциям, встраивать одну в другую, разрешать функции сохранять свою локальную среду (полнофункциональная синтаксическая клауза), создавать анонимные функции (лямбда-функции), с удобством манипулировать ими и еще множество дополнительных «вкусностей». Со временем мы доберемся до каждой из них. О функциях можно еще долго рассказывать. Можно передавать функции другим функциям, встраивать одну в другую, разрешать функции сохранять свою локальную среду (полнофункциональная синтаксическая клауза), создавать анонимные функции (лямбда-функции), с удобством манипулировать ими и еще множество дополнительных «вкусностей». Со временем мы доберемся до каждой из них.
[Исходный код](src/chapter-1-3/) [Исходный код](src/chapter-1-3/)
[В начало ⮍](#1-3-основы-работы-с-функциями) [Наверх ⮍](#1-знакомство-с-языком-d) [В начало ⮍](#1-3-основы-работы-с-функциями) [Наверх ⮍](#1-знакомство-с-языком-d)
## 1.4. Массивы и ассоциативные массивы ## 1.4. Массивы и ассоциативные массивы
@ -336,6 +340,7 @@ b = a.dup; // Полностью скопировать a в b
``` ```
[Исходный код](src/chapter-1-4-1/) [Исходный код](src/chapter-1-4-1/)
[В начало ⮍](#1-4-1-работаем-со-словарем) [Наверх ⮍](#1-знакомство-с-языком-d) [В начало ⮍](#1-4-1-работаем-со-словарем) [Наверх ⮍](#1-знакомство-с-языком-d)
### 1.4.2. Получение среза массива. Функции с обобщенными типами параметров. Тесты модулей ### 1.4.2. Получение среза массива. Функции с обобщенными типами параметров. Тесты модулей
@ -400,6 +405,7 @@ bool binarySearchR(T)(T[] input, T value)
Рекурсивная реализация явно проще и концентрированнее по сравнению со своим итеративным собратом. Кроме того, она ничуть не менее эффективна, так как рекурсивные вызовы оптимизируются благодаря популярной среди компиляторов технике, известной как *оптимизация хвостовой рекурсии*. В двух словах: если функция возвращает просто вызов самой себя (но с другими аргументами), компилятор модифицирует аргументы и инициирует переход к началу функции. Рекурсивная реализация явно проще и концентрированнее по сравнению со своим итеративным собратом. Кроме того, она ничуть не менее эффективна, так как рекурсивные вызовы оптимизируются благодаря популярной среди компиляторов технике, известной как *оптимизация хвостовой рекурсии*. В двух словах: если функция возвращает просто вызов самой себя (но с другими аргументами), компилятор модифицирует аргументы и инициирует переход к началу функции.
[Исходный код](src/chapter-1-4-2/) [Исходный код](src/chapter-1-4-2/)
[В начало ⮍](#1-4-2-получение-среза-массива-функции-с-обобщенными-типами-параметров-тесты-модулей) [Наверх ⮍](#1-знакомство-с-языком-d) [В начало ⮍](#1-4-2-получение-среза-массива-функции-с-обобщенными-типами-параметров-тесты-модулей) [Наверх ⮍](#1-знакомство-с-языком-d)
### 1.4.3. Подсчет частот. Лямбда-функции ### 1.4.3. Подсчет частот. Лямбда-функции
@ -510,6 +516,7 @@ sort!(‹аргументы времени компиляции›)(аргу
Что и ожидалось: самые часто употребляемые слова набрали больше всего «очков». Настораживает лишь «Ham»[^7]. Это слово в пьесе вовсе не отражает кулинарные предпочтения героев. «Ham» всего лишь сокращение от «Hamlet» (Гамлет), которым помечена каждая из его реплик. Явно у него был повод высказаться 358 раз больше, чем любой другой герой пьесы. Далее по списку следует король всего 116 реплик, меньше трети сказанного Гамлетом. А Офелия с ее 58 репликами просто молчунья. Что и ожидалось: самые часто употребляемые слова набрали больше всего «очков». Настораживает лишь «Ham»[^7]. Это слово в пьесе вовсе не отражает кулинарные предпочтения героев. «Ham» всего лишь сокращение от «Hamlet» (Гамлет), которым помечена каждая из его реплик. Явно у него был повод высказаться 358 раз больше, чем любой другой герой пьесы. Далее по списку следует король всего 116 реплик, меньше трети сказанного Гамлетом. А Офелия с ее 58 репликами просто молчунья.
[Исходный код](src/chapter-1-4-3/) [Исходный код](src/chapter-1-4-3/)
[В начало ⮍](#1-4-3-подсчет-частот-лямбда-функции) [Наверх ⮍](#1-знакомство-с-языком-d) [В начало ⮍](#1-4-3-подсчет-частот-лямбда-функции) [Наверх ⮍](#1-знакомство-с-языком-d)
## 1.5. Основные структуры данных ## 1.5. Основные структуры данных
@ -686,6 +693,7 @@ Ambassador 41 34
В выводе есть немного шума (например, `"Both [Mar"`), который прилежный программист легко устранит и который вряд ли статистически влияет на то, что действительно представляет для нас интерес. Тем не менее исправление последних огрехов поучительное (и рекомендуемое) упражнение. В выводе есть немного шума (например, `"Both [Mar"`), который прилежный программист легко устранит и который вряд ли статистически влияет на то, что действительно представляет для нас интерес. Тем не менее исправление последних огрехов поучительное (и рекомендуемое) упражнение.
[Исходный код](src/chapter-1-5/) [Исходный код](src/chapter-1-5/)
[В начало ⮍](#1-5-основные-структуры-данных) [Наверх ⮍](#1-знакомство-с-языком-d) [В начало ⮍](#1-5-основные-структуры-данных) [Наверх ⮍](#1-знакомство-с-языком-d)
## 1.6. Интерфейсы и классы ## 1.6. Интерфейсы и классы
@ -789,6 +797,7 @@ auto newStat = cast(Stat) Object.factory("stats." ~ arg);
Оглянувшись назад, мы заметим, что львиная доля того, что делает скрипт, выполняется в первых пяти его строках. Эта самая сложная часть, которая полностью определяет весь остальной код. Второй цикл читает по одному числу за раз (об этом заботится функция `readf`) и вызывает `accumulate` для всех объектов, собирающих статистику. Функция `readf` возвращает число объектов, успешно прочитанных согласно заданной строке формата. В нашем случае формат задан в виде строки `" %s "`, что означает «один элемент, окруженный любым количеством пробелов». (Тип элемента определяется типом считанного элемента, в нашем случае `x` принимает значение типа `double`.) Последнее, что делает программа, выводит результаты вычислений на печать. Оглянувшись назад, мы заметим, что львиная доля того, что делает скрипт, выполняется в первых пяти его строках. Эта самая сложная часть, которая полностью определяет весь остальной код. Второй цикл читает по одному числу за раз (об этом заботится функция `readf`) и вызывает `accumulate` для всех объектов, собирающих статистику. Функция `readf` возвращает число объектов, успешно прочитанных согласно заданной строке формата. В нашем случае формат задан в виде строки `" %s "`, что означает «один элемент, окруженный любым количеством пробелов». (Тип элемента определяется типом считанного элемента, в нашем случае `x` принимает значение типа `double`.) Последнее, что делает программа, выводит результаты вычислений на печать.
[Исходный код](src/chapter-1-6/) [Исходный код](src/chapter-1-6/)
[В начало ⮍](#1-6-интерфейсы-и-классы) [Наверх ⮍](#1-знакомство-с-языком-d) [В начало ⮍](#1-6-интерфейсы-и-классы) [Наверх ⮍](#1-знакомство-с-языком-d)
### 1.6.1. Больше статистики. Наследование ### 1.6.1. Больше статистики. Наследование
@ -860,6 +869,7 @@ class Average : IncrementalStat
Начнем с того, что в `Average` вводится еще одно поле, `items`, которое инициализируется нулем с помощью синтаксиса `items = 0` (только для того, чтобы показать, как надо инициализировать переменные, но, как отмечалось выше, целые числа и так инициализируются нулем по умолчанию). Второе, что необходимо отметить: `Average` определяет конструктор, который присваивает переменной `_result` ноль. Так сделано, потому что, в отличие от минимума или максимума, при отсутствии аргументов среднее арифметическое считается равным нулю. И хотя может показаться, что инициализировать `_result` значением NaN только для того, чтобы тут же записать в эту переменную ноль, бессмысленное действие, уход от так называемого «мертвого присваивания» представляет собой легкую добычу для любого оптимизатора. Наконец, `Average` переопределяет метод `postprocess`, несмотря на то что в классе `IncrementalStat` он уже определен. В языке D по умолчанию можно переопределить (унаследовать и заново определить) методы любого класса, но надо обязательно добавлять директиву `override`, чтобы избежать всевозможных несчастных случаев (таких как неудача переопределения в связи с какой-нибудь опечаткой или изменением в базовом типе, либо переопределение чего-нибудь по ошибке). Если вы поставите перед методом класса ключевое слово `final`, то запретите классам-потомкам переопределять эту функцию (что эффективно останавливает механизм динамического поиска методов по дереву классов). Начнем с того, что в `Average` вводится еще одно поле, `items`, которое инициализируется нулем с помощью синтаксиса `items = 0` (только для того, чтобы показать, как надо инициализировать переменные, но, как отмечалось выше, целые числа и так инициализируются нулем по умолчанию). Второе, что необходимо отметить: `Average` определяет конструктор, который присваивает переменной `_result` ноль. Так сделано, потому что, в отличие от минимума или максимума, при отсутствии аргументов среднее арифметическое считается равным нулю. И хотя может показаться, что инициализировать `_result` значением NaN только для того, чтобы тут же записать в эту переменную ноль, бессмысленное действие, уход от так называемого «мертвого присваивания» представляет собой легкую добычу для любого оптимизатора. Наконец, `Average` переопределяет метод `postprocess`, несмотря на то что в классе `IncrementalStat` он уже определен. В языке D по умолчанию можно переопределить (унаследовать и заново определить) методы любого класса, но надо обязательно добавлять директиву `override`, чтобы избежать всевозможных несчастных случаев (таких как неудача переопределения в связи с какой-нибудь опечаткой или изменением в базовом типе, либо переопределение чего-нибудь по ошибке). Если вы поставите перед методом класса ключевое слово `final`, то запретите классам-потомкам переопределять эту функцию (что эффективно останавливает механизм динамического поиска методов по дереву классов).
[Исходный код](src/chapter-1-6-1/) [Исходный код](src/chapter-1-6-1/)
[В начало ⮍](#1-6-1-больше-статистики-наследование) [Наверх ⮍](#1-знакомство-с-языком-d) [В начало ⮍](#1-6-1-больше-статистики-наследование) [Наверх ⮍](#1-знакомство-с-языком-d)
## 1.7. Значения против ссылок ## 1.7. Значения против ссылок
@ -905,6 +915,7 @@ void main()
В завершение хочется сказать, что структуры пожалуй, наиболее гибкое проектное решение. Определив структуру, вы можете вдохнуть в нее любую семантику. Вы можете сделать так, что значение будет копироваться постоянно, реализовать ленивое копирование, а-ля копирование при записи, или подсчитывать ссылки, или выбрать что-то среднее между этими способами. Вы даже можете определить ссылочную семантику, используя классы или указатели *внутри* своей структуры. С другой стороны, некоторые из этих альтернатив требуют подкованности в техническом плане; использование классов, напротив, подразумевает простоту и унифицированность. В завершение хочется сказать, что структуры пожалуй, наиболее гибкое проектное решение. Определив структуру, вы можете вдохнуть в нее любую семантику. Вы можете сделать так, что значение будет копироваться постоянно, реализовать ленивое копирование, а-ля копирование при записи, или подсчитывать ссылки, или выбрать что-то среднее между этими способами. Вы даже можете определить ссылочную семантику, используя классы или указатели *внутри* своей структуры. С другой стороны, некоторые из этих альтернатив требуют подкованности в техническом плане; использование классов, напротив, подразумевает простоту и унифицированность.
[Исходный код](src/chapter-1-7/) [Исходный код](src/chapter-1-7/)
[В начало ⮍](#1-7-значения-против-ссылок) [Наверх ⮍](#1-знакомство-с-языком-d) [В начало ⮍](#1-7-значения-против-ссылок) [Наверх ⮍](#1-знакомство-с-языком-d)
## 1.8. Итоги ## 1.8. Итоги