1-基础入门玩法
auto fib(auto n){ if (n == 0) return 0; if (n == 1) return 1; return fib(n-1) + fib(n-2); }
2-metaprogramming,编译期间展开
template<auto N> struct FibT; template<> struct FibT<0>{ static constexpr int result = 0; }; template<> struct FibT<1>{ static constexpr int result = 1; }; template<auto N> struct FibT{ static constexpr int result = FibT<N-1>::result + FibT<N-2>::result; }; template<auto N> inline constexpr auto fib_v = FibT<N>::result;
std::cout << fib_v<3> << std::endl;
3,metaprogramming,同样编译期间计算展开
template<auto N> struct FibT{ static auto constexpr compute(){ auto result = 0; if constexpr (N==0) result = 0; else if constexpr (N==1) result = 1; else result = FibT<N-1>::compute() + FibT<N-2>::compute(); return result; } }; template<auto N> inline constexpr auto fib_v = FibT<N>::compute();
std::cout << fib_v<3> << std::endl;
4,运行期间展开的有个问题,由于是递归,会再递归的 node上重复计算
比如fib(5),第一次递归,fib(4),fib(3),而计算fib(4)也计算了fib(3)
#include <iostream> #include <vector> static std::vector<int > cache{0,1}; auto fib(auto n){ if(cache.size() > n){ return cache[n]; } else{ auto var = fib(n-1) + fib(n-2); cache.emplace_back(var); return var; } } int main() { std::cout << fib(5) << std::endl; std::cout << fib(4) << std::endl;// cached std::cout << "container:" << std::endl; for(auto &&v : cache){ std::cout << v << " " ; } std::cout << std::endl; return 0; }
输出:
5 3 container: 0 1 1 2 3 5
5,对任意函数的结果cache
template<typename ResultT, typename ... Args> auto make_memoized(ResultT (*func)(Args...)) { using TupleArgsT = std::tuple< Args...>; std::map< TupleArgsT, ResultT> cache;// unordered map can not use in this place return [func, cache](Args... args) mutable -> ResultT { auto args_tuple= std::make_tuple( args...); auto iter = cache.find(args_tuple); if(iter == cache.end()){ auto result = func( args...); cache[args_tuple] = result; return result; } else{ return cache[args_tuple]; } return ResultT(); }; }
调用方法:
auto fib_mem = make_memoized(fib);
fib_mem(fib_n);
fib_mem(fib_n) ;
fib_mem(fib_n) ;
第一次会慢点,但是第二次调用会非常快,但是这个也有缺陷,
— 函数结果被cache,函数本身的过程没cache
— 适合调用相同的函数多次
对 N=40 性能的测试:
#include <iostream> #include <unordered_map> #include <tuple> #include <functional> #include <map> #include <chrono> class Timer{ private: std::chrono::steady_clock::time_point last; public: Timer(): last{std::chrono::steady_clock::now()}{ } void reset(){ last = std::chrono::steady_clock::now(); } void printDiff(const std::string &msg = "time diff: "){ auto now{std::chrono::steady_clock::now()}; std::chrono::duration<double, std::milli> diff{now-last}; std::cout << msg << diff.count() << std::endl; last = std::chrono::steady_clock::now(); } }; int fib(int n){ if(n==0 || n==1) return n; else return fib(n-1)+fib(n-2); } template<typename ResultT, typename ... Args> auto make_memoized(ResultT (*func)(Args...)) { using TupleArgsT = std::tuple< Args...>; std::map< TupleArgsT, ResultT> cache;// unordered map can not use in this place return [func, cache](Args... args) mutable -> ResultT { auto args_tuple= std::make_tuple( args...); auto iter = cache.find(args_tuple); if(iter == cache.end()){ auto result = func( args...); cache[args_tuple] = result; return result; } else{ return cache[args_tuple]; } return ResultT(); }; } template<auto N> struct FibT; template<> struct FibT<0>{ static constexpr int result = 0; }; template<> struct FibT<1>{ static constexpr int result = 1; }; template<auto N> struct FibT{ static constexpr int result = FibT<N-1>::result + FibT<N-2>::result; }; template<auto N> inline constexpr auto fib_v = FibT<N>::result; int main() { constexpr auto fib_n = 40; Timer t; fib(fib_n) ; fib(fib_n) ; fib(fib_n) ; t.printDiff("fib() time: "); t.reset(); auto fib_mem = make_memoized(fib); fib_mem(fib_n); fib_mem(fib_n) ; fib_mem(fib_n) ; t.printDiff("fib_mem() time: "); t.reset(); fib_v<fib_n>; fib_v<fib_n>; fib_v<fib_n>; t.printDiff("metaprogramming time: "); t.reset(); return 0; }
View Code
上面代码是对每种函数调用3次结果:
fib() time: 3202.85
fib_mem() time: 1066.87
metaprogramming time: 0.000604
如果调用一次:
fib() time: 1056.4
fib_mem() time: 1056.15
metaprogramming time: 0.000681
编译器的递归也有限制,比如将fib_n = 100时候:
Scanning dependencies of target fib_mem [ 50%] Building CXX object CMakeFiles/fib_mem.dir/main.cpp.o /home/father/dev/cpp/fib_mem/main.cpp: In instantiation of ‘constexpr const int FibT<47>::result’: /home/father/dev/cpp/fib_mem/main.cpp:76:46: recursively required from ‘constexpr const int FibT<99>::result’ /home/father/dev/cpp/fib_mem/main.cpp:76:46: required from ‘constexpr const int FibT<100>::result’ /home/father/dev/cpp/fib_mem/main.cpp:79:40: required from ‘constexpr const auto fib_v<100>’ /home/father/dev/cpp/fib_mem/main.cpp:101:5: required from here /home/father/dev/cpp/fib_mem/main.cpp:76:53: warning: integer overflow in expression of type ‘int’ results in ‘-1323752223’ [-Woverflow] 76 | static constexpr int result = FibT<N-1>::result + FibT<N-2>::result; | ~~~~~~~^~~~~~~~~~~~~~~~~~~ /home/father/dev/cpp/fib_mem/main.cpp:76:26: error: overflow in constant expression [-fpermissive] 76 | static constexpr int result = FibT<N-1>::result + FibT<N-2>::result; | ^~~~~~ /home/father/dev/cpp/fib_mem/main.cpp:76:26: error: overflow in constant expression [-fpermissive] make[2]: *** [CMakeFiles/fib_mem.dir/build.make:72: CMakeFiles/fib_mem.dir/main.cpp.o] Error 1 make[1]: *** [CMakeFiles/Makefile2:83: CMakeFiles/fib_mem.dir/all] Error 2 make: *** [Makefile:91: all] Error 2
REF:
Functional Programming In C++ — Ivan