簡體   English   中英

C ++中的函數組合

[英]Function Composition in C++

有許多令人印象深刻的Boost庫,如Boost.Lambda或Boost.Phoenix,它們使C ++成為一種真正的功能語言。 但有沒有一種直接的方法可以從任何2個或更多任意函數或函子創建復合函數?

如果我有: int f(int x)int g(int x) ,我想做類似f . g事情f . g f . g將靜態生成一個等於f(g(x)).的新函數對象f(g(x)).

這似乎可以通過各種技術實現,例如這里討論的技術。 當然,你可以鏈接調用boost::lambda::bind來創建一個復合函子。 但是Boost中是否有任何東西可以輕松地讓你接受任何2個或更多的函數或函數對象並將它們組合起來創建一個復合函子,類似於你在Haskell這樣的語言中的表達方式?

對於任何絆到這個頁面的人來說,有一個關於這個主題的博客文章來自第14局:

http://blog.quasardb.net/function-composition-in-c11/

這利用了C ++ 11中的新功能以及使用boost。

在這個問題上磕磕絆絆,我想指出今天遇到這個問題的人,只需使用標准庫和一些幫助類就可以通過相對優雅的語法實現這一點,這要歸功於decltype,auto和完美轉發。

定義這兩個類:

template <class Arg, class ArgCall, class OuterCall>
class pipe {
private:
    ArgCall argcall;
    OuterCall outercall;
public:
    typedef pipe<Arg, ArgCall, OuterCall>  this_type;
    pipe(ArgCall ac, OuterCall oc) : argcall(ac), outercall(oc) {}
    auto operator()(Arg arg) -> decltype(outercall(argcall(arg))) {
        return outercall(argcall(arg));
    }
    template <class NewCall>
    pipe<Arg, this_type, NewCall> operator[](NewCall&& nc) {
        return {*this, std::forward<NewCall>(nc)};
    }
};

template <class Arg>
class pipe_source {
public:
    typedef pipe_source<Arg> this_type;
    Arg operator()(Arg arg) {
        return arg;
    }
    template <class ArgCall, class OuterCall>
    static pipe<Arg, ArgCall, OuterCall> create(ArgCall&& ac, OuterCall&& oc) {
        return {std::forward<ArgCall>(ac), std::forward<OuterCall>(oc)};
    }
    template <class OuterCall>
    pipe<Arg, this_type, OuterCall> operator[](OuterCall&& oc) {
        return {*this, std::forward<OuterCall>(oc)};
    }
};

一個簡單的程序:

int f(int x) {
        return x*x;
}

int g(int x) {
        return x-2;
}

int h(int x) {
        return x/2;
}

int main() {
        auto foo = pipe_source<int>::create(f, g);
        //or:
        auto bar = pipe_source<int>()[g][h];
        std::cout << foo(10) << std::endl;
        std::cout << bar(10) << std::endl;
        return 0;
}

這有一個額外的好處,一旦它在管道中,只要返回類型是正確的,你可以使用管道[f]向鏈中添加另一個函數f。

然后:

$ g++ test.cpp -o test -std=c++11
$ ./test
98
4
$

我不知道任何支持您目前所需語法的內容。 但是,創建一個是一件簡單的事情。 只需覆蓋*為仿函數(例如boost :: function <>),以便它返回一個復合仿函數。


template < typename R1, typename R2, typename T1, typename T2 >
boost::function<R1(T2)> operator * (boost::function<R1(T2)> const& f, boost::function<R2(T2)> const& g)
{
  return boost::bind(f, boost::bind(g, _1));
}

未經測試,但我懷疑它是否接近,如果它沒有開箱即用。

模板他們。

template<typename T1> class FunctorOne {
    FunctorOne(T1 newt)
        : t(newt) {}
    void operator()() {
        t();
    }
    T1 t;
};
template<> class FunctorOne<void> {
    void operator()() {
    }
};
template<typename T1> class FunctorTwo {
    FunctorOne(T1 newt)
        : t(newt) {}
    void operator()() {
        t();
    }
    T1 t;
};
template<> class FunctorTwo<void> {
    void operator()() {
    }
};
FunctorOne<FunctorTwo<FunctorOne<FunctorTwo<void>>>>> strangefunctionobject(FunctorTwo(FunctorOne(FunctorTwo()));

建議使用typedef。
編輯:哎呀。 事實證明,構造函數中的類型推斷很糟糕。 我會在一分鍾內找到真正有用的東西:P
更多編輯:
如果你只想要函子而不是函數,你可以創建一個新實例,甚至只使用靜態函數。

template<typename T1, typename T2> class FunctorOne {
public:
    static bool Call() {
        T1::Call(T2::Call());
        return true;
    }
};
template<> class FunctorOne<void, void> {
public:
    static bool Call() {
    }
};
template<typename T1> class FunctorTwo {
public:
    static bool Call() {
        T1::Call();
    }
};
template<> class FunctorTwo<void> {
public:
    static bool Call() {
    }
};

bool haicakes = FunctorOne<FunctorTwo<void>, FunctorTwo<void>>::Call();

這假設在任何給定的函數中,您可以手動處理每個不同的簽名。 在這方面使用decltype可以幫助C ++ 0x編譯器。

請參閱此答案https://stackoverflow.com/a/27727236/286335 真的很短,容易和一般。

C ++ 11。 沒有提升。 沒有幫助類。 任何數量的論點。 只是std :: function和variadic模板。

template <typename F1, typename F2>
struct function_composition_traits : public function_composition_traits<decltype(&F1::operator()), decltype(&F2::operator())>
{};

template <typename ClassType1, typename ReturnType1, typename... Args1, typename ClassType2, typename ReturnType2, typename... Args2>
struct function_composition_traits<ReturnType1(ClassType1::*)(Args1...) const, ReturnType2(ClassType2::*)(Args2...) const>
{
        typedef std::function<ReturnType2(Args1...)> composition;

        template <typename Func1, typename Func2>
        inline static composition compose(const Func1& f1, const Func2& f2) {
            return [f1,f2](Args1... args) -> ReturnType2 { return f2(f1(std::forward<Args1>(args)...)); };
        }
};

template <typename F1, typename F2>
typename function_composition_traits<F1,F2>::composition compose(const F1& lambda1,const F2& lambda2)
{
        return function_composition_traits<F1,F2>::template compose<F1,F2>(lambda1, lambda2);
}

template <typename F, typename... Fs>
auto compose(F f, Fs... fs) -> decltype(compose(f, compose(fs...)))
{
        return compose(f, compose(std::forward<Fs>(fs)...));
}

用法:

auto add = [](int x, int y){ return x+y; };
auto mul = [](int x){ return x*2; };
auto divide = [](int x) { return (double)x / 3.0; };
auto test = compose(add, mul, divide);

cout << "Result: " << test(2,3);

輸出:

結果:3.33333

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM