C++ で std::function を使わない不動点コンビネータ
どうも不動点コンビネータとYコンビネータの違いがよくわかってないマンです。
C++ で不動点コンビネータを調べるといくつか実装があったんですが id:iorate さんの実装が一番すっきりしていたのでを元にして、std::function を使わないようにしてみました。
まぁ汎用性はないんですが。
[ソース]
#include <iostream> #include <type_traits> template<typename Func, typename T, typename Result, typename ...Args> typename std::common_type<Result(*)(Args...)>::type lambda_to_function(Result(T::*)(Args...) const, Func func){ return func; } template<typename Func> auto lambda_to_function(Func func) ->decltype(lambda_to_function(&decltype(func)::operator(), func)){ return lambda_to_function(&decltype(func)::operator(), func); } // std::function を使わない版 template<typename Sig> struct self; template<typename Result, typename ...Args> struct self<Result(Args...)>{ using func_type = Result(*)(self, Args...); Result operator ()(Args... args){ return func(*this, args...); } func_type func; }; template<typename Result, typename ...Args> self<Result(Args...)> Y_impl(Result(*func)(self<Result(Args...)>, Args...)){ return { func }; } template<typename F> auto Y(F func) ->decltype(Y_impl(lambda_to_function(func))){ return Y_impl(lambda_to_function(func)); } // オリジナル版 #include <functional> #include <boost/progress.hpp> template <class F> struct fix_result { F m_f; template <class ...Args> auto operator()(Args &&...args) const -> decltype(m_f(*std::declval<fix_result const *>(), std::declval<Args>()...)) { return m_f(*this, std::forward<Args>(args)...); } }; template <class F> inline fix_result<typename std::decay<F>::type> fix(F &&f) { return { std::forward<F>(f) }; } int main(){ auto f = Y([](self<int(int)> f, int n){ return n == 1 ? 1 : f(n-1) * n; }); auto n = f(6); std::cout << n << std::endl; std::cout << "-----------" << std::endl; { boost::progress_timer t; std::cout << Y([](self<long long(long long)> f, long long x){ return x <= 0 ? 0 : x + f(x - 1); })(1000000) << std::endl; } { boost::progress_timer t; std::cout << fix([](std::function<long long(long long)> f, long long x){ return x <= 0 ? 0 : x + f(x - 1); })(1000000) << std::endl; } return 0; }