简体   繁体   English

C ++中的函数组合

[英]Function Composition in C++

There are a lot of impressive Boost libraries such as Boost.Lambda or Boost.Phoenix which go a long way towards making C++ into a truly functional language. 有许多令人印象深刻的Boost库,如Boost.Lambda或Boost.Phoenix,它们使C ++成为一种真正的功能语言。 But is there a straightforward way to create a composite function from any 2 or more arbitrary functions or functors? 但有没有一种直接的方法可以从任何2个或更多任意函数或函子创建复合函数?

If I have: int f(int x) and int g(int x) , I want to do something like f . g 如果我有: int f(int x)int g(int x) ,我想做类似f . g事情f . g f . g which would statically generate a new function object equivalent to f(g(x)). f . g将静态生成一个等于f(g(x)).的新函数对象f(g(x)).

This seems to be possible through various techniques, such as those discussed here . 这似乎可以通过各种技术实现,例如这里讨论的技术。 Certainly, you can chain calls to boost::lambda::bind to create a composite functor. 当然,你可以链接调用boost::lambda::bind来创建一个复合函子。 But is there anything in Boost which easily allows you to take any 2 or more functions or function objects and combine them to create a single composite functor, similar to how you would do it in a language like Haskell? 但是Boost中是否有任何东西可以轻松地让你接受任何2个或更多的函数或函数对象并将它们组合起来创建一个复合函子,类似于你在Haskell这样的语言中的表达方式?

To anyone stumbling onto this page, there's a great blog post on this subject from bureau14: 对于任何绊到这个页面的人来说,有一个关于这个主题的博客文章来自第14局:

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

This takes advantage of the new features in C++ 11 as well as using boost. 这利用了C ++ 11中的新功能以及使用boost。

Stumbling upon this question, I'd like to point out to anyone who comes across this today that this is possible with a relatively elegant syntax using just the standard library and a few helper classes thanks to decltype, auto, and perfect forwarding. 在这个问题上磕磕绊绊,我想指出今天遇到这个问题的人,只需使用标准库和一些帮助类就可以通过相对优雅的语法实现这一点,这要归功于decltype,auto和完美转发。

Defining these two classes: 定义这两个类:

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)};
    }
};

A simple program: 一个简单的程序:

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;
}

This has the added benefit that once it's in a pipe, as long as the return type is correct you can add another function f to the chain with pipe[f]. 这有一个额外的好处,一旦它在管道中,只要返回类型是正确的,你可以使用管道[f]向链中添加另一个函数f。

Then: 然后:

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

I don't know of anything that supports the syntax you wish for currently. 我不知道任何支持您目前所需语法的内容。 However, it would be a simple matter to create one. 但是,创建一个是一件简单的事情。 Simply override * for functors (boost::function<> for example) so that it returns a composite functor. 只需覆盖*为仿函数(例如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));
}

Untested, but I suspect it's close if it doesn't work out of the box. 未经测试,但我怀疑它是否接近,如果它没有开箱即用。

Template them. 模板他们。

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()));

Excellent use of typedefs is recommended. 建议使用typedef。
Edit: Whoops. 编辑:哎呀。 Turns out that type inference in constructors sucks. 事实证明,构造函数中的类型推断很糟糕。 I'll get back in a minute with something that actually works :P 我会在一分钟内找到真正有用的东西:P
Even more edit: 更多编辑:
If you wanted just functors rather than functionoids, you could just create a new instance, or even just use static functions. 如果你只想要函子而不是函数,你可以创建一个新实例,甚至只使用静态函数。

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();

This assumes that in any given function, you can handle each different signature somewhat manually. 这假设在任何给定的函数中,您可以手动处理每个不同的签名。 Use of decltype could help in this regard with a C++0x compiler. 在这方面使用decltype可以帮助C ++ 0x编译器。

See this answer https://stackoverflow.com/a/27727236/286335 . 请参阅此答案https://stackoverflow.com/a/27727236/286335 Really short, easy and general. 真的很短,容易和一般。

C++11. C ++ 11。 No boost. 没有提升。 No helper classes. 没有帮助类。 Any amount of arguments. 任何数量的论点。 Just std::function and variadic templates. 只是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)...));
}

Usage: 用法:

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);

Output: 输出:

Result: 3.33333 结果:3.33333

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM