It's common knowledge that Functional Programming is spreading like a wildfire in mainstream languages. Latest promoted languages: Java 8 and C++, both of which now support lambdas. So, let the lambdas begin! and may the fun be ever on your side. The same text is available in slides form on Slideshare. This blog post and the talk/slides are inspired by JSON inventor Douglas Crockford.
Write an Identity function that takes an argument and returns the same argument.
Write an Identity function that takes an argument and returns the same argument.
auto Identity = [](auto x) { return x; }; Identity(3); // 3Write 3 functions add, sub, and mul that take 2 parameters each 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; }; int mul (int x, int y) { return x * y; };Write a function, identityf, that takes an argument and returns an inner class object that returns that argument.
auto identityf = [](auto x) { class Inner { int x; public: Inner(int i): x(i) {} int operator() () { return x; } }; return Inner(x); }; identityf(5)(); // 5Write a function, identityf, that takes an argument and returns a function that returns that argument.
auto identityf = [](auto x) { return [=]() { return x; }; }; identityf(5)(); // 5Lambda != Closure
- A lambda is just a syntax sugar to define anonymous functions and function objects.
- A closure in C++ is a function object which closes over the environment in which it was created. The line #2 above defines a closure that closes over x.
- A lambda is a syntactic construct (expression), and a closure is a run-time object, an instance of a closure type.
- C++ closures do not extend the lifetime of their context. (If you need this use shared_ptr)
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(); // 1Write a function that adds from two invocations.
auto addf = [](auto x) { return [=](auto y) { return x+y; }; }; addf(5)(4); // 9Write a function swap that swaps the arguments of a binary function.
auto swap =[](auto binary) { return [=](auto x, auto y) { return binary(y, x); }; }; swap(sub)(3, 2); // -1Write a function twice that takes a binary function and returns a unary function that passes its argument to the binary function twice.
auto twice =[](auto binary) { return [=](auto x) { return binary(x, x); }; }; twice(add)(11); // 22Write a function that takes a binary function and makes it callable with two invocations.
auto applyf = [](auto binary) { return [=](auto x) { return [=](auto y) { return binary(x, y); }; }; }; applyf(mul)(3)(4); // 12Write a function that takes a function and an argument and returns a function that takes the second argument and applies the function.
auto curry = [](auto binary, auto x) { return [=](auto y) { return binary(x, y); }; }; curry(mul, 3)(4); // 12Currying (schönfinkeling)
- Currying is the technique of transforming a function that takes multiple arguments in such a way that it can be called as a chain of functions, each with a single argument.
- In lambda calculus functions take a single argument only.
- Must know Currying to understand Haskell.
- Currying != Partial function application
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); //10Without creating a new function show 3 ways to create the inc function.
auto inc = curry(add, 1); auto inc = addf(1); auto inc = applyf(add)(1);Write a function composeu that takes two unary functions and returns a unary function that calls them both.
auto composeu =[](auto f1, auto f2) { return [=](auto x) { return f2(f1(x)); }; }; composeu(inc1, curry(mul, 5))(3) // 20Write a function that returns a function that allows a binary function to be called exactly 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); // 7Write a function that takes a binary function and returns a function that takes two arguments and a 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; });Write 3 functions:
- unit – same as Identityf
- stringify – that stringifies its argument and applies unit to it
- bind – that takes a result of unit and returns a function that takes a callback and returns the result of callback applied 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()); }; };Then verify.
std::cout << "Left Identity " << stringify(15)() << "==" << bind(unit(15))(stringify)() << std::endl; std::cout << "Right Identity " << stringify(5)() << "==" << bind(stringify(5))(unit)() << std::endl;Why are unit and bind special? Read more about them here.
Comments
fromto(0, 10); would return the lambda and from(0, 10)(); will only print 0.
Isn't that the case ?
@Igor: Are you referring to identityf::Inner? For simplicity, I'm using just an int member.
The Lambda != Closure section is confusing though.
"A lambda is just an anonymous function." -- I understand the desire to make the explanation as simple as possible, but here it happens at the cost of correctness. "Just functions" don't carry state. Perhaps "A lambda is just a syntax sugar to define anonymous functions and function objects"?
"Not all closures are lambdas and not all lambdas are closures." -- sounds weird. A lambda is a syntactic construct (expression), and a closure is a run-time object, an instance of a closure type. Neither can "be" the other.
auto identityf = [](auto x)
{
class inner
{
public:
using X = decltype(x);
X mx;
inner(X x): mx(x) {}
X operator()() { return mx; }
};
return inner{x};
};
(you version of inner uses int)
Jumping on the back of @joaof's comment: Using decltype to ascertain the type of auto x allows for using an interesting pattern of, what the D Language community calls, Voldemort types. Walter Bright wrote an interesting article about it which you can read here.
Interesting to note that, unlike in D, we can later retrieve the type of Inner outside of the lambda:
auto foo = identifyf(5);
foo(); //5
using Foo = decltype(foo);
Foo bar(10);
bar(); //10
Live code: http://melpon.org/wandbox/permlink/mfteteOhqXrZGxVS
I am quite sure about the rest: http://ideone.com/h2ZRs3 :) I think, maybe, that I was confusing something in your example contra mine.
/Dave
and example?
and example?
gclub