我正在尝试更好地学习Y-combinator(我在中理解它在Scheme中)并在D 2.0中实现它,而且我很失败:
auto fact = delegate(uint delegate(uint) recurse)
{
return delegate(uint n)
{
return n > 1 ? n * recurse(n - 1) : 1;
};
};
fact(fact)(5);
这不起作用,因为显而易见的原因是我无法将fact
传递给fact
(它的类型是什么?)。此外,我仍然需要fact
的名字传递给自己,所以无论如何它都不会起作用,对吧?
但是......我如何在D中实现Y-combinator?
答案 0 :(得分:7)
查看详尽的解释here。
答案 1 :(得分:4)
D(和C / C ++)的一个已知限制是,处理类型化自引用的函数是不可能的(我上次检查)要声明。
我觉得这很具有讽刺意味,因为它等于语法的限制(函数原型的长度是无限的)或命名约定(同样的问题,但名称是错误的)而不是任何语义或架构。
答案 2 :(得分:3)
我不知道D,但是对于大多数语言,当你尝试实现非递归时,你会遇到函数类型的问题(你的代码中还没有Y-Combinator)。通常的方法是通过将类型分成几个部分来实现,这样就可以将递归转换为类型。
其他语言的一些例子:
在C中,通常会编写一个包含函数指针的结构。然后可以将此结构用作参数。
在OCaml中,会添加一个包含函数类型的变体类型。同样,这允许分离类型,因此可以进行类型递归。
如果您想要其他语言的示例,请查看this page。
答案 3 :(得分:3)
我在D中的通用Y组合器:
import std.stdio, std.algorithm, std.range;
auto Y(R,T...)(R delegate(T) delegate (R delegate(T)) f){
struct F{R delegate(F,T) f;};
return (R delegate(T)delegate(F) a){return a(F((F b,T i){return f(a(b))(i);}));
}((F b){return (T n){return b.f(b,n);};});
}
void main(){
auto factorial=Y((long delegate(long) self){return (long i){return i?self(i-1)*i:1;};});
auto fibonacci=Y((int delegate(int) self){return (int i){return i<2?i:self(i-1)+self(i-2);};});
auto ackermann=Y((long delegate(long,long) self){return (long n,long m){return n?m?self(n-1,self(n,m-1)):self(n-1,1):m+1;};});
writeln(map!factorial(iota(0,20)));
writeln(map!fibonacci(iota(0,20)));
writeln(map!((a){return ackermann(a%4,a/4);})(iota(0,20)));
}
我从一个简单的阶乘函数的递归定义开始,并修改它直到我的代码看起来像这样。无限类型问题正在使用类型包装器(struct F)。