Một hàm có thể làm tham số cho hàm khác:
void doSomething(int function(int, int) doer) {
// gọi hàm cho bởi tham số
doer(5,5);
}
doSomething(&add); // dùng phép cộng `add`
Tham số doer
có thể được gọi giống như bất kỳ hàm nào khác.
Ví dụ trên dùng kiểu function
là kiểu con trỏ, trỏ đến hàm toàn cục.
Khi một hàm thành phần (của một lớp), hoặc hàm địa phương được tham khảo,
ủy nhiệm hàm (delegate
) sẽ được dùng đến. Ủy nhiệm hàm cũng là con trỏ
hàm, nhưng có thêm thông tin về ngữ cảnh được gọi (enclosure, hay còn
gọi là closure trong nhiều ngôn ngữ khác).
Ví dụ, ủy hiệm hàm thành phần của một lớp (class) chứa thêm con trỏ đến
đối tượng của lớp. Tuy nhiên, ủy nhiệm hàm tạo bởi hàm lồng nhau chỉ chứa
liên kết đến các ngữ cảnh bao bên ngoài, và trình biên dịch D có thể
tự động sao chép ngữ cảnh trên vùng heap
khi cần thiết,
sau đó một ủy nhiệm hàm sẽ liên kết tới vùng heap
đó.
void foo() {
void local() {
writeln("local");
}
auto f = &local; // f là ủy nhiệm hàm
}
Quay lại ví dụ đầu tiên, hàm doSomething
có thể nhận tham số là ủy nhiệm hàm
như sau :
void doSomething(int delegate(int,int) doer);
Ủy nhiệm hàm (delegate
) không thể lẫn lộn với hàm (function
).
Tuy nhiên, nhờ hàm tiêu chuẩn
std.functional.toDelegate
bạn có thể chuyển một hàm function
thành một ủy nhiệm delegate
.
Bởi hàm có thể dùng như biến để truyền tham số cho hàm khác, nên nhiều lúc không cần thiết đặt tên cho hàm đó (như kiểu định nghĩa hàm thông thường). D cho phép bạn định nghĩa các hàm không tên, và các hàm một dòng lambda:
auto f = (int lhs, int rhs) {
return lhs + rhs;
};
auto f = (int lhs, int rhs) => lhs + rhs; // Lambda
Trong D có thể dùng chuỗi như là mẫu tham số hàm truyền cho các hàm
trong thư viện tiêu chuẩn. Ví dụ, phép gộp reducer
(hay folding
)
có thể viết ngắn gọn như sau:
[1, 2, 3].reduce!`a + b`; // 6
Tuy nhiên, cách tương tự chỉ áp dụng cho hàm có một hoặc hai tham số,
trong đó luôn luôn a
chỉ tham số thứ nhất còn b
chỉ tham số thứ hai.