New features of lambdas in C ++ 14
- Transfer
- Tutorial
Everyone knows that functional programming is spreading fast in modern programming languages. Recent examples are Java 8 and C ++, both of which now support lambda functions.
So, let's begin (and may the fun come with us). This text is also available as slides on Slideshare . The author of this article was inspired by JSON creator Douglas Crockford .
An Identity function that takes an argument and returns the same argument:
Translator's Note : New compared to C ++ 11 is the ability to not specify type names.
Functions add, sub and mul, which take two arguments and return their sum, difference and product, respectively:
The identityf function, which takes an argument and returns an instance of the inner class, when called, the original argument will be returned:
Another identityf implementation that returns not an object, but a function (yes, now you can return functions):
Note: lambda functions ≠ closure:
A function that returns a generator function that returns numbers from a given interval:
A function that takes numbers one at a time and adds them:
A function that swaps the arguments of another function:
A twice function that takes a binary function and returns a unary function that passes an argument to the binary twice:
A function that takes a binary function and returns a function that takes two arguments in turn:
A curry function that takes a binary function and an argument and returns a function that takes a second argument:
Note: Currying (schrying, schönfinkeling) is the transformation of a function that takes several arguments into a chain of functions that take one argument.
Partial use of the function:
Three options, how to get a function that adds one to the argument without creating a new function:
Implementation of composition of functions:
A function that takes a binary function and modifies it so that it can be called only once:
A function that takes a binary function and returns a function that takes two arguments and callback:
Finally, we write the following three functions:
Now make sure that everything works:
What is so interesting about the unit and bind functions? The fact is that this is a monad .
Read the second post from the author’s blog series .
So, let's begin (and may the fun come with us). This text is also available as slides on Slideshare . The author of this article was inspired by JSON creator Douglas Crockford .
An Identity function that takes an argument and returns the same argument:
auto Identity = [](auto x) {
return x;
};
Identity(3); // 3
Translator's Note : New compared to C ++ 11 is the ability to not specify type names.
Functions add, sub and mul, which take two arguments and return their sum, difference and product, respectively:
auto add = [](auto x, auto y) {
return x + y;
};
auto sub = [](auto x, auto y) {
return x - y;
};
auto mul = [](auto x, auto y) {
return x * y;
};
The identityf function, which takes an argument and returns an instance of the inner class, when called, the original argument will be returned:
auto identityf = [](auto x) {
class Inner {
int x;
public: Inner(int i): x(i) {}
int operator() () { return x; }
};
return Inner(x);
};
identityf(5)(); // 5
Another identityf implementation that returns not an object, but a function (yes, now you can return functions):
auto identityf = [](auto x) {
return [=]() { return x; };
};
identityf(5)(); // 5
Note: lambda functions ≠ closure:
- Lambda is just an anonymous function.
- A closure is a function that uses objects from the environment in which it was declared. In the second line of the previous example, the equal sign denotes "capture of context."
- Not all lambdas are closures, and not all closures are lambdas.
- Closures in C ++ are common objects that you can call.
- Closures do not extend the life of the objects they use (for this, use shared_ptr).
A function that returns a generator function that returns numbers from a given interval:
auto fromto = [](auto start, auto finish) {
return [=]() mutable {
if(start < finish)
return start++;
else
throw std::runtime_error("Complete");
};
};
auto range = fromto(0, 10);
range(); // 0
range(); // 1
A function that takes numbers one at a time and adds them:
auto addf = [](auto x) {
return [=](auto y) {
return x+y;
};
};
addf(5)(4); // 9
A function that swaps the arguments of another function:
auto swap =[](auto binary) {
return [=](auto x, auto y) {
return binary(y, x);
};
};
swap(sub)(3, 2); // -1
A twice function that takes a binary function and returns a unary function that passes an argument to the binary twice:
auto twice =[](auto binary) {
return [=](auto x) {
return binary(x, x);
};
};
twice(add)(11); // 22
A function that takes a binary function and returns a function that takes two arguments in turn:
auto applyf = [](auto binary) {
return [=](auto x) {
return [=](auto y) {
return binary(x, y);
};
};
};
applyf(mul)(3)(4); // 12
A curry function that takes a binary function and an argument and returns a function that takes a second argument:
auto curry = [](auto binary, auto x) {
return [=](auto y) {
return binary(x, y);
};
};
curry(mul, 3)(4); // 12
Note: Currying (schrying, schönfinkeling) is the transformation of a function that takes several arguments into a chain of functions that take one argument.
- In a λ-analysis, all functions take only one argument.
- You need to understand how currying works in order to learn Haskell.
- Currying is a partial application of a function.
Partial use of the function:
auto addFour = [](auto a, auto b,
auto c, auto d) {
return a+b+c+d;
};
auto partial = [](auto func, auto a, auto b) {
return [=](auto c, auto d) {
return func(a, b, c, d);
};
};
partial(addFour,1,2)(3,4); //10
Three options, how to get a function that adds one to the argument without creating a new function:
auto inc = curry(add, 1);
auto inc = addf(1);
auto inc = applyf(add)(1);
Implementation of composition of functions:
auto composeu =[](auto f1, auto f2) {
return [=](auto x) {
return f2(f1(x));
};
};
composeu(inc1, curry(mul, 5))(3) // (3 + 1) * 5 = 20
A function that takes a binary function and modifies it so that it can be called only once:
auto once = [](auto binary) {
bool done = false;
return [=](auto x, auto y) mutable {
if(!done) {
done = true;
return binary(x, y);
}
else
throw std::runtime_error("once!");
};
};
once(add)(3,4); // 7
A function that takes a binary function and returns a function that takes two arguments and callback:
auto binaryc = [](auto binary) {
return [=](auto x, auto y, auto callbk) {
return callbk(binary(x,y));
};
};
binaryc(mul)(5, 6, inc) // 31
binaryc(mul)(5, 6, [](int a) { return a+1; }); // то же самое
Finally, we write the following three functions:
- unit is the same as identityf;
- stringify - turns its argument into a string and applies unit to it;
- bind - takes the result of unit and returns a function that takes a callback and returns the result of applying it to the result of unit.
auto unit = [](auto x) {
return [=]() { return x; };
};
auto stringify = [](auto x) {
std::stringstream ss;
ss << x;
return unit(ss.str());
};
auto bind = [](auto u) {
return [=](auto callback) {
return callback(u());
};
};
Now make sure that everything works:
std::cout << "Left Identity "
<< stringify(15)()
<< "=="
<< bind(unit(15))(stringify)()
<< std::endl;
std::cout << "Right Identity "
<< stringify(5)()
<< "=="
<< bind(stringify(5))(unit)()
<< std::endl;
What is so interesting about the unit and bind functions? The fact is that this is a monad .
Read the second post from the author’s blog series .