【问题标题】:How can currying be done in C++?如何在 C++ 中进行柯里化?
【发布时间】:2010-09-14 04:47:13
【问题描述】:

什么是柯里化?

如何在 C++ 中进行柯里化?

请解释一下 STL 容器中的活页夹?

【问题讨论】:

    标签: c++ stl functional-programming currying binders


    【解决方案1】:

    1。什么是咖喱?

    Currying 只是意味着将多个参数的函数转换为单个参数的函数。这很容易用一个例子来说明:

    获取一个接受三个参数的函数f

    int
    f(int a,std::string b,float c)
    {
        // do something with a, b, and c
        return 0;
    }
    

    如果我们想调用f,我们必须提供它的所有参数f(1,"some string",19.7f)

    然后是f 的柯里化版本,我们称它为curried_f=curry(f) 只需要一个参数,对应于f 的第一个参数,即参数a。此外,f(1,"some string",19.7f) 也可以使用柯里化版本编写为curried_f(1)("some string")(19.7f)。另一方面,curried_f(1) 的返回值只是另一个函数,它处理f 的下一个参数。最后,我们得到一个满足以下等式的函数或可调用curried_f

    curried_f(first_arg)(second_arg)...(last_arg) == f(first_arg,second_arg,...,last_arg).
    

    2。 C++中如何实现柯里化?

    以下有点复杂,但对我来说效果很好(使用 c++11)......它还允许像这样进行任意程度的柯里化:auto curried=curry(f)(arg1)(arg2)(arg3) 和后来的auto result=curried(arg4)(arg5)。就是这样:

    #include <functional>
    
    namespace _dtl {
    
        template <typename FUNCTION> struct
        _curry;
    
        // specialization for functions with a single argument
        template <typename R,typename T> struct
        _curry<std::function<R(T)>> {
            using
            type = std::function<R(T)>;
            
            const type
            result;
            
            _curry(type fun) : result(fun) {}
            
        };
    
        // recursive specialization for functions with more arguments
        template <typename R,typename T,typename...Ts> struct
        _curry<std::function<R(T,Ts...)>> {
            using
            remaining_type = typename _curry<std::function<R(Ts...)> >::type;
            
            using
            type = std::function<remaining_type(T)>;
            
            const type
            result;
            
            _curry(std::function<R(T,Ts...)> fun)
            : result (
                [=](const T& t) {
                    return _curry<std::function<R(Ts...)>>(
                        [=](const Ts&...ts){ 
                            return fun(t, ts...); 
                        }
                    ).result;
                }
            ) {}
        };
    }
    
    template <typename R,typename...Ts> auto
    curry(const std::function<R(Ts...)> fun)
    -> typename _dtl::_curry<std::function<R(Ts...)>>::type
    {
        return _dtl::_curry<std::function<R(Ts...)>>(fun).result;
    }
    
    template <typename R,typename...Ts> auto
    curry(R(* const fun)(Ts...))
    -> typename _dtl::_curry<std::function<R(Ts...)>>::type
    {
        return _dtl::_curry<std::function<R(Ts...)>>(fun).result;
    }
    
    #include <iostream>
    
    void 
    f(std::string a,std::string b,std::string c)
    {
        std::cout << a << b << c;
    }
    
    int 
    main() {
        curry(f)("Hello ")("functional ")("world!");
        return 0;
    }
    

    View output

    好的,正如 Samer 评论的那样,我应该添加一些关于它是如何工作的解释。实际的实现是在_dtl::_curry 中完成的,而模板函数curry 只是方便的包装器。该实现对std::function模板参数FUNCTION的参数进行递归。

    对于只有一个参数的函数,结果与原始函数相同。

            _curry(std::function<R(T,Ts...)> fun)
            : result (
                [=](const T& t) {
                    return _curry<std::function<R(Ts...)>>(
                        [=](const Ts&...ts){ 
                            return fun(t, ts...); 
                        }
                    ).result;
                }
            ) {}
    

    这里有一个棘手的问题:对于具有更多参数的函数,我们返回一个 lambda,其参数绑定到调用 fun 的第一个参数。最后,剩余的N-1 参数的剩余柯里化被委托给_curry&lt;Ts...&gt; 的实现,并减少了一个模板参数。

    c++14 / 17 更新:

    一个解决柯里化问题的新想法刚刚想到我......随着if constexpr 到c++17 中的引入(并在void_t 的帮助下确定一个函数是否完全柯里化),事情似乎变得容易多了:

    template< class, class = std::void_t<> > struct 
    needs_unapply : std::true_type { };
     
    template< class T > struct 
    needs_unapply<T, std::void_t<decltype(std::declval<T>()())>> : std::false_type { };
    
    template <typename F> auto
    curry(F&& f) {
      /// Check if f() is a valid function call. If not we need 
      /// to curry at least one argument:
      if constexpr (needs_unapply<decltype(f)>::value) {
           return [=](auto&& x) {
                return curry(
                    [=](auto&&...xs) -> decltype(f(x,xs...)) {
                        return f(x,xs...);
                    }
                );
            };    
      }
      else {  
        /// If 'f()' is a valid call, just call it, we are done.
        return f();
      }
    }
    
    int 
    main()
    {
      auto f = [](auto a, auto b, auto c, auto d) {
        return a  * b * c * d;
      };
      
      return curry(f)(1)(2)(3)(4);
    }
    

    here 上查看代码。使用类似的方法,here 是如何使用任意数量的参数对函数进行柯里化。

    如果我们根据测试 needs_unapply&lt;decltype(f)&gt;::valueconstexpr if 替换为模板选择,那么同样的想法似乎也适用于 C++14:

    template <typename F> auto
    curry(F&& f);
    
    template <bool> struct
    curry_on;
    
    template <> struct
    curry_on<false> {
        template <typename F> static auto
        apply(F&& f) {
            return f();
        }
    };
    
    template <> struct
    curry_on<true> {
        template <typename F> static auto 
        apply(F&& f) {
            return [=](auto&& x) {
                return curry(
                    [=](auto&&...xs) -> decltype(f(x,xs...)) {
                        return f(x,xs...);
                    }
                );
            };
        }
    };
    
    template <typename F> auto
    curry(F&& f) {
        return curry_on<needs_unapply<decltype(f)>::value>::template apply(f);
    }
    

    【讨论】:

    • 您需要在措辞中解释答案,而不仅仅是发布代码,OP 不仅仅是要求代码,他是在要求解释!
    • +1 这是第一个真正为“如何在 C++ 中进行柯里化?”提供解决方案的答案。正如在其他一些答案的 cmets 中所指出的那样,一般柯里化(如此处所实现)与有限数量的特定参数的部分应用不同。
    • 还要检查 Luc Danton 在Partial application with a C++ lambda? 中的回答,它还提供了关于部分应用主题的一个不错且可能更完整的实现。
    • 这是我正在寻找的答案:使用可变参数函数模板实现柯里化。
    • 不要在 C++ 代码中的任何地方使用双下划线(除非您是编译器供应商)。这根本不合法。
    【解决方案2】:

    简而言之,柯里化采用函数f(x, y),并给定一个固定的Y,给出一个新函数g(x),其中

    g(x) == f(x, Y)
    

    可以在只提供一个参数的情况下调用这个新函数,并将调用传递给带有固定 Y 参数的原始 f 函数。

    STL 中的绑定器允许您为 C++ 函数执行此操作。例如:

    #include <functional>
    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    // declare a binary function object
    class adder: public binary_function<int, int, int> {
    public:
        int operator()(int x, int y) const
        {
            return x + y;
        }
    };
    
    int main()
    {
        // initialise some sample data
        vector<int> a, b;
        a.push_back(1);
        a.push_back(2);
        a.push_back(3);
    
        // here we declare a function object f and try it out
        adder f;
        cout << "f(2, 3) = " << f(2, 3) << endl;
    
        // transform() expects a function with one argument, so we use
        // bind2nd to make a new function based on f, that takes one
        // argument and adds 5 to it
        transform(a.begin(), a.end(), back_inserter(b), bind2nd(f, 5));
    
        // output b to see what we got
        cout << "b = [" << endl;
        for (vector<int>::iterator i = b.begin(); i != b.end(); ++i) {
            cout << "  " << *i << endl;
        }
        cout << "]" << endl;
    
        return 0;
    }
    

    【讨论】:

    • Marcin:raj 在我添加之前请求了 C++ 示例。 :)
    • 我希望我能投票支持评论线索。这个评论链很有趣。 :)
    • 其实我觉得这个答案是错误的;这个答案描述的是“部分应用程序”,bind 函数可以做到这一点。 “柯里化”是将一个接受 N 个参数的函数转换为一个接受一个参数并返回一个具有 N-1 个参数的函数的过程,例如void (*)(int, short, bool) 变为 X (*)(int)X 变为 void (*)(short, bool)。请参阅haskell.org/haskellwiki/Currying,了解有关柯里化和偏函数应用的所有信息。
    • @FrerichRaabe 你是完全正确的;但柯里化的目的是实现(容易)部分应用。
    • @Marcin:我来找这个问题是因为我想要 currying 而不仅仅是任何部分应用程序。不同之处在于,当您仔细编写了带有 6 个参数的函数以便需要修复第一个参数时,您不想将所有占位符都放在那里!
    【解决方案3】:

    使用 tr1 简化 Gregg 的示例:

    #include <functional> 
    using namespace std;
    using namespace std::tr1;
    using namespace std::tr1::placeholders;
    
    int f(int, int);
    ..
    int main(){
        function<int(int)> g     = bind(f, _1, 5); // g(x) == f(x, 5)
        function<int(int)> h     = bind(f, 2, _1); // h(x) == f(2, x)
        function<int(int,int)> j = bind(g, _2);    // j(x,y) == g(y)
    }
    

    Tr1 函数式组件允许您用 C++ 编写丰富的函数式代码。同样,C++0x 也将允许内联 lambda 函数执行此操作:

    int f(int, int);
    ..
    int main(){
        auto g = [](int x){ return f(x,5); };      // g(x) == f(x, 5)
        auto h = [](int x){ return f(2,x); };      // h(x) == f(2, x)
        auto j = [](int x, int y){ return g(y); }; // j(x,y) == g(y)
    }
    

    虽然 C++ 不提供某些面向函数的编程语言执行的丰富副作用分析,但 const 分析和 C++0x lambda 语法可以提供帮助:

    struct foo{
        int x;
        int operator()(int y) const {
            x = 42; // error!  const function can't modify members
        }
    };
    ..
    int main(){
        int x;
        auto f = [](int y){ x = 42; }; // error! lambdas don't capture by default.
    }
    

    希望对您有所帮助。

    【讨论】:

    • 是的...这不是咖喱。
    【解决方案4】:

    看看Boost.Bind,它使 Greg 展示的过程更加通用:

    transform(a.begin(), a.end(), back_inserter(b), bind(f, _1, 5));
    

    这会将5 绑定到f 的第二个参数。

    值得注意的是,这不是柯里化(而是部分应用)。然而,在 C++ 中以一般方式使用柯里化是很困难的(事实上,它直到最近才成为可能)并且经常使用部分应用程序来代替。

    【讨论】:

    • Konrad,既然您使用的是 std::transform 的单源形式,您不应该使用 _1 而不是 _2 吗?您编写 bind(f, 5, _1) 而不是 bind(f, _1, 5) 的事实表明该变量作为第二个参数插入到 f 中。
    • 我对 Boost.Bind 的推荐不够——学习、使用、爱上它!!
    • 这不是柯里化。这是部分应用。
    • @Thomas 你当然是完全正确的。事实上,我对 R 社区使用了错误的术语感到非常恼火。我的答案实际上并没有试图将其作为 currying 来出售,而只是展示了一种更好的方法来实现公认的答案。也就是说,我已经更新了我的答案以明确这一点。
    • 柯里化和偏应用的区别:偏应用采用 f(x, y) 之类的函数,并将一些参数绑定到特定值,返回一个参数较少的函数。例如。我们可以将 y 绑定到 2 的特定值,然后返回一个函数 h(x) = f(x, 2):h 是 f,部分应用。 h(x) 不表示函数;它表示值 f(x, 2)。柯里化不绑定特定值。如果我们通过删除 y 参数来柯里化 f(x, y),我们最终会得到一个返回函数的 h(x)。 h(x) 表示函数 g,因此 g(y) 表示 f(x, y)。
    【解决方案5】:

    其他答案很好地解释了活页夹,所以我不会在这里重复这部分。我将仅演示如何在 C++0x 中使用 lambda 进行柯里化和部分应用。

    代码示例:(cmets 中的解释)

    #include <iostream>
    #include <functional>
    
    using namespace std;
    
    const function<int(int, int)> & simple_add = 
      [](int a, int b) -> int {
        return a + b;
      };
    
    const function<function<int(int)>(int)> & curried_add = 
      [](int a) -> function<int(int)> {
        return [a](int b) -> int {
          return a + b;
        };
      };
    
    int main() {
      // Demonstrating simple_add
      cout << simple_add(4, 5) << endl; // prints 9
    
      // Demonstrating curried_add
      cout << curried_add(4)(5) << endl; // prints 9
    
      // Create a partially applied function from curried_add
      const auto & add_4 = curried_add(4);
      cout << add_4(5) << endl; // prints 9
    }
    

    【讨论】:

      【解决方案6】:

      如果您使用的是 C++14,这非常简单:

      template<typename Function, typename... Arguments>
      auto curry(Function function, Arguments... args) {
          return [=](auto... rest) {
              return function(args..., rest...);
          }; // don't forget semicolumn
      }
      

      然后你可以像这样使用它:

      auto add = [](auto x, auto y) { return x + y; }
      
      // curry 4 into add
      auto add4 = curry(add, 4);
      
      add4(6); // 10
      

      【讨论】:

      • 从技术上讲,这是部分函数评估:完整的 curry 将采用 3 参数函数 f,并在调用之前允许 curry(f)(3)(2)(1)。 +1,因为部分函数评估仍然有用(通常人们所说的“咖喱”是什么意思),而且因为它有多紧。您可以通过更多的代码和完美的转发来提高性能。
      • ...或者只添加一点代码,并将参数从外部“工厂”函数移动到返回的 lambda! ...或者,如果已知它们始终未修改,则只需获取 const 引用,无需复制或移动任何内容。也就是说,在 C++20 之前,将一个可变参数包移动到 lambda 是有点痛苦的:在此之前,您必须将它们存储在 tuple 和稍后的 apply 中。
      • @underscore_d C++20 在这里;这个答案现在可以改进吗?
      【解决方案7】:

      这里有一些很好的答案。我想我会添加自己的,因为玩这个概念很有趣。

      部分函数应用:仅将函数的部分参数“绑定”到函数的过程,其余的推迟到以后填充。结果是另一个参数更少的函数。

      柯里化:是一种特殊形式的偏函数应用程序,您一次只能“绑定”一个参数。结果是另一个函数正好少了 1 个参数。

      我将要介绍的代码是部分函数应用程序,其中柯里化是可能的,但不是唯一的可能性。与上述柯里化实现相比,它提供了一些好处(主要是因为它是部分函数应用程序而不是柯里化,呵呵)。

      • 应用于空函数:

        auto sum0 = [](){return 0;};
        std::cout << partial_apply(sum0)() << std::endl;
        
      • 一次应用多个参数:

        auto sum10 = [](int a, int b, int c, int d, int e, int f, int g, int h, int i, int j){return a+b+c+d+e+f+g+h+i+j;};
        std::cout << partial_apply(sum10)(1)(1,1)(1,1,1)(1,1,1,1) << std::endl; // 10
        
      • constexpr 支持允许编译时 static_assert:

        static_assert(partial_apply(sum0)() == 0);
        
      • 如果您在提供参数时不小心走得太远,则会收到一条有用的错误消息:

        auto sum1 = [](int x){ return x;};
        partial_apply(sum1)(1)(1);
        

        错误:static_assert 失败“尝试应用太多参数!”

      上面的其他答案返回绑定参数然后返回更多 lambdas 的 lambdas。这种方法将基本功能包装到一个可调用对象中。 operator() 的定义允许调用内部 lambda。可变参数模板允许我们检查是否有人走得太远,而函数调用的结果类型的隐式转换函数允许我们打印结果或将对象与原语进行比较。

      代码:

      namespace detail{
      template<class F>
      using is_zero_callable = decltype(std::declval<F>()());
      
      template<class F>
      constexpr bool is_zero_callable_v = std::experimental::is_detected_v<is_zero_callable, F>;
      }
      
      template<class F>
      struct partial_apply_t
      {
          template<class... Args>
          constexpr auto operator()(Args... args)
          {
              static_assert(sizeof...(args) == 0 || !is_zero_callable, "Attempting to apply too many arguments!");
              auto bind_some = [=](auto... rest) -> decltype(myFun(args..., rest...))
              {
                 return myFun(args..., rest...);
              };
              using bind_t = decltype(bind_some);
      
              return partial_apply_t<bind_t>{bind_some};
          }
          explicit constexpr partial_apply_t(F fun) : myFun(fun){}
      
          constexpr operator auto()
          {
              if constexpr (is_zero_callable)
                  return myFun();
              else
                  return *this; // a callable
          }
          static constexpr bool is_zero_callable = detail::is_zero_callable_v<F>;
          F myFun;
      };
      

      Live Demo

      还有一些注意事项:

      • 我选择使用is_detected主要是为了享受和练习;它的作用与此处的普通类型特征相同。
      • 出于性能原因,肯定需要做更多的工作来支持完美转发
      • 代码是 C++17,因为它需要 constexpr lambda 支持 in C++17
        • 而且 GCC 7.0.1 似乎还没有完全出现,所以我使用了 Clang 5.0.0

      一些测试:

      auto sum0 = [](){return 0;};
      auto sum1 = [](int x){ return x;};
      auto sum2 = [](int x, int y){ return x + y;};
      auto sum3 = [](int x, int y, int z){ return x + y + z; };
      auto sum10 = [](int a, int b, int c, int d, int e, int f, int g, int h, int i, int j){return a+b+c+d+e+f+g+h+i+j;};
      
      std::cout << partial_apply(sum0)() << std::endl; //0
      static_assert(partial_apply(sum0)() == 0, "sum0 should return 0");
      std::cout << partial_apply(sum1)(1) << std::endl; // 1
      std::cout << partial_apply(sum2)(1)(1) << std::endl; // 2
      std::cout << partial_apply(sum3)(1)(1)(1) << std::endl; // 3
      static_assert(partial_apply(sum3)(1)(1)(1) == 3, "sum3 should return 3");
      std::cout << partial_apply(sum10)(1)(1,1)(1,1,1)(1,1,1,1) << std::endl; // 10
      //partial_apply(sum1)(1)(1); // fails static assert
      auto partiallyApplied = partial_apply(sum3)(1)(1);
      std::function<int(int)> finish_applying = partiallyApplied;
      std::cout << std::boolalpha << (finish_applying(1) == 3) << std::endl; // true
      
      auto plus2 = partial_apply(sum3)(1)(1);
      std::cout << std::boolalpha << (plus2(1) == 3) << std::endl; // true
      std::cout << std::boolalpha << (plus2(3) == 5) << std::endl; // true
      

      【讨论】:

        【解决方案8】:

        Currying 是一种将接受多个参数的函数简化为一系列嵌套函数的方法,每个函数都有一个参数:

        full = (lambda a, b, c: (a + b + c))
        print full (1, 2, 3) # print 6
        
        # Curried style
        curried = (lambda a: (lambda b: (lambda c: (a + b + c))))
        print curried (1)(2)(3) # print 6
        

        Currying 很好,因为您可以定义简单地包装其他具有预定义值的函数的函数,然后传递简化的函数。 C++ STL binders 在 C++ 中提供了这个实现。

        【讨论】:

        • > C++ STL 绑定器在 C++ 中提供了一个实现。是的,但仅适用于一元和二元函数...
        • 和损坏的 c++03(当前 c++)绑定器不适用于具有引用参数的函数。他们根本无法处理引用到引用的问题。增强粘合剂更智能
        • @ugasoft B-但是一元运算就像在发酵葡萄汁中稀释葡萄酒困惑
        【解决方案9】:

        我也使用可变参数模板实现了柯里化(参见 Julian 的回答)。但是,我没有使用递归或std::function。注意:它使用了许多 C++14 功能。

        提供的示例(main 函数)实际上在编译时运行,证明柯里化方法不会胜过编译器的基本优化。

        代码可以在这里找到:https://gist.github.com/Garciat/c7e4bef299ee5c607948

        使用此帮助文件:https://gist.github.com/Garciat/cafe27d04cfdff0e891e

        代码仍然需要(很多)工作,我可能很快也可能不会很快完成。无论哪种方式,我都会在此处发布此内容以供将来参考。

        在链接失效的情况下发布代码(尽管它们不应该):

        #include <type_traits>
        #include <tuple>
        #include <functional>
        #include <iostream>
        
        // ---
        
        template <typename FType>
        struct function_traits;
        
        template <typename RType, typename... ArgTypes>
        struct function_traits<RType(ArgTypes...)> {
            using arity = std::integral_constant<size_t, sizeof...(ArgTypes)>;
        
            using result_type = RType;
        
            template <size_t Index>
            using arg_type = typename std::tuple_element<Index, std::tuple<ArgTypes...>>::type;
        };
        
        // ---
        
        namespace details {
            template <typename T>
            struct function_type_impl
              : function_type_impl<decltype(&T::operator())>
            { };
        
            template <typename RType, typename... ArgTypes>
            struct function_type_impl<RType(ArgTypes...)> {
                using type = RType(ArgTypes...);
            };
        
            template <typename RType, typename... ArgTypes>
            struct function_type_impl<RType(*)(ArgTypes...)> {
                using type = RType(ArgTypes...);
            };
        
            template <typename RType, typename... ArgTypes>
            struct function_type_impl<std::function<RType(ArgTypes...)>> {
                using type = RType(ArgTypes...);
            };
        
            template <typename T, typename RType, typename... ArgTypes>
            struct function_type_impl<RType(T::*)(ArgTypes...)> {
                using type = RType(ArgTypes...);
            };
        
            template <typename T, typename RType, typename... ArgTypes>
            struct function_type_impl<RType(T::*)(ArgTypes...) const> {
                using type = RType(ArgTypes...);
            };
        }
        
        template <typename T>
        struct function_type
          : details::function_type_impl<typename std::remove_cv<typename std::remove_reference<T>::type>::type>
        { };
        
        // ---
        
        template <typename Args, typename Params>
        struct apply_args;
        
        template <typename HeadArgs, typename... Args, typename HeadParams, typename... Params>
        struct apply_args<std::tuple<HeadArgs, Args...>, std::tuple<HeadParams, Params...>>
          : std::enable_if<
                std::is_constructible<HeadParams, HeadArgs>::value,
                apply_args<std::tuple<Args...>, std::tuple<Params...>>
            >::type
        { };
        
        template <typename... Params>
        struct apply_args<std::tuple<>, std::tuple<Params...>> {
            using type = std::tuple<Params...>;
        };
        
        // ---
        
        template <typename TupleType>
        struct is_empty_tuple : std::false_type { };
        
        template <>
        struct is_empty_tuple<std::tuple<>> : std::true_type { };
        
        // ----
        
        template <typename FType, typename GivenArgs, typename RestArgs>
        struct currying;
        
        template <typename FType, typename... GivenArgs, typename... RestArgs>
        struct currying<FType, std::tuple<GivenArgs...>, std::tuple<RestArgs...>> {
            std::tuple<GivenArgs...> given_args;
        
            FType func;
        
            template <typename Func, typename... GivenArgsReal>
            constexpr
            currying(Func&& func, GivenArgsReal&&... args) :
              given_args(std::forward<GivenArgsReal>(args)...),
              func(std::move(func))
            { }
        
            template <typename... Args>
            constexpr
            auto operator() (Args&&... args) const& {
                using ParamsTuple = std::tuple<RestArgs...>;
                using ArgsTuple = std::tuple<Args...>;
        
                using RestArgsPrime = typename apply_args<ArgsTuple, ParamsTuple>::type;
        
                using CanExecute = is_empty_tuple<RestArgsPrime>;
        
                return apply(CanExecute{}, std::make_index_sequence<sizeof...(GivenArgs)>{}, std::forward<Args>(args)...);
            }
        
            template <typename... Args>
            constexpr
            auto operator() (Args&&... args) && {
                using ParamsTuple = std::tuple<RestArgs...>;
                using ArgsTuple = std::tuple<Args...>;
        
                using RestArgsPrime = typename apply_args<ArgsTuple, ParamsTuple>::type;
        
                using CanExecute = is_empty_tuple<RestArgsPrime>;
        
                return std::move(*this).apply(CanExecute{}, std::make_index_sequence<sizeof...(GivenArgs)>{}, std::forward<Args>(args)...);
            }
        
        private:
            template <typename... Args, size_t... Indices>
            constexpr
            auto apply(std::false_type, std::index_sequence<Indices...>, Args&&... args) const& {
                using ParamsTuple = std::tuple<RestArgs...>;
                using ArgsTuple = std::tuple<Args...>;
        
                using RestArgsPrime = typename apply_args<ArgsTuple, ParamsTuple>::type;
        
                using CurryType = currying<FType, std::tuple<GivenArgs..., Args...>, RestArgsPrime>;
        
                return CurryType{ func, std::get<Indices>(given_args)..., std::forward<Args>(args)... };
            }
        
            template <typename... Args, size_t... Indices>
            constexpr
            auto apply(std::false_type, std::index_sequence<Indices...>, Args&&... args) && {
                using ParamsTuple = std::tuple<RestArgs...>;
                using ArgsTuple = std::tuple<Args...>;
        
                using RestArgsPrime = typename apply_args<ArgsTuple, ParamsTuple>::type;
        
                using CurryType = currying<FType, std::tuple<GivenArgs..., Args...>, RestArgsPrime>;
        
                return CurryType{ std::move(func), std::get<Indices>(std::move(given_args))..., std::forward<Args>(args)... };
            }
        
            template <typename... Args, size_t... Indices>
            constexpr
            auto apply(std::true_type, std::index_sequence<Indices...>, Args&&... args) const& {
                return func(std::get<Indices>(given_args)..., std::forward<Args>(args)...);
            }
        
            template <typename... Args, size_t... Indices>
            constexpr
            auto apply(std::true_type, std::index_sequence<Indices...>, Args&&... args) && {
                return func(std::get<Indices>(std::move(given_args))..., std::forward<Args>(args)...);
            }
        };
        
        // ---
        
        template <typename FType, size_t... Indices>
        constexpr
        auto curry(FType&& func, std::index_sequence<Indices...>) {
            using RealFType = typename function_type<FType>::type;
            using FTypeTraits = function_traits<RealFType>;
        
            using CurryType = currying<FType, std::tuple<>, std::tuple<typename FTypeTraits::template arg_type<Indices>...>>;
        
            return CurryType{ std::move(func) };
        }
        
        template <typename FType>
        constexpr
        auto curry(FType&& func) {
            using RealFType = typename function_type<FType>::type;
            using FTypeArity = typename function_traits<RealFType>::arity;
        
            return curry(std::move(func), std::make_index_sequence<FTypeArity::value>{});
        }
        
        // ---
        
        int main() {
            auto add = curry([](int a, int b) { return a + b; });
        
            std::cout << add(5)(10) << std::endl;
        }
        

        【讨论】:

          【解决方案10】:

          这些链接是相关的:

          Wikipedia 上的 Lambda 微积分页面有一个明确的柯里化示例
          http://en.wikipedia.org/wiki/Lambda_calculus#Motivation

          本文讨论 C/C++ 中的柯里化
          http://asg.unige.ch/site/papers/Dami91a.pdf

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-12-30
            • 2010-11-29
            • 1970-01-01
            • 2011-08-06
            • 1970-01-01
            • 2015-02-23
            • 1970-01-01
            相关资源
            最近更新 更多