関数は他の関数の引数にもなれます:
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);
delegate
とfunction
オブジェクトは混ざることができません。しかし標準関数
std.functional.toDelegate
はfunction
をdelegate
に変換します。
関数を変数として保持して他の関数に渡すことができるため、それらに独自の名前を与えて定義するのは面倒です。 従って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
を使います。