В этой главе описываются функции, используемые в программировании на D.
В своей основе определение функции состоит из заголовка функции и тела функции.
возвращаемый_тип имя_функции( список_параметров ) { тело_функции }
Вот описание всех частей функции:
Возвращаемый тип – Функция может возвращать значение. Тип возвращаемый_тип – это тип данных значения, возвращаемого функцией. Некоторые функции выполняют свои действия без возврата значения. В этом случае в качестве возвращаемого_типа ставится ключевое слово void.
Имя функции − Это фактическое имя функции. Имя функции и список параметров совместно составляют сигнатуру функции.
Параметры − Параметр похож на заполнитель. Когда функция вызывается, вы передаете значение параметру. На это значение ссылается фактический параметр или аргумент. Список_параметров определяет тип, порядок и количество параметров функции. Параметры являются необязательными, то есть функция может не содержать никаких параметров.
Тело функции − Тело функции содержит набор операторов, которые определяют, что делает функция.
Вы можете вызвать функцию следующим образом:
имя_функции(список_параметров)
D поддерживает широкий спектр функций, и они перечислены ниже.
Ниже описаны различные функции.
Чистые функции (pure) – это функции, которые не могут получить доступ к глобальному или статическому, изменяемому состоянию, сохранять его через свои аргументы. Таким образом появляется возможность включить оптимизацию, основанную на том факте, что чистая функция, как гарантируется, не изменяет ничего, что не передается ей, а в тех случаях, когда компилятор может гарантировать, что чистая функция не может изменить свои аргументы, она может обеспечить полную функциональную чистоту, то есть гарантировать, что функция всегда вернёт один и тот же результат для одних и тех же аргументов.
import std.stdio; int x = 10; immutable int y = 30; const int* p; pure int purefunc(int i,const char* q,immutable int* s) { //writeln("Simple print"); //нельзя вызывать "нечистую" функцию 'writeln' debug writeln("in foo()"); // можно, "нечистый" код допустим в операторе debug // x = i; // ошибка, изменение глобального состояния // i = x; // ошибка, чтение изменяемого глобального состояния // i = *p; // ошибка, чтение константного глобального состояния i = y; // можно, чтение неизменяемого глобального состояния auto myvar = new int; // Можно использовать выражение new return i; } void main() { writeln("Значение, возвращаемое из чистой функции : ",purefunc(x,null,null)); }
Когда вы скомпилируете и выполните эту программу, она возвратит следующий результат:
Значение, возвращаемое из чистой функции : 30
Функции Nothrow не выбрасывают никаких исключений, унаследованных от класса Exception.
Nothrow гарантирует, что функция не выбрасывает никаких исключений.
import std.stdio; int add(int a, int b) nothrow { //writeln("сложение"); Это не удастся, потому что writeln может бросать исключения int result; try { writeln("сложение"); // компилируется result = a + b; } catch (Exception error) { // ловит все исключения } return result; } void main() { writeln("Результат сложения: ", add(10,20)); }
Когда вы скомпилируете и выполните эту программу, она возвратит следующий результат:
сложение Результат сложения: 30
Ref-Функции позволяют возвращать значение функции по ссылке. Это аналогично ref-параметрам функции.
import std.stdio; // "greater" переводится как "больший" ref int greater(ref int first, ref int second) { return (first > second) ? first : second; } void main() { int a = 1; int b = 2; greater(a, b) += 10; writefln("a: %s, b: %s", a, b); }
Когда вы скомпилируете и выполните эту программу, она возвратит следующий результат:
a: 1, b: 12
Auto-Функции могут возвращать значение любого типа. Нет никаких ограничений на возвращаемый тип. Ниже приведён простой пример функции типа auto.
import std.stdio; auto add(int first, double second) { double result = first + second; return result; } void main() { int a = 1; double b = 2.5; writeln("add(a,b) = ", add(a, b)); }
Когда вы скомпилируете и выполните эту программу, она возвратит следующий результат:
add(a,b) = 3.5
Функции с переменным числом аргументов (variadic) являются такими функциями, в которых число параметров функции определяется во время выполнения. В C существует ограничение, должен быть по крайней мере один параметр. Но в языке D нет такого ограничения. Ниже приведен простой пример.
import std.stdio; import core.vararg; void printargs(int x, ...) { for (int i = 0; i < _arguments.length; i++) { write(_arguments[i]); if (_arguments[i] == typeid(int)) { int j = va_arg!(int)(_argptr); writefln("\t%d", j); } else if (_arguments[i] == typeid(long)) { long j = va_arg!(long)(_argptr); writefln("\t%d", j); } else if (_arguments[i] == typeid(double)) { double d = va_arg!(double)(_argptr); writefln("\t%g", d); } } } void main() { printargs(1, 2, 3L, 4.5); }
Когда вы скомпилируете и выполните эту программу, она возвратит следующий результат:
int 2 long 3 double 4.5
Inout может использоваться как для параметров, так и для возвращаемых типов функций. Это похоже на шаблон для квалификаторов изменяемости, const и immutable. Атрибут изменяемости выводится из параметра. Значит, inout переносит выведенный атрибут изменяемости на возвращаемый тип. Ниже показан простой пример, показывающий, как меняется квалификатор изменяемости.
import std.stdio; inout(char)[] qoutedWord(inout(char)[] phrase) { return '"' ~ phrase ~ '"'; } void main() { char[] a = "проверка a".dup; a = qoutedWord(a); writeln(typeof(qoutedWord(a)).stringof," ", a); const(char)[] b = "проверка b"; b = qoutedWord(b); writeln(typeof(qoutedWord(b)).stringof," ", b); immutable(char)[] c = "проверка c"; c = qoutedWord(c); writeln(typeof(qoutedWord(c)).stringof," ", c); }
Когда вы скомпилируете и выполните эту программу, она возвратит следующий результат:
char[] "проверка a" const(char)[] "проверка b" string "проверка c"
Свойства (properties) позволяют использовать функции-члены как данные-члены. Они используют ключевое слово @property. Свойства связаны с родственной функцией, возвращающей значение, когда оно требуется. Ниже приведен простой пример свойства.
import std.stdio; // прямоугольник struct Rectangle { double width; // ширина double height; // высота // площадь double area() const @property { return width*height; } void area(double newArea) @property { auto multiplier = newArea / area; width *= multiplier; writeln("Значение установлено!"); } } void main() { auto rectangle = Rectangle(20,10); writeln("Площадь равна ", rectangle.area); rectangle.area = 300; writeln("Изменённая ширина равна ", rectangle.width); }
Когда вы скомпилируете и выполните эту программу, она возвратит следующий результат:
Площадь равна 200 Значение установлено! Изменённая ширина равна 30