Skip to content

Instantly share code, notes, and snippets.

@gintenlabo
Created November 5, 2010 16:43
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gintenlabo/664418 to your computer and use it in GitHub Desktop.
Save gintenlabo/664418 to your computer and use it in GitHub Desktop.
C++0x から <algorithm> に追加される関数の C++03 による実装
//
// C++0x から <algorithm> に追加される関数を C++03 で使えるよう実装したヘッダ
// やっつけテストも記述しました。
//
#ifndef ETUDE_INCLUDED_ALGORITHM_HPP_
#define ETUDE_INCLUDED_ALGORITHM_HPP_
#include <algorithm>
#include <iterator>
#include <utility>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/algorithm/minmax_element.hpp>
#include "predicates.hpp"
#include "find_nth.hpp"
namespace etude {
// Non-modifying sequence operations
template <class InputIter, class Pred>
inline bool all_of( InputIter first, InputIter last, Pred pred ) {
for( ; first != last; ++first ) {
if( !pred(*first) ){ return false; }
}
return true;
}
template <class InputIter, class Pred>
inline bool any_of( InputIter first, InputIter last, Pred pred ) {
return ! ::etude::all_of( first, last, ::etude::not1(pred) );
}
template <class InputIter, class Pred>
inline bool none_of( InputIter first, InputIter last, Pred pred ) {
return ::etude::all_of( first, last, ::etude::not1(pred) );
}
using std::for_each;
using std::find;
using std::find_if;
template <class InputIter, class Pred>
inline InputIter find_if_not( InputIter first, InputIter last, Pred pred ) {
return std::find_if( first, last, ::etude::not1(pred) );
}
using std::find_end;
using std::find_first_of;
using std::adjacent_find;
using std::count;
using std::count_if;
using std::mismatch;
using std::equal;
// thanks to @pepshiso, http://twitter.com/pepshiso/status/800678804983808
template< class FwdIter1, class FwdIter2, class Comp >
inline bool is_permutation( FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, Comp comp )
{
if( first1 == last1 ){ return true; }
while( comp( *first1, *first2 ) ) {
++first1; ++first2;
if( first1 == last1 ) {
return true;
}
}
FwdIter2 last2 = first2;
std::advance( last2, std::distance( first1, last1 ) );
for( ; first1 != last1; ++first1 ) {
if( ::etude::find_nth( first2, last2, std::count( first1, last1, *first1 ), *first1, comp ) == last2 ) {
return false;
}
}
return true;
}
template< class FwdIter1, class FwdIter2 >
inline bool is_permutation( FwdIter1 first1, FwdIter1 last1, FwdIter2 first2 )
{
return ::etude::is_permutation( first1, last1, first2, ::etude::equal_to<>() );
}
using std::search;
using std::search_n;
// Mutating sequence operations
using std::copy;
template<class InputIter, class Size, class OutputIter>
inline OutputIter copy_n( InputIter first, Size n, OutputIter result ) {
for( ; n > 0; --n ) {
*result = *first;
++result; ++first;
}
return result;
}
template<class InputIter, class OutputIter, class Pred>
inline OutputIter copy_if( InputIter first, InputIter last, OutputIter result, Pred pred ) {
return std::remove_copy_if( first, last, result, ::etude::not1(pred) );
}
using std::copy_backward;
// move, move_backward は未実装( C++03 では無理な為)
using std::swap_ranges;
using std::iter_swap;
using std::transform;
using std::replace;
using std::replace_if;
using std::replace_copy;
using std::replace_copy_if;
using std::fill;
using std::fill_n;
using std::generate;
using std::generate_n;
using std::remove;
using std::remove_if;
using std::remove_copy;
using std::remove_copy_if;
using std::unique;
using std::unique_copy;
using std::reverse;
using std::reverse_copy;
using std::rotate;
using std::rotate_copy;
using std::random_shuffle;
// shuffle はこのヘッダでは実装しない
// <boost/random.hpp> が必要になるため
template<class InputIter, class Pred>
inline bool is_partitioned( InputIter first, InputIter last, Pred pred ) {
if( first == last ) { return true; }
while( pred(*first) ) {
++first;
if( first == last ){ return true; }
}
return ::etude::none_of( ++first, last, pred );
}
using std::partition;
using std::stable_partition;
template <class InputIter, class OutputIter1, class OutputIter2, class Pred>
inline std::pair<OutputIter1, OutputIter2> partition_copy (
InputIter first, InputIter last,
OutputIter1 out_true, OutputIter2 out_false,
Pred pred
){
for( ; first != last; ++first ) {
if( pred(*first) ) {
*out_true = *first;
++out_true;
}
else {
*out_false = *first;
++out_false;
}
}
return std::make_pair( out_true, out_false );
}
template<class FwdIter, class Pred>
inline FwdIter partition_point( FwdIter first, FwdIter last, Pred pred ) {
typedef typename std::iterator_traits<FwdIter>::difference_type diff_type;
diff_type n = std::distance( first, last );
FwdIter mid = first;
while( n > 0 ) {
BOOST_ASSERT( mid == first );
diff_type const half = n / 2;
std::advance( mid, half );
if( pred(*mid) ) {
first = ++mid;
n = n - half - 1;
}
else {
n = half;
mid = first;
}
}
return first;
}
// Sorting and related operations
using std::sort;
using std::stable_sort;
using std::partial_sort;
using std::partial_sort_copy;
template<class FwdIter, class Comp>
inline FwdIter is_sorted_until( FwdIter first, FwdIter last, Comp comp ) {
if( first == last ){ return last; }
FwdIter next = first;
for( ++next; next != last; first = next, ++next ) {
if( comp( *next, *first ) ) {
return next;
}
}
return next;
}
template<class FwdIter>
inline FwdIter is_sorted_until( FwdIter first, FwdIter last ) {
return ::etude::is_sorted_until( first, last, ::etude::less<>() );
}
template<class FwdIter>
inline bool is_sorted( FwdIter first, FwdIter last ) {
return ::etude::is_sorted_until( first, last ) == last;
}
template<class FwdIter, class Comp>
inline bool is_sorted( FwdIter first, FwdIter last, Comp comp ) {
return ::etude::is_sorted_until( first, last, comp ) == last;
}
using std::nth_element;
using std::lower_bound;
using std::upper_bound;
using std::equal_range;
using std::binary_search;
using std::merge;
using std::inplace_merge;
using std::includes;
using std::set_union;
using std::set_intersection;
using std::set_difference;
using std::set_symmetric_difference;
// min/max はマクロによる事故を避けるために Boost のマクロで using する
// using std::min; using std::max;
BOOST_USING_STD_MIN(); BOOST_USING_STD_MAX();
// minmax は boost 版は使わない( std::pair を返したいので)
// コンパイラによっては参照の参照問題でコンパイルが通らない可能性がある。
// その場合は using boost::minmax; に書き換える。
template<class T>
inline std::pair<T const&, T const&> minmax( T const& a, T const& b ) {
typedef std::pair<T const&, T const&> result_type;
return b < a ? result_type(b, a) : result_type(a, b);
}
template<class T, class Comp>
inline std::pair<T const&, T const&> minmax( T const& a, T const& b, Comp comp ) {
typedef std::pair<T const&, T const&> result_type;
return comp(b, a) ? result_type(b, a) : result_type(a, b);
}
using std::min_element;
using std::max_element;
using boost::minmax_element;
using std::lexicographical_compare;
using std::next_permutation;
using std::prev_permutation;
} // namespace etude
#endif // #ifndef ETUDE_INCLUDED_ALGORITHM_HPP_
#include "algorithm.hpp"
#include <boost/test/minimal.hpp>
#include <boost/lambda/lambda.hpp>
namespace bll = boost::lambda;
#include <vector>
#include <deque>
#include <iterator>
int test_main( int, char** )
{
int const a[] = {
1, 9, 8, 5, 0, 6, 1, 7
};
std::size_t const n = sizeof(a) / sizeof(a[0]);
// all_of, any_of, none_of
BOOST_CHECK( etude:: all_of( a, a+n, etude::less_than(10) ) );
BOOST_CHECK( etude:: any_of( a, a+n, etude::less_than(5) ) );
BOOST_CHECK( etude::none_of( a, a+n, etude::less_than(0) ) );
BOOST_CHECK( !etude:: all_of( a, a+n, etude::less_than(5) ) );
BOOST_CHECK( !etude:: any_of( a, a+n, etude::less_than(0) ) );
BOOST_CHECK( !etude::none_of( a, a+n, etude::less_than(1) ) );
// find_if_not
BOOST_CHECK( etude::find_if_not( a, a+n, bll::_1 % 2 == 1 ) == a + 2 ); // 8
// is_permutation
{
int b[n];
std::copy( a, a+n, b );
std::random_shuffle( b, b+n );
BOOST_CHECK( etude::is_permutation( a, a+n, b ) );
b[0] = 10;
std::random_shuffle( b, b+n );
BOOST_CHECK( !etude::is_permutation( a, a+n, b ) );
}
// copy_n
{
std::vector<int> v;
etude::copy_n( a, 4, std::back_inserter(v) );
BOOST_CHECK( v.size() == 4 );
BOOST_CHECK( std::equal( a, a+4, v.begin() ) );
}
// copy_if
{
std::vector<int> v;
etude::copy_if( a, a+n, std::back_inserter(v), etude::less_than(5) );
BOOST_CHECK( v.size() == 3 );
BOOST_CHECK( v[0] == 1 );
BOOST_CHECK( v[1] == 0 );
BOOST_CHECK( v[2] == 1 );
}
// is_partitioned
BOOST_CHECK( !etude::is_partitioned( a, a+n, etude::less_than(5) ) );
BOOST_CHECK( etude::is_partitioned( a, a+n, etude::not1( etude::equals_to(7) ) ) );
BOOST_CHECK( etude::is_partitioned( a, a+n, etude::greater_than(10) ) ); // none
BOOST_CHECK( etude::is_partitioned( a, a+n, bll::_1 >= 0 ) ); // all
// patition_copy && partition_point
{
std::deque<int> x;
etude::partition_copy( a, a+n, std::front_inserter(x), std::back_inserter(x),
bll::_1 % 2 == 0 ); // 偶数を先頭に、奇数を後方に。
BOOST_CHECK( x.size() == n );
BOOST_CHECK( x[0] == 6 );
BOOST_CHECK( x[1] == 0 );
BOOST_CHECK( x[2] == 8 );
BOOST_CHECK( x[3] == 1 );
BOOST_CHECK( x[4] == 9 );
BOOST_CHECK( x[5] == 5 );
BOOST_CHECK( x[6] == 1 );
BOOST_CHECK( x[7] == 7 );
BOOST_CHECK( etude::partition_point( x.begin(), x.end(), bll::_1 % 2 == 0 )
== x.begin() + 3 ); // x[2] は偶数、 x[3] は奇数。
}
// is_sorted
{
std::vector<int> v( a, a+n );
std::sort( v.begin(), v.end() );
// is_sorted
BOOST_CHECK( !etude::is_sorted( a, a+n ) );
BOOST_CHECK( etude::is_sorted( v.begin(), v.end() ) );
BOOST_CHECK( !etude::is_sorted( v.begin(), v.end(), std::greater<int>() ) );
BOOST_CHECK( etude::is_sorted( v.rbegin(), v.rend(), std::greater<int>() ) );
// is_sorted_until
BOOST_CHECK( etude::is_sorted_until( a, a+n ) == a + 2 ); // 1, 9 まで
BOOST_CHECK( etude::is_sorted_until( a, a+n, std::greater<int>() ) == a + 1 ); // 1 まで
BOOST_CHECK( etude::is_sorted_until( v.begin(), v.end() ) == v.end() ); // 全部
BOOST_CHECK( etude::is_sorted_until( v.rbegin(), v.rend(), std::greater<int>() ) == v.rend() ); // 全部
}
// minmax
{
std::pair<int, int> p = etude::minmax( 1, 2 );
BOOST_CHECK( p.first == 1 && p.second == 2 );
p = etude::minmax( 2, 1 );
BOOST_CHECK( p.first == 1 && p.second == 2 );
p = etude::minmax( 23, 42, std::greater<int>() );
BOOST_CHECK( p.first == 42 && p.second == 23 );
p = etude::minmax( 42, 23, std::greater<int>() );
BOOST_CHECK( p.first == 42 && p.second == 23 );
}
return 0;
}
//
// 困ったちゃん binder 兄弟を boost::result_of を使って少しはマシにしよう計画
//
#ifndef ETUDE_INCLUDED_BINDERS_HPP_
#define ETUDE_INCLUDED_BINDERS_HPP_
#include <boost/compressed_pair.hpp>
#include <boost/utility/result_of.hpp>
#include <boost/type_traits/add_reference.hpp>
namespace etude {
// 困ったちゃん1号
template<class Fn, class T = typename Fn::first_argument_type>
class binder1st
: boost::compressed_pair<Fn, T>
{
typedef boost::compressed_pair<Fn, T> base;
using base::first; using base::second;
typedef typename boost::add_reference<T const>::type param_type;
public:
binder1st( Fn f, param_type x )
: base( f, x ) {}
template<class Signature>
struct result;
template<class F_, class U>
struct result<F_(U)>
: boost::result_of<Fn(T, U)> {};
template<class U>
typename boost::result_of<Fn(T, U)>::type operator()( U const& y ) {
return first()( second(), y );
}
template<class U>
typename boost::result_of<Fn(T, U)>::type operator()( U const& y ) const {
return first()( second(), y );
}
};
// 困ったちゃん2号
template<class Fn, class U = typename Fn::second_argument_type>
class binder2nd
: boost::compressed_pair<Fn, U>
{
typedef boost::compressed_pair<Fn, U> base;
using base::first; using base::second;
typedef typename boost::add_reference<U const>::type param_type;
public:
binder2nd( Fn f, param_type y )
: base( f, y ) {}
template<class Signature>
struct result;
template<class F_, class T>
struct result<F_(T)>
: boost::result_of<Fn(T, U)> {};
template<class T>
typename boost::result_of<Fn(T, U)>::type operator()( T const& x ) {
return first()( x, second() );
}
template<class T>
typename boost::result_of<Fn(T, U)>::type operator()( T const& x ) const {
return first()( x, second() );
}
};
// ヘルパ関数
// T, U が先なのは、参照キャプチャしたい時に
// bind1st<hoge&>( f, x ) って書けるようにするため。
template<class T, class Fn>
inline binder1st<Fn, T> bind1st( Fn f, T x ) {
return binder1st<Fn, T>( f, x );
}
template<class U, class Fn>
inline binder2nd<Fn, U> bind2nd( Fn f, U y ) {
return binder2nd<Fn, U>( f, y );
}
} // namespace etude
#endif // #ifndef ETUDE_INCLUDED_BINDERS_HPP_
//
// EBO 用に組み込み型をクラス化するクラス
// 使い方: private 継承。 値を得たいときは get を呼ぶ。
// 実装例は predicates.hpp の (unary|binary)_negate を参照。
//
#ifndef ETUDE_INCLUDED_CLASS_WRAPPER_HPP_
#define ETUDE_INCLUDED_CLASS_WRAPPER_HPP_
#include <boost/config.hpp>
#include <boost/type_traits/add_reference.hpp>
#include <boost/type_traits/is_empty.hpp>
#include <boost/type_traits/remove_cv.hpp>
namespace etude {
namespace class_wrapper_ {
namespace detail_ {
template<class T, bool isEmpty = boost::is_empty<T>::value >
class class_wrapper_impl_;
template<class T>
class class_wrapper_impl_<T, true>
: boost::remove_cv<T>::type
{
typedef typename boost::remove_cv<T>::type base;
public:
typedef typename boost::add_reference<T>::type reference;
typedef typename boost::add_reference<T const>::type const_reference;
class_wrapper_impl_()
: base() {}
class_wrapper_impl_( const_reference x )
: base(x) {}
#ifdef BOOST_HAS_RVALUE_REFS
class_wrapper_impl_( T&& x )
: base( static_cast<T&&>(x) ) {}
#endif
reference get() {
return *this;
}
const_reference get() const {
return *this;
}
};
template<class T>
class class_wrapper_impl_<T, false>
{
T data;
public:
typedef typename boost::add_reference<T>::type reference;
typedef typename boost::add_reference<T const>::type const_reference;
class_wrapper_impl_()
: data() {}
class_wrapper_impl_( const_reference x )
: data(x) {}
#ifdef BOOST_HAS_RVALUE_REFS
class_wrapper_impl_( T&& x )
: data( static_cast<T&&>(x) ) {}
#endif
reference get() {
return data;
}
const_reference get() const {
return data;
}
};
} // namespace detail_
template<class T, class Tag = void>
class class_wrapper
: detail_::class_wrapper_impl_<T>
{
typedef detail_::class_wrapper_impl_<T> base;
public:
typedef typename base::reference reference;
typedef typename base::const_reference const_reference;
class_wrapper()
: base() {}
class_wrapper( const_reference x )
: base(x) {}
#ifdef BOOST_HAS_RVALUE_REFS
class_wrapper( T&& x )
: base( static_cast<T&&>(x) ) {}
#endif
using base::get;
};
}
using namespace class_wrapper_;
} // namespace etude
#endif // #ifndef ETUDE_INCLUDED_CLASS_WRAPPER_HPP_
//
// n 番目の要素を検索する
//
#ifndef ETUDE_INCLUDED_FIND_NTH_HPP_
#define ETUDE_INCLUDED_FIND_NTH_HPP_
#include <boost/assert.hpp>
#include "predicates.hpp"
namespace etude {
// Predicate を使って検索する
template<class FwdIter, class Int, class Pred>
inline FwdIter find_if_nth( FwdIter first, FwdIter last, Int n, Pred pred )
{
BOOST_ASSERT( n > 0 );
for( ; first != last; ++first ) {
if( pred(*first) ) {
if( --n == 0 ) {
return first;
}
}
}
return first;
}
// not 版
template<class FwdIter, class Int, class Pred>
inline FwdIter find_if_not_nth( FwdIter first, FwdIter last, Int n, Pred pred ) {
return ::etude::find_if_nth( first, last, n, ::etude::not1(pred) );
}
// 直接値で検索する
template<class FwdIter, class T, class Int>
inline FwdIter find_nth( FwdIter first, FwdIter last, Int n, T const& x ) {
return ::etude::find_if_nth( first, last, n, ::etude::equals_to(x) );
}
// 比較関数を与える
template<class FwdIter, class T, class Int, class Comp>
inline FwdIter find_nth( FwdIter first, FwdIter last, Int n, T const& x, Comp comp ) {
return ::etude::find_if_nth( first, last, n,
::etude::bind2nd<T const&>( ::etude::pred2(comp), x ) );
// bind(1st|2nd) のテンプレート引数によって引数型を明示できる。今回は参照渡し。
// pred2 を噛ませて bool を返すと明示することにより comp の条件を緩和する。
}
} // namespace etude
#endif // #ifndef ETUDE_INCLUDED_FIND_NTH_HPP_
//
// <functional> のうち、比較演算子と (unary|binary)_nagate を改良
// あと諸々の「条件判定で使うと便利そうなもの」を用意
//
#ifndef ETUDE_INCLUDED_PREDICATES_HPP_
#define ETUDE_INCLUDED_PREDICATES_HPP_
#include <functional>
#include "class_wrapper.hpp"
#include "binders.hpp"
namespace etude {
#define ETUDE_PREDICATES_GEN_( predicate, op ) \
template<class T = void> \
struct predicate \
: std::predicate<T> {}; \
\
template<> \
struct predicate<void> \
{ \
typedef bool result_type; \
\
template<class T, class U> \
bool operator()( T const& x, U const& y ) const { \
return x op y; \
} \
\
}; \
/* ETUDE_PREDICATES_GEN_ */
ETUDE_PREDICATES_GEN_( equal_to, == )
ETUDE_PREDICATES_GEN_( not_equal_to, != )
ETUDE_PREDICATES_GEN_( less, < )
ETUDE_PREDICATES_GEN_( less_equal, <= )
ETUDE_PREDICATES_GEN_( greater , > )
ETUDE_PREDICATES_GEN_( greater_equal, >= )
#undef ETUDE_PREDICATES_GEN_
// std::unary_negate は条件きついので改良版。
template<class Pred>
class unary_negate
: class_wrapper<Pred>
{
typedef class_wrapper<Pred> base;
using base::get;
public:
explicit unary_negate( Pred pred_ )
: base( pred_ ) {}
typedef bool result_type;
template<class T>
bool operator()( T const& x ) {
return !get()(x);
}
template<class T>
bool operator()( T const& x ) const {
return !get()(x);
}
};
// 補助関数
template<class Pred>
inline unary_negate<Pred> not1( Pred pred ) {
return unary_negate<Pred>( pred );
}
// 同じく std::binary_negate の改良版。
template<class Pred>
struct binary_negate
: class_wrapper<Pred>
{
typedef class_wrapper<Pred> base;
using base::get;
public:
explicit binary_negate( Pred pred_ )
: base( pred_ ) {}
typedef bool result_type;
template<class T, class U>
bool operator()( T const& x, U const& y ) {
return !get()( x, y );
}
template<class T, class U>
bool operator()( T const& x, U const& y ) const {
return !get()( x, y );
}
};
// 補助関数
template<class Pred>
inline binary_negate<Pred> not2( Pred pred ) {
return binary_negate<Pred>( pred );
}
// 渡された関数を「 bool を返す関数」にする
// result_type が定義されてないよ困った、って時に。
// 一引数版
template<class Pred>
struct unary_predicate
: class_wrapper<Pred>
{
typedef class_wrapper<Pred> base;
using base::get;
public:
explicit unary_predicate( Pred pred_ )
: base( pred_ ) {}
typedef bool result_type;
template<class T>
bool operator()( T const& x ) {
return bool( get()(x) );
}
template<class T>
bool operator()( T const& x ) const {
return bool( get()(x) );
}
};
// 補助関数
template<class Pred>
inline unary_predicate<Pred> pred1( Pred pred ) {
return unary_predicate<Pred>( pred );
}
// 二引数版
template<class Pred>
struct binary_predicate
: class_wrapper<Pred>
{
typedef class_wrapper<Pred> base;
using base::get;
public:
explicit binary_predicate( Pred pred_ )
: base( pred_ ) {}
typedef bool result_type;
template<class T, class U>
bool operator()( T const& x, U const& y ) {
return bool( get()( x, y ) );
}
template<class T, class U>
bool operator()( T const& x, U const& y ) const {
return bool( get()( x, y ) );
}
};
// 補助関数
template<class Pred>
inline binary_predicate<Pred> pred2( Pred pred ) {
return binary_predicate<Pred>( pred );
}
// 以降は便利な小物関数。
// find_if とかで使うファンクタを作りやすいように比較をやりやすいように
// 参照キャプチャと値キャプチャの二つを用意
template<class U>
inline binder2nd<equal_to<>, U const&> equals_to( U const& y ) {
return binder2nd<equal_to<>, U const&>( equal_to<>(), y );
}
template<class U>
inline binder2nd<equal_to<>, U> equals_to_safe( U const& y ) {
return binder2nd<equal_to<>, U>( equal_to<>(), y );
}
template<class U>
inline binder2nd<less<>, U const&> less_than( U const& y ) {
return binder2nd<less<>, U const&>( less<>(), y );
}
template<class U>
inline binder2nd<less<>, U> less_than_safe( U const& y ) {
return binder2nd<less<>, U>( less<>(), y );
}
template<class U>
inline binder2nd<greater<>, U const&> greater_than( U const& y ) {
return binder2nd<greater<>, U const&>( greater<>(), y );
}
template<class U>
inline binder2nd<greater<>, U> greater_than_safe( U const& y ) {
return binder2nd<greater<>, U>( greater<>(), y );
}
} // namespace etude
#endif // #ifndef ETUDE_INCLUDED_PREDICATES_HPP_
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment