C ++ 11: lambda, curry

I have the following code. Can you explain to me how this works?

template<typename Function, typename... Arguments> auto curry(Function func, Arguments... args) { return [=](auto... rest) { return func(args..., rest...); }; } int main() { auto add = [](auto x, auto y) { return x + y; }; auto add4 = curry(add, 4); std::cout << add4(3) << '\n'; //output: 7. (Ok) } 
+5
source share
1 answer

Firstly, you should know what currying is, or in your question this is, in particular, the case of a private application (which includes currying, but a little different).

Basically, this means reducing a function with a certain number of parameters to create another function with some fixed fixed parameter values.

I reviewed your original add(x,y) function, which has two parameters x and y. You reduce the addition of the arity function by setting x to 4 and creating a smaller add4(y) function such as

add4 (y) = add (x, y), where x = 4

Now, how is this achieved in your C ++ code? With the help of variational patterns , variational functions and lambda functions .

Lambda functions are in C ++ with C ++ 11. In fact, this is an anonymous function created on the fly, which can be stored in a variable. You create add as lambda in main() :

 auto add = [](auto x, auto y) { return x + y; }; 

One recognizes the lambda as the template [] (list-of-arguments) {function-body};

Note: [] not always empty, see "capturing variables" there , we will return to it later.

Now the purpose of the curry function is to take one of these lambdas func functions and a certain number of values ​​as arguments and define a new function by assigning the value, in order, before the first func arguments.

The "certain number of arguments" mechanism is allowed by the variadic template Arguments... args argument, which allows you to call a template with any number of types as template parameters (if they are known as compilation time). Therefore, in our case, the argument passed is 4, so Arguments... args will be replaced by int , and the curry instance will accept lambda and int as parameters.

If we look at the curry code, we will see that this is only the lambda function itself (it is a variational function , just like printf() ), the sole purpose of which is to concatenate arguments whose value is already corrected when creating the template instance ( args... ) and those whose value is passed as arguments to the curried ( rest... ) function.

The sign [=] is a special capture for the lambda function, which allows you to use all local variables in the body of the function (here it allows you to use args ).

So, to wrap this up, here's what happens in your main function:

  • You create a variable called add that contains the lambda function by taking two arguments and adding them.
  • When you call curry(add,4) , you instantiate the curry template.
    • The first parameter to the Function template is the add type (lambda that takes two int and returns int )
    • The second variational parameters contain only one type: type 4 , i.e. int

This curry function looks like this:

 curry( (int,int)->(int) func, int arg){ return [=](auto... rest) {return func(arg, rest...);}; } 

Note that you never see these types due to auto and template type inference.

  1. Then you call this instance with func = add and arg = 4

  2. You store the result of this instance in add4 , which is now a lambda that takes one parameter. You can then call add4 with argument 3 as the argument ( rest... equals 3), which will then call add(4,3) and return 7.

Note that technically you can try calling add4 more than one argument, as the curried function is a variational function. The compiler will fail only when it finds out that it does not have a place for these additional arguments when calling add (see here )

+10
source

All Articles