function composition in C++ / C++11

前端 未结 7 1487
北荒
北荒 2020-12-01 09:38

I am currently coding some cryptographic algorithms in C++11 that require a lot of function compositions. There are 2 types of composition I have to deal with :

相关标签:
7条回答
  • 2020-12-01 09:55

    A quick implementation of function iteration with argument forwarding. The helper type is unfortunately necessary because function templates can’t be partially specialised.

    #include <functional>
    #include <iostream>
    using namespace std;
    
    template<int n, typename A>
    struct iterate_helper {
      function<A(A)> f;
      iterate_helper(function<A(A)> f) : f(f) {}
      A operator()(A&& x) {
        return f(iterate_helper<n - 1, A>(f)(forward<A>(x)));
      };
    };
    
    template<typename A>
    struct iterate_helper<1, A> {
      function<A(A)> f;
      iterate_helper(function<A(A)> f) : f(f) {}
      A operator()(A&& x) {
        return f(forward<A>(x));
      };
    };
    
    template<int n, typename A>
    function<A(A)> iterate(function<A(A)> f) {
      return iterate_helper<n, A>(f);
    }
    
    int succ(int x) {
      return x + 1;
    }
    
    int main() {
      auto add5 = iterate<5>(function<int(int)>(succ));
      cout << add5(10) << '\n';
    }
    
    0 讨论(0)
  • 2020-12-01 09:57

    Thanks for the fun question, Gabriel of year 2013. Here is a solution. It works in c++14.

    #include <functional>
    #include <iostream>
    using std::function;
    
    // binary function composition for arbitrary types
    template <class F, class G>
    auto compose(F f, G g){
      return [f,g](auto x){return f(g(x));};
    }
    
    // for convienience
    template <class F, class G>
    auto operator*(F f, G g){
      return compose(f,g);
    }
    
    // composition for n arguments
    template <class F, typename... Fs>
    auto compose(F f, Fs&&... fs) {
      return f * compose(fs...);
    }
    
    // composition for n copies of f
    template <int i, class F>
    //must wrap chain in a struct to allow partial template specialization
    struct multi { static F chain(F f){
        return f * multi<i-1,F>::chain(f);
    }};
    template <class F>
    struct multi <2,F> { static F chain(F f){
        return f * f;
    }};
    template <int i, class F>
    F compose(F f) {return multi<i,F>::chain(f); }
    
    int main(int argc, char const *argv[]) {
    
      function<double(int)> f = [](auto i){return i + 3;};
      function<int(double)> g = [](auto i){return i * 2;};
      function<int(int)   > h = [](auto i){return i + 1;};
    
      std::cout
        << '\n' <<  "9 == " << compose(f,g,f)   (0)  \
        << '\n' <<  "9 == " << (f * g * h)      (0)  \
        << '\n' <<  "3 == " << compose<100>(h)  (0)  \
        << '\n';
    
      return 0;
    }
    

    You can define

    Matrix compose(Matrix f, Matrix g);
    

    or

    Rotation compose(Rotation f, Rotation g);
    

    to reuse this code for all sorts of things.

    0 讨论(0)
  • 2020-12-01 09:58

    Here is a simple c++14 solution (it may probably be re-written to c++11):

    #include <iostream>
    
    // base condition
    template <typename F>
    auto compose(F&& f)
    {
        return [a = std::move(f)](auto&&... args){
            return a(std::move(args)...);
        };
    }
    
    // recursive composition
    // from compose(a, b, c...) to compose(ab, c...)
    template <typename F1, typename F2, typename... Fs>
    auto compose(F1&& f1, F2&& f2, Fs&&... fs)
    {
        return compose(
            [first = std::move(f1), second = std::move(f2)]
            (auto&&... args){
                return second(first(std::move(args)...));
            },
            std::move(fs)...
        );
    }
    

    Possible usage:

    int main()
    {
    const auto f = compose(
      [](const auto n){return n * n;},
      [](const auto n){return n + 2;},
      [](const auto n){return n + 2;}
    );
    std::cout << f(10) << std::endl; // outputs 104
    }
    

    Here is a link to the repo with a few more examples: https://github.com/nestoroprysk/FunctionComposition

    0 讨论(0)
  • 2020-12-01 09:58

    What about (untested):

    template < typename Func, typename T >
    T  compose_impl( Func &&, T &&x, std::integral_constant<std::size_t, 0> )
    { return std::forward<T>(x); }
    
    template < typename Func, typename T, std::size_t N >
    T compose_impl( Func &&f, T &&x, std::integral_constant<std::size_t, N> )
    {
        return compose_impl( std::forward<Func>(f),
         std::forward<Func>(f)(std::forward<T>( x )),
         std::integral_constant<std::size_t, N-1>{} );
    }
    
    template < std::size_t Repeat = 1, typename Func, typename T >
    T  compose( Func &&f, T &&x )
    {
        return compose_impl( std::forward<Func>(f), std::forward<T>(x),
         std::integral_constant<std::size_t, Repeat>{} );
    }
    

    We can use variadic function templates for multiple functions (untested):

    template < typename Func, typename T >
    constexpr  // C++14 only, due to std::forward not being constexpr in C++11
    auto chain_compose( Func &&f, T &&x )
     noexcept( noexcept(std::forward<Func>( f )( std::forward<T>(x) )) )
     -> decltype( std::forward<Func>(f)(std::forward<T>( x )) )
    { return std::forward<Func>(f)(std::forward<T>( x )); }
    
    template < typename Func1, typename Func2, typename Func3, typename ...RestAndT >
    constexpr  // C++14 only, due to std::forward
    auto  chain_compose( Func1 &&f, Func2 &&g, Func3 &&h, RestAndT &&...i_and_x )
     noexcept( CanAutoWorkHereOtherwiseDoItYourself )
     -> decltype( auto )  // C++14 only
    {
        return chain_compose( std::forward<Func1>(f),
         chain_compose(std::forward<Func2>( g ), std::forward<Func3>( h ),
         std::forward<RestAndT>( i_and_x )...) );
    }
    

    The upcoming decltype(auto) construct automatically computes the return type from an inlined function. I don't know if there's a similar automatic computation for noexcept

    0 讨论(0)
  • 2020-12-01 10:08

    Something along these lines, perhaps (untested):

    template <typename F>
    class Composer {
      int n_;
      F f_;
    public:
      Composer(int n, F f) : n_(n), f_(f) {}
    
      template <typename T>
      T operator()(T x) const {
        int n = n_;
        while (n--) {
          x = f_(x);
        }
        return x;
      }
    };
    
    template <int N, typename F>
    Composer<F> compose(F f) {
      return Composer<F>(N, f);
    }
    

    EDIT: And for the second case (tested this time):

    #include <iostream>
    
    template <typename F0, typename... F>
    class Composer2 {
        F0 f0_;
        Composer2<F...> tail_;
    public:
        Composer2(F0 f0, F... f) : f0_(f0), tail_(f...) {}
    
        template <typename T>
        T operator() (const T& x) const {
            return f0_(tail_(x));
        }
    };
    
    template <typename F>
    class Composer2<F> {
        F f_;
    public:
        Composer2(F f) : f_(f) {}
    
        template <typename T>
        T operator() (const T& x) const {
            return f_(x);
        }
    };
    
    template <typename... F>
    Composer2<F...> compose2(F... f) {
        return Composer2<F...>(f...);
    }
    
    int f(int x) { return x + 1; }
    int g(int x) { return x * 2; }
    int h(int x) { return x - 1; }
    
    int main() {
      std::cout << compose2(f, g, h)(42);
      return 0;
    }
    
    0 讨论(0)
  • A very general example (g++ -std=c++1y composition.cpp):

    // ---------------------------------------------------------
    // "test" part
    // ---------------------------------------------------------
    int f(int a) { return 2*a; }
    double g(int a) { return a+2.5; }
    double h(double a) { return 2.5*a; }
    double i(double a) { return 2.5-a; }
    
    class Functor {
      double x;
    public:
      Functor (double x_) :  x(x_) { }
      double operator() (double a) { return a*x; }
    };
    
    // ---------------------------------------------------------
    // ---------------------------------------------------------
    int main () {
    
      auto l1 = [] (double a) { return a/3; };
      auto l2 = [] (double a) { return 3.5+a; };
    
      Functor fu {4.5};
    
      auto compos1 = compose (f, g, l1, g, h, h, l1, l2); 
    
      auto compos2 = compose (compos1, l1, l2, fu);
    
      auto x = compos2 (3);
    
      cout << x << endl;
      cout << compos2(3) << endl;
    
      cout << fu(l2(l1(l2(l1(h(h(g(l1(g(f(3))))))))))) << endl;
    
    } // ()
    

    Library part:

    // ---------------------------------------------------------
    // "library" part
    // ---------------------------------------------------------
    template<typename F1, typename F2>
    class Composite{
    private:
      F1  f1;
      F2  f2;
    
    public:
      Composite(F1  f1,  F2  f2) : f1(f1), f2(f2) { }
    
      template<typename IN>
      decltype(auto) operator() (IN i)   
      { 
        return f2 ( f1(i) ); 
      }
    };
    
    // ---------------------------------------------------------
    // ---------------------------------------------------------
    template<typename F1, typename F2>
    decltype(auto) compose (F1 f, F2 g) {
      return Composite<F1, F2> {f,g};
    }
    
    // ---------------------------------------------------------
    // ---------------------------------------------------------
    template<typename F1, typename... Fs>
    decltype(auto) compose (F1  f,  Fs  ... args) 
    {
      return compose (f, compose(args...));
    }
    

    The whole program:

    //  g++ -std=c++1y composition.cpp 
    
    #include <iostream>
    
    using namespace std;
    
    // ---------------------------------------------------------
    // "library" part
    // ---------------------------------------------------------
    template<typename F1, typename F2>
    class Composite{
    private:
      F1  f1;
      F2  f2;
    
    public:
      Composite(F1  f1,  F2  f2) : f1(f1), f2(f2) { }
    
      template<typename IN>
      decltype(auto) operator() (IN i)   
      { 
        return f2 ( f1(i) ); 
      }
    };
    
    // ---------------------------------------------------------
    // ---------------------------------------------------------
    template<typename F1, typename F2>
    decltype(auto) compose (F1 f, F2 g) {
      return Composite<F1, F2> {f,g};
    }
    
    // ---------------------------------------------------------
    // ---------------------------------------------------------
    template<typename F1, typename... Fs>
    decltype(auto) compose (F1  f,  Fs  ... args) 
    {
      return compose (f, compose(args...));
    }
    
    // ---------------------------------------------------------
    // "test" part
    // ---------------------------------------------------------
    int f(int a) { return 2*a; }
    double g(int a) { return a+2.5; }
    double h(double a) { return 2.5*a; }
    double i(double a) { return 2.5-a; }
    
    class Functor {
      double x;
    public:
      Functor (double x_) :  x(x_) { }
      double operator() (double a) { return a*x; }
    };
    
    // ---------------------------------------------------------
    // ---------------------------------------------------------
    int main () {
    
      auto l1 = [] (double a) { return a/3; };
      auto l2 = [] (double a) { return 3.5+a; };
    
      Functor fu {4.5};
    
      auto compos1 = compose (f, g, l1, g, h, h, l1, l2); 
    
      auto compos2 = compose (compos1, l1, l2, fu);
    
      auto x = compos2 (3);
    
      cout << x << endl;
      cout << compos2(3) << endl;
    
      cout << fu(l2(l1(l2(l1(h(h(g(l1(g(f(3))))))))))) << endl;
    
    } // ()
    
    0 讨论(0)
提交回复
热议问题