D 中的柯里化函数模板?
Template for currying functions in D?
是否可以编写一个模板或类似的模板来自动柯里化 D 中的函数?手动写出所有嵌套委托让我很痛苦。
基本上,对于函数 f
例如3 个参数,通常可以像 f(a,b,c)
一样调用,我希望它可以像 f(a)(b)(c)
一样调用。
我知道 std.functional.partial,但这不是我想要的。我想翻译函数定义端,而不是调用端。
我也知道这远非最佳实践,但我正在生成代码,所以请耐心等待。
嗯,按照这些思路应该可以完成工作:
template autocurry(alias what) {
import std.traits;
static if(Parameters!what.length)
auto autocurry(Parameters!what[0] arg) {
alias Remainder = Parameters!what[1 .. $];
auto dg = delegate(Remainder args) {
return what(arg, args);
};
static if(Remainder.length > 1)
return &autocurry!dg;
else
return dg;
}
else
alias autocurry = what;
}
int foo(int a, string b, float c) {
import std.stdio; writeln(a, " ", b, " ", c);
return 42;
}
string test() {
import std.stdio; writeln("called test");
return "no args";
}
void main() {
import std.stdio;
alias lol = autocurry!foo;
writeln(lol(30)("lol")(5.3));
auto partial = lol(20);
partial("wtf")(10.5);
alias t = autocurry!test;
writeln(t());
}
这个想法很简单:根据剩余的参数生成辅助函数 - 如果有的话,return 辅助函数的地址作为委托,否则,只是 return委托调用收集的参数。一点递归处理 1+ arg 的情况,而外部的静态 if 仅通过 returning 原始函数来处理 0 arg 的情况。
需要注意的语言特点:
同名模板。当模板有与模板同名的成员时(本例为autocurry),使用时自动引用
元组扩展。当我调用 what(arg, args)
时,作为内置元组的 args
会自动展开以创建完整的参数列表。
此处的各种自动 return(显式 auto autocurry
和隐式 delegate
关键字,未指定 return 类型)只是转发任何内容其他随机类型正文恰好return.
在main函数中,我做了alias lol = autocurry!foo;
(我经常使用lol作为我的占位符名称,lol)。您也可以在顶层重载它:
int foo(int a, string b, float c) {
import std.stdio; writeln(a, " ", b, " ", c);
return 42;
}
alias foo = autocurry!foo; // overloads the auto-curried foo with the original foo
现在您可以直接使用它,与原来的一起使用:
void main() {
foo(30)("lol")(5.3); // overload resolves to curried version
foo(40, "cool", 103.4); // overload resolves to original automatically
}
如果您更喜欢一个新名称,或者重载由您决定,两者都可以。
请注意,每个参数都可能分配一些内存来为下一个委托存储它。 GC 将负责清理它。
是否可以编写一个模板或类似的模板来自动柯里化 D 中的函数?手动写出所有嵌套委托让我很痛苦。
基本上,对于函数 f
例如3 个参数,通常可以像 f(a,b,c)
一样调用,我希望它可以像 f(a)(b)(c)
一样调用。
我知道 std.functional.partial,但这不是我想要的。我想翻译函数定义端,而不是调用端。
我也知道这远非最佳实践,但我正在生成代码,所以请耐心等待。
嗯,按照这些思路应该可以完成工作:
template autocurry(alias what) {
import std.traits;
static if(Parameters!what.length)
auto autocurry(Parameters!what[0] arg) {
alias Remainder = Parameters!what[1 .. $];
auto dg = delegate(Remainder args) {
return what(arg, args);
};
static if(Remainder.length > 1)
return &autocurry!dg;
else
return dg;
}
else
alias autocurry = what;
}
int foo(int a, string b, float c) {
import std.stdio; writeln(a, " ", b, " ", c);
return 42;
}
string test() {
import std.stdio; writeln("called test");
return "no args";
}
void main() {
import std.stdio;
alias lol = autocurry!foo;
writeln(lol(30)("lol")(5.3));
auto partial = lol(20);
partial("wtf")(10.5);
alias t = autocurry!test;
writeln(t());
}
这个想法很简单:根据剩余的参数生成辅助函数 - 如果有的话,return 辅助函数的地址作为委托,否则,只是 return委托调用收集的参数。一点递归处理 1+ arg 的情况,而外部的静态 if 仅通过 returning 原始函数来处理 0 arg 的情况。
需要注意的语言特点:
同名模板。当模板有与模板同名的成员时(本例为autocurry),使用时自动引用
元组扩展。当我调用
what(arg, args)
时,作为内置元组的args
会自动展开以创建完整的参数列表。此处的各种自动 return(显式
auto autocurry
和隐式delegate
关键字,未指定 return 类型)只是转发任何内容其他随机类型正文恰好return.在main函数中,我做了
alias lol = autocurry!foo;
(我经常使用lol作为我的占位符名称,lol)。您也可以在顶层重载它:
int foo(int a, string b, float c) {
import std.stdio; writeln(a, " ", b, " ", c);
return 42;
}
alias foo = autocurry!foo; // overloads the auto-curried foo with the original foo
现在您可以直接使用它,与原来的一起使用:
void main() {
foo(30)("lol")(5.3); // overload resolves to curried version
foo(40, "cool", 103.4); // overload resolves to original automatically
}
如果您更喜欢一个新名称,或者重载由您决定,两者都可以。
请注意,每个参数都可能分配一些内存来为下一个委托存储它。 GC 将负责清理它。