デリゲート

引数としての関数

関数は他の関数の引数にもなれます:

void doSomething(int function(int, int) doer) {
    // 渡された関数を呼ぶ
    doer(5,5);
}

doSomething(&add); // ここでグローバル関数`add`を使う
                  // addはintの引数を2つもつ必要があります

そしてdoerは他の普通の関数のように呼びだされます。

文脈のあるローカル関数

上記のサンプルは、グローバル関数へのポインタであるfunction型を使っています。 メンバ関数またはローカル関数が参照され次第、delegateは使用されなければなりません。 それは関数ポインタに加えてその文脈 - またはそれを作った関数(enclosure) - の 情報を含むもので、したがって他の言語ではクロージャとも呼ばれます。 例えばクラスのメンバ関数を指すdelegateはクラスオブジェクトのポインタを含みます。 ネストした関数によって作られたdelegateは代わりに囲まれた文脈へのリンクを含みます。 しかし、それがメモリ安全のために必要ならば、Dコンパイラは自動的にヒープ上に 文脈のコピーを作ることもあり、そのときデリゲートはこのヒープ領域へリンクします。

void foo() {
    void local() {
        writeln("local");
    }
    auto f = &local; // fはdelegate()型
}

delegateをとる同様の関数doSomethingはこのようになります:

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

delegatefunctionオブジェクトは混ざることができません。しかし標準関数 std.functional.toDelegatefunctiondelegateに変換します。

無名関数とラムダ

関数を変数として保持して他の関数に渡すことができるため、それらに独自の名前を与えて定義するのは面倒です。 従ってDは名無し関数と1行のラムダが使えます。

auto f = (int lhs, int rhs) {
    return lhs + rhs;
};
auto f = (int lhs, int rhs) => lhs + rhs; // ラムダ - 内部で上のように変換されます

テンプレート引数からDの標準関数の機能部品として文字列を渡すこともできます。 例えば畳み込み(folding。reducerとしても知られる)を定義する便利な方法を提供します:

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

文字列関数は1つまたは2つの引数のみが可能で、その時1番目にa、2番目の引数にbを使います。

掘り下げる

rdmd playground.d