我试图通过Y-combinator编写递归而不引用C ++中的函数名。但是,我无法在以下尝试中找出函数的类型:
#include <iostream>
using std::cin;
using std::cout;
template<class Function> unsigned long factorial1(Function self, unsigned long n) {
return n ? n * self(self, n - 1) : 1;
}
unsigned long factorial(unsigned long n) {
return factorial1(factorial1, n);
}
int main() {
unsigned long n;
cin >> n;
cout << factorial(n) << '\n';
return 0;
}
编译器无法推断出Function
是什么,我也不能。然后我尝试了以下内容:
#include <iostream>
using std::cin;
using std::cout;
struct Factorial {
template<class Function> unsigned long operator()(Function self, unsigned long n) const {
return n ? n * self(self, n - 1) : 1;
}
};
unsigned long factorial(unsigned long n) {
return Factorial()(Factorial(), n);
}
int main() {
unsigned long n;
cin >> n;
cout << factorial(n) << '\n';
return 0;
}
这与上面的例子相比,不同之处在于我将工作函数更改为可调用对象,Function
可以轻松推导为Factorial
,从而导致以下完整的组合实现:
#include <iostream>
using std::cin;
using std::cout;
struct Factorial {
template<class Function> unsigned long operator()(Function self, unsigned long n) const {
return n ? n * self(self, n - 1) : 1;
}
};
template<class Function> auto y(Function f) {
return [f](auto n) {
return f(f, n);
};
}
int main() {
unsigned long n;
cin >> n;
cout << y(Factorial())(n) << '\n';
return 0;
}
问题是,是否可以将结构Factorial
重写为普通函数?
答案 0 :(得分:2)
你做得有点错误:factorial1
的第一个参数应该是factorial1
的固定点,类型为unsigned long(*)(unsigned long)
,而不是factorial1
本身,所以没有需要提供self
作为论据:
unsigned long factorial1(unsigned long(*self)(unsigned long), unsigned long n) {
return n ? n * self(n - 1) : 1;
}
C ++不允许将闭包作为函数指针传递,因此我们必须:
将std::function
或其他包装传递给self
。没有意思的IMO。
使用template magic在编译时生成定点函数。
第二个选项可以轻松完成:
template<class X, X(*Fn)(X(*)(X), X)>
struct Fix {
static X Function(X x) {
return Fn(Fix<X, Fn>::Function, x);
}
};
现在,Fix<unsigned long, factorial1>::Function
为a fixed point of factorial1
:
unsigned long factorial(unsigned long n) {
return Fix<unsigned long, factorial1>::Function(n);
};
请注意,此Fix
实现仍然按名称引用自身,因此任何没有类型系统黑客的定点组合器的实现也是如此。
答案 1 :(得分:0)
您无法直接传递模板,但您可以在飞行中使用通用lambda,因此最终看起来就像使用模板一样:
#define PASS_FUNC(name) [dummy=nullptr](auto&&... args){return name(decltype(args)(args)...);}
template<class Function> unsigned long factorial1(Function self, unsigned long n) {
return n ? n * self(self, n - 1) : 1;
}
unsigned long factorial(unsigned long n) {
return factorial1(PASS_FUNC(factorial1), n);
}
但我认为这是一个黑客攻击,因为lambdas仍然是函数对象。
答案 2 :(得分:-1)
您描述的案例看起来像无限类型或递归类型。如果您尝试自己手动推断类型,您可以看到它是无限的,您可能也是自己发现的类型。为了表明这一点,我想将您的factorial1
函数简化为:
template <class T> void foobar(T self) {
self(self);
}
然后尝试使用函数指针而不是模板编写此函数,以手动推断其类型。
首先,我们希望foobar
将函数指针作为参数。
void foobar(void (*self)());
^^^^^^^^^^^^^^
但这仍然不是我们想要的,这个函数指针应该作为一个参数作为一个指向自己的指针。
void foobar(void (*self)(void (*)()));
^^^^^^^^^^
但是我们还没有完成,因为我们必须再次添加一个指向自身的指针
void foobar(void (*self)(void (*)(void (*)())));
^^^^^^^^^^
你可以看到模式如何继续下去。
void foobar(void (*self)(void (*)(void (*)(void (*)()))));
^^^^^^^^^^
void foobar(void (*self)(void (*)(void (*)(void (*)(void (*)())))));
^^^^^^^^^^
您给出的示例,您通过结构实现了这一点,只是通过operator()
来模仿它。如果您将该函数的名称更改为foobar
,它看起来像是:
struct Factorial {
template<class Function> unsigned long foobar(Function self, unsigned long n) const {
return n ? n * self.foobar(self, n - 1) : 1;
}
};
unsigned long factorial(unsigned long n) {
return Factorial().foobar(Factorial(), n);
}
所以你基本上在foobar
中递归调用foobar
,这与你的初始陈述相矛盾,你想在不知道/引用它的名字的情况下调用函数。