2011-02-25 10 views
13

一連の変換、int f1(int)int f2(int)int f3(int)をオブジェクトのリストに適用するとします。素朴な方法は C++イテレータのパイプライン設計

SourceContainer source; 

TempContainer1 temp1; 
transform(source.begin(), source.end(), back_inserter(temp1), f1); 
TempContainer2 temp2; 
transform(temp1.begin(), temp1.end(), back_inserter(temp2), f2); 

TargetContainer target; 
transform(temp2.begin(), temp2.end(), back_inserter(target), f3); 

この最初ソリューション

が原因 temp1temp2と、余分なスペース要件の最適ではないだろう。それでは、これで賢く取得してみましょう:

int f123(int n) { return f3(f2(f1(n))); } 
... 
SourceContainer source; 
TargetContainer target; 
transform(source.begin(), source.end(), back_inserter(target), f123); 

コードは簡単ですが、より重要な中間計算せずに、より少ないスペース要件があるだけでなくので、このソリューションがはるかに優れています。

ただし、コンパイル時に組成f123を決定する必要があるため、実行時に固定されます。

実行時に構成を決定する場合、これを効率的に実行する方法を教えてください。たとえば、このコードがRPCサービス内にあり、実際のコンポジション(f1f2、およびf3のサブセットの任意の順列)は、RPC呼び出しの引数に基づいています。

+3

+1私は大好きです人々が何を考え出すかを見てください。 – templatetypedef

+1

あなたは正しい場所で「変形」できることを知っていますか? –

+0

この例では、 'SourceContainer'と' TargetContainer'を使用しましたが、反復子はinput-streamとoutput-streamから来ている可能性があります。 – kirakun

答えて

3

はEDIThttp://ideone.com/5GxnWで働くバージョンを。以下のバージョンはアイデアを持っていますが、コンパイルはしません。実行時の型チェック、および実行時の関数の構成をサポートしています。

考え方は、汎用(単項)関数クラスと、実行時型チェックでそれらを構成する方法を定義することです。これは、boost::any,boost::functionとタイプ消去イディオムの組み合わせで行われます。

// First function passed is evaluated first. Feel free to change. 
template <typename Arg, typename Res, typename I> 
boost::function<Res(Arg)> pipeline(I begin, I end) 
{ 
    return std::accumulate(begin, end, 
     any_function::id<Arg>, 
     std::ptr_fun(any_function::compose) 
    ).to_function<Res, Arg>(); 
} 

とあなたも使用することができます

std::vector<any_function> f; 
std::vector<double> v; 
std::vector<int> result; 

std::transform(v.begin(), v.end(), 
    result.begin(), 
    pipeline<double, int>(f.begin(), f.end()) 
); 

それを適用するには、次を使用します。

#include <boost/any.hpp> 
#include <boost/function.hpp> 
#include <boost/shared_ptr.hpp> 


template <typename T> 
struct identity 
{ 
    T operator()(const T& x) { return x; } 
}; 

struct any_function 
{ 
    template <typename Res, typename Arg> 
    any_function(boost::function<Res, Arg> f) 
    { 
     impl = make_impl(f); 
    } 

    boost::any operator()(const boost::any& x) 
    { 
     return impl->invoke(x); 
    } 

    static any_function compose(const any_function& f, 
           const any_function& g) 
    { 
     any_function ans; 
     ans.impl = compose_impl(f.impl, g.impl); 
     return ans; 
    } 

    template <typename T> 
    static any_function id() 
    { 
     using boost::function 
     return any_function(function<T(T)>(identity<T>())); 
    } 

    template <typename Res, typename Arg> 
    boost::function<Res(Arg)> to_function() 
    { 
     using boost::function; 
     return function<Res(Arg)>(to_function_helper(impl)); 
    } 

private: 
    any_function() {} 

    struct impl_type 
    { 
     virtual ~impl_type() {} 
     virtual boost::any invoke(const boost::any&) = 0; 
    }; 

    boost::shared_ptr<impl_type> impl; 

    template <typename Res, typename Arg> 
    static impl_type* make_impl(boost::function<Res(Arg)> f) 
    { 
     using boost::function; 
     using boost::any; 
     using boost::any_cast; 

     class impl : public impl_type 
     { 
      function<Res(Arg)> f; 

      any invoke(const any& x) 
      { 
       const Arg& a = any_cast<Arg>(x); 
       return any(f(a)); 
      } 

     public: 
      impl(function<Res(Arg)> f) : f(f) {} 
     }; 

     return new impl(f); 
    } 

    impl_type* compose_impl(boost::shared_ptr<impl_type> f, 
          boost::shared_ptr<impl_type> g) 
    { 
     using boost::any; 
     using boost::shared_ptr; 

     class impl : public impl_type 
     { 
      shared_ptr<impl> f, g; 

      any invoke(const any& x) 
      { 
       return g->invoke(f->invoke(x)); 
      } 

     public: 
      impl(const shared_ptr<impl>& f, 
       const shared_ptr<impl>& g) 
       : f(f), g(g) 
      {} 
     }; 

     return new impl(f, g); 
    } 

    struct to_function_helper 
    { 
     template <typename Res, typename Arg> 
     Res operator()(const Arg& x) 
     { 
      using boost::any; 
      using boost::any_cast; 

      return any_cast<Res>(p->invoke(any(x))); 
     } 

     to_function_helper(const boost::shared_ptr<impl>& p) : p(p) {} 

    private: 
     boost::shared_ptr<impl> p; 
    }; 
}; 

それでは、標準アルゴリズムを使用して、これを(これも空の配列に動作します)やらせますboost::transform_iterator

typedef boost::transform_iterator< 
    boost::function<double, int>, 
    std::vector<double>::const_iterator 
> iterator; 

boost::function<double, int> f = pipeline<double, int>(f.begin(), f.end()); 
std::copy(iterator(v.begin(), f), iterator(v.end(), f), result.begin()); 
+0

STLの精神を強調した素晴らしいC++コード。ローカルクラスやboost :: anyを使ってC++の動的型をシミュレートするなど、いくつかのことを学びました。 – kirakun

+0

偉大なトリック 'identity'。私たちが望むなら、変換モノイドを持っています。あなたはそれを自分で作りましたか? –

+0

@Oleg: 'accumulate'アルゴリズムは関数型言語では' fold'のようなもので、開始値が必要です。私たちは実際にここで関数レベルで作業しています.C++は特に冗長です。 –

1

あなたは機能の可変数を必要とするならば、関数のベクトルを使用するFunctorコンストラクタシグネチャを変更ファンクタのコンストラクタに変換関数に

typedef int (*FunctionType)(int); 

class Functor 
{ 
    FunctionType m_f1; 
    FunctionType m_f2; 
    FunctionType m_f3; 
public: 
    Functor(FunctionType f1, FunctionType f2, FunctionType f3): 
     m_f1(f1), m_f2(f2), m_f3(f3) 
    {} 
    int operator()(int n) 
    { 
     return (*m_f1)((*m_f2)((*m_f3)(n))); 
    } 
}; 

// ... 

transform(source.begin(), source.end(), back_inserter(temp1), Functor(f1,f2,f3)); 

よう

何かを代わりに関数のファンクタを使用し、必要に応じを渡す必要がありますtransformを呼び出す前にそのベクトルを埋めてください。

+0

関数ポインタのベクトルを使うほうが良いかもしれません。 –

+0

@Zanはい、関数ポインタのベクトルはより良いでしょう – rmflow

3
template<class T> 
class compose { 
    typedef T (*f)(T); 

    f first_func; 
    f second_func; 

public: 

    compose(f one,f two) : 
     first_func(one), 
     second_func(two)   
    {} 

    T operator()(T const &input) { 
     T temp = first_func(input); 
     return second_func(temp); 
    } 
}; 

#ifdef TEST 

int f(int x) { return 8 + x; } 
int g(int x) { return 2 * x; } 
int h(int x) { return x * x; } 

#include <iostream> 

int main(int argc, char **argv) { 
    compose<int> x(f, g); 
    compose<int> y(g, f); 

    std::cout << x(6) << std::endl; 
    std::cout << y(6) << std::endl; 

    typedef int (*func)(int); 

    func funcs[] = {f, g, h}; 

    compose<int> z(funcs[atoi(argv[1])], funcs[atoi(argv[2])]); 
    std::cout << z(6); 

    return 0; 
} 

#endif 

C++ 0xでは、引数/戻り値の型を指定する必要がなくなるため、autoを使用できるようになります。私は彼らが同じだと仮定した瞬間、理論的には、コンバージョンをミックスに含めることが好きかもしれません。

+0

これは、ファンクタで動作するこれのvariadicバージョンはありますか? – kirakun

+0

私は、OPが実行時の構成を探していると思うので、複雑さが増します。 – fbrereto

+0

@kirakun:私は一度も書きましたが、書き込めない理由はありません。このコードは古くて、当時、テンプレートを使用することはコンパイラ機能の限界を押し進めていました。 –

0
typedef int (*f_t)(int); 

int f1(int a) { return a + 1; } 
int f2(int a) { return a * 2; } 
int f3(int a) { return a * a; } 

int main() 
{ 
    std::vector<f_t> ff = {f1, f2, f3}; 
    std::vector<int> source = {1, 2, 3, 4}, target; 

    std::transform(source.begin(), source.end(), std::back_inserter(target) 
    , [&](int a) { for (f_t &f : ff) a = f(a); return a; }); 

    // print target 
    std::copy(target.begin(), target.end(), std::ostream_iterator<int,char>(std::cout,"\n")); 
    system("pause"); 
    return 0; 
} 
+0

今日は本番環境でC++ 0xを導入することができればうれしいです。 – kirakun

0

だけ あなたが欲しいものを行い、イテレータを定義:

template<typename T> 
struct source 
{ 
    virtual source<T>& operator++(void) = 0; 
    virtual T operator*(void) = 0; 
    virtual bool atend() = 0; 
}; 

struct source_exhausted 
{ 
}; 

template<typename T> 
bool operator==(const source<T>& comparand, const source_exhausted&) 
{ return comparand.atend(); } 

template<typename T> 
bool operator!=(const source<T>& comparand, const source_exhausted&) 
{ return !comparand.atend(); } 

template<typename T> 
bool operator==(const source_exhausted&, const source<T>& comparand) 
{ return comparand.atend(); } 

template<typename T> 
bool operator!=(const source_exhausted&, const source<T>& comparand) 
{ return !comparand.atend(); } 

template<typename T, typename iterT, typename endT> 
struct source_iterator : source<T> 
{ 
    iterT m_iter; 
    endT m_end; 
    source_iterator(iterT iter, endT end) : m_iter(iter), m_end(end) {} 

    virtual source<T>& operator++(void) { ++m_iter; return *this; } 
    virtual T operator*(void) { return *m_iter; } 
    virtual bool atend() { return m_iter == m_end; } 
}; 
template<typename T, typename iterT, typename endT> 
auto make_source_iterator(iterT iter, endT end) -> source_iterator<decltype(*iter), iterT, endT> 
{ 
    return source_iterator<decltype(*iter), iterT, endT>(iter, end); 
} 
template<typename TContainer> 
auto make_source_iterator(TContainer& c) -> source_iterator<typename TContainer::value_type, decltype(c.begin()), decltype(c.end())> 
{ 
    return source_iterator<typename TContainer::value_type, decltype(c.begin()), decltype(c.end())>(c.begin(), c.end()); 
} 

template<typename TIn, typename TOut, typename TXform> 
struct source_transformer : source<TOut> 
{ 
    source<TIn>& m_src; 
    TXform const m_f; 
    source_transformer(source<TIn>& src, TXform f) : m_f(f), m_src(src) {} 

    virtual source<TOut>& operator++(void) { ++m_src; return *this; } 
    virtual TOut operator*(void) { return m_f(*m_src); } 
    virtual bool atend() { return m_src.atend(); } 
}; 
template<typename TIn, typename TOut, typename TXform> 
auto make_source_transformer(source<TIn>& src, TXform f) -> source_transformer<TIn, decltype(f(*(TIn*)0)), TXform> 
{ 
    return source_transformer<TIn, decltype(f(*(TIn*)0)), TXform>(src, f); 
} 
+0

あなたのコードで 'auto'と' - ''が何をするのか分かりません。 – kirakun

関連する問題