Boost.MPLでエラトステネスの篩を実装してみた
『C++テンプレートメタプログラミング』を読み始めたので、Boost.MPLでエラトステネスの篩を実装してみました。
コード
Pythonで書くところの
def sieve(ns): return [ns[0]] + sieve([n for n in ns if n % ns[0]]) if ns else []
を、そのまま(?)C++のコードに落とします。
#include <iostream> #include <boost/mpl/copy_if.hpp> #include <boost/mpl/empty.hpp> #include <boost/mpl/for_each.hpp> #include <boost/mpl/front.hpp> #include <boost/mpl/front_inserter.hpp> #include <boost/mpl/int.hpp> #include <boost/mpl/list.hpp> #include <boost/mpl/modulus.hpp> #include <boost/mpl/not_equal_to.hpp> #include <boost/mpl/placeholders.hpp> #include <boost/mpl/push_front.hpp> #include <boost/mpl/range_c.hpp> namespace mpl = boost::mpl; using namespace boost::mpl::placeholders; template<class Seq, bool Empty = mpl::empty<Seq>::value> struct sieve : mpl::push_front< typename sieve< typename mpl::reverse_copy_if< Seq, mpl::not_equal_to< mpl::modulus<_, typename mpl::front<Seq>::type>, mpl::int_<0> >, mpl::front_inserter<mpl::list<> > >::type >::type, typename mpl::front<Seq>::type > {}; template<class Seq> struct sieve<Seq, true> : mpl::list<> {}; typedef sieve<mpl::range_c<int, 2, 100> >::type primes; struct value_printer { template<class T> void operator()(T x) const { std::cout << x << ' '; } }; int main() { mpl::for_each<primes>(value_printer()); }
出力
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
VC10+Boost1.45.0とideone(GCC4.3.4+Boost 1.39.0)で動作確認。
ちょっと解説
テンプレートクラスsieveが整数列を篩にかけるメタ関数です。
列が空であるかどうかによる分岐は、bool型のテンプレート引数Emptyと部分的特殊化によって行います。2つのテンプレートの相互再帰で実装することもできますが(http://ideone.com/yud4V)、その場合は再帰の深さが倍になります。
reverse_copy_ifをfilter_viewに変えれば、Haskell的な遅延評価版もできるのですが、コンパイルが耐えられないくらい遅いのでやめました。