Menu

Делегаты

Функции как аргументы

Функция также может быть параметром другой функции:

void doSomething(int function(int, int) doer) {
    // вызов переданной функции
    doer(5,5);
}

doSomething(add); // здесь используется глобальная 
                  // функция `add` (сложение), у нее
                  // должно быть два параметра int

Затем doer может быть вызвана подобно любой обычной функции.

Локальные функции с контекстом

В примере выше используется тип function, который является указателем на глобальную функцию. При ссылках на методы или локальные функции нужно использовать делегаты (delegate). Это указатель на функцию, который дополнительно содержит информацию об контексте, иначе - enclosure, также называемое closure (замыкание) в других языках. Например, delegate, указывающий на метод класса, также содержит указатель на объект класса. Делегат, полученный из вложенной функции, содержит указатель на контекст внешней функции. Однако компилятор может автоматически скопировать эти данные на кучу, если посчитает это нужным для безопасности памяти. Тогда делегат будет содержать указатель на эту область кучи.

void foo() {
    void local() {
        writeln("local");
    }
    auto f = &local; // Тип f - delegate()
}

Такая же функция doSomething, принимающая delegate, выглядела бы так:

void doSomething(int delegate(int,int) doer);

Объекты delegate и functionнельзя смешивать. Но стандартная функция std.functional.toDelegate преобразует function в delegate.

Анонимные функции и лямбды

Поскольку функции можно сохранять как переменные и передавать в другие функции, было бы утомительно давать им собственные имена и определять их. Поэтому в D допускаются безымянные функции и однострочные лямбды.

auto f = (int lhs, int rhs) => lhs + rhs;
// Внутри лямбда преобразовывается в следующее:

auto f = (int lhs, int rhs) {
    return lhs + rhs;
};

Также возможно передавать строки как аргумент шаблона в функциональные части стандартной библиотеки языка D. Например, они предлагают удобный способ определения свёртки списка (folding, или иначе reducer):

[1, 2, 3].reduce!`a + b`; // 6

Строковые функции возможны только для одного или двух аргументов, тогда используется a как первый аргумент и b как второй аргумент.

Подробнее

rdmd playground.d