This commit is contained in:
Alexander Zhirov 2023-01-22 11:36:37 +03:00
parent 2b58cd0390
commit b2bfe5c7a1
1 changed files with 44 additions and 3 deletions

View File

@ -1,8 +1,8 @@
# 1. Знакомство с языком D
- [1.1. Числа и выражения](#11-числа-и-выражения)
- [1.2. Инструкции](#12-инструкции)
- [1.3. Основы работы с функциями]()
- [1.1. Числа и выражения](#1-1-числа-и-выражения)
- [1.2. Инструкции](#1-2-инструкции)
- [1.3. Основы работы с функциями](#1-3-основы-работы-с-функциями)
- 1.4. Массивы и ассоциативные массивы
- [1.4.1. Работа со словарем]()
- [1.4.2. Получение среза массива. Функции с обобщенными типами параметров. Тесты модулей]()
@ -178,4 +178,45 @@ if (‹выражение›) инструкция1 else ‹инструк
Чисто теоретический вывод, известный как принцип структурного программирования, гласит, что все алгоритмы можно реализовать с помощью составных инструкций, `if`-проверок и циклов а-ля `for` и `foreach`. Разумеется, любой адекватный язык (как и D) предлагает гораздо больше, но мы пока постановим, что с нас довольно и этих инструкций, и двинемся дальше.
## 1.3. Основы работы с функциями
Оставим пока в стороне обязательное определение функции `main` и посмотрим, как определяются другие функции на D. Определение функции соответствует модели, характерной и для других Алгол-подобных языков: сначала пишется возвращаемый тип, потом имя функции и, наконец, заключенный в круглые скобки список формальных аргументов, разделенных запятыми. Например, определение функции с именем `pow`, которая принимает значения типа `double` и `int`, а возвращает `double`, записывается так:
```d
double pow(double base, int exponent)
{
...
}
```
Каждый параметр функции (`base` и `exponent` в данном примере) кроме типа может иметь необязательный ***класс памяти*** (***storage class***), определяющий способ передачи аргумента в функцию при ее вызове[^2].
По умолчанию аргументы передаются в `pow` по значению. Если перед типом параметра указан класс памяти `ref`, то параметр привязывается напрямую к входному аргументу, так что изменение параметра непосредственно отражается на значении, полученном извне. Например:
```d
import std.stdio;
void fun(ref uint x, double y)
{
x = 42;
y = 3.14;
}
void main()
{
uint a = 1;
double b = 2;
fun(a, b);
writeln(a, " ", b);
}
```
Эта программа печатает `42 2`, потому что `x` определен как `ref uint`, то есть когда значение присваивается x, на самом деле операция проводится с `a`. С другой стороны, присваивание значения переменной `y` никак не скажется на `b`, поскольку `y` это внутренняя копия в распоряжении функции `fun`.
Последние «украшения», которые мы обсудим в этом кратком введении, это `in` и `out`. Попросту говоря, `in` данное функцией «обещание» только смотреть на параметр, не «трогая» его. Указание `out` в определении параметра функции действует сходно с `ref`, с той поправкой, что параметр принудительно инициализируется своим значением по умолчанию при «входе» в функцию. (Для каждого типа `T` определено начальное значение, обозначаемое как `T.init`. Пользовательские типы могут определять собственное значение по умолчанию.)
О функциях можно еще долго рассказывать. Можно передавать функции другим функциям, встраивать одну в другую, разрешать функции сохранять свою локальную среду (полнофункциональная синтаксическая клауза), создавать анонимные функции (лямбда-функции), с удобством манипулировать ими и еще множество дополнительных «вкусностей». Со временем мы доберемся до каждой из них.
[^1]: «Shebang» (от shell bang: shell консоль, bang восклицательный знак), или «shabang» (# sharp) обозначение пути к компилятору или интерпретатору в виде `#!/путь/к/программе`. *Прим. пер.*
[^2]: В этой книге под «параметром» понимается значение, используемое внутри функции, а под «аргументом» значение, передаваемое в функцию извне.