Lambda expressions


#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>

using namespace std;

int main()
{
    vector<int> v;
    for(int i = 0; i < 10; ++i)
        v.push_back(i);

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });
    cout << endl;
    return 0;
}


$ g++ --version
g++ (SUSE Linux) 4.5.0 20100604 [gcc-4_5-branch revision 160292]

$ g++ -std=gnu++0x l.cpp
$ ./a.out
0 1 2 3 4 5 6 7 8 9 
$


[]        lambda-introducer
(int n)   lambda parameter declaration
{ ... }   compound statement



Lambda expressons define classes and construct objects.
The program above is equivalent to:


#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>

using namespace std;

struct LambdaFunctor
{
    void operator() (int n) const { cout << n << " "; }
};

int main()
{
    vector<int> v;
    for(int i = 0; i < 10; ++i)
        v.push_back(i);

    for_each(v.begin(), v.end(), LambdaFunctor());
    cout << endl;
    return 0;
}



The expression can contain multiply statements


#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>

using namespace std;

int main()
{
    vector<int> v;
    for(int i = 0; i < 10; ++i)
        v.push_back(i);

    for_each(v.begin(), v.end(), [](int n) { 
                    cout << n ;
                    if ( n % 2 )
                        cout << ":odd ";
                    else
                        cout << ":even ";
                                           });
                        cout << endl;
    return 0;
}


0:even 1:odd 2:even 3:odd 4:even 5:odd 6:even 7:odd 8:even 9:odd


If the lambda expession has return value, than the return type
is automatically deduced from that expression:


int main()
{
    vector<int> v;
    for(int i = 0; i < 10; ++i)
        v.push_back(i);

    deque<int> d;

    transform(v.begin(), v.end(), front_inserter(d), [](int n) {return n*n;} );

    for_each(d.begin(), d.end(), [](int n) { cout << n << " "; });
                        
    cout << endl;
    return 0;
}


81 64 49 36 25 16 9 4 1 0



Sometimes it is not easy to deduce the return type. We can explicitly
specify the return type.



#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>
#include <deque>

using namespace std;

int main()
{
    vector<int> v;
    for(int i = 0; i < 10; ++i)
        v.push_back(i);

    deque<double> d;

    transform(v.begin(), v.end(), front_inserter(d),
                    [](int n) -> double  { return n / 2.0; } );

    for_each(d.begin(), d.end(), [](double n) { cout << n << " "; });
                                
    cout << endl;
    return 0;
}


4.5 4 3.5 3 2.5 2 1.5 1 0.5 0


-> double    is the lambda return type clause
             (it is on the right, because [] should be on left for parsing)



[] means, that the lambda is "stateless", but we can "capture" local variables


#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>

using namespace std;

int main()
{
    vector<int> v;
    for(int i = 0; i < 10; ++i)
        v.push_back(i);

    int x = 0;
    int y = 0;
    cin >> x >> y;

    v.erase( remove_if(v.begin(),
                       v.end(),
                       [x,y](int n) { return x < n && n < y; }
                      ),  
             v.end()
           );  

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });
                                
    cout << endl;
    return 0;
}


3 6
0 1 2 3 6 7 8 9


We passed the x and y parameters by value. Basically it is equivalent:


struct LambdaFunctor
{
public:
    LambdaFunctor(int a, int b) : m_a(a), m_b(b) { }
    bool operator()(int n) const { return m_a < n && n < m_b; }
private:
    int m_a;
    int m_b;
};

// ...

 v.erase( remove_if(v.begin(),v.end(),LambdaFunctor(x,y)), v.end());


The x and y parameters are copied and being stored in the function object.
We cannot modify the captured values because the operator() in functor is
const.It is a "real" copy, therefore teh modification of x and y is not 
reflected inside the lambda.



[=]    capture-default lambda introducer. Captures all locals.


#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>

using namespace std;

int main()
{
    vector<int> v;
    for(int i = 0; i < 10; ++i)
        v.push_back(i);

    int x = 0;
    int y = 0;
    cin >> x >> y;

    v.erase( remove_if(v.begin(),
                       v.end(),
                       [=](int n) { return x < n && n < y; }
                      ),  
             v.end()
           );  

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });
                                
    cout << endl;
    return 0;
}


3 6
0 1 2 3 6 7 8 9



Lambda expressions are const by default but we can manage to modify x and y.
iNote, that modification of prev does not update local prev variable.


#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>

using namespace std;

int main()
{
    vector<int> v;
    for(int i = 0; i < 10; ++i)
        v.push_back(i);

    int prev = 0;
    for_each(v.begin(), v.end(), [=](int& r) mutable { 
                            const int oldr = r;
                            r *= prev;
                            prev = oldr;            
                         });

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });
                                
    cout << "prev = " << prev << endl;
    return 0;
}


0 0 2 6 12 20 30 42 56 72 prev = 0


Capture by reference


#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>

using namespace std;

int main()
{
    vector<int> v;
    for(int i = 0; i < 10; ++i)
        v.push_back(i);

    int prev = 0;
    for_each(v.begin(), v.end(), [&prev](int& r) mutable { 
                                    const int oldr = r;
                                    r *= prev;
                                    prev = oldr;            
                             });

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });
                                
    cout << "prev = " << prev << endl;
    return 0;
}


0 0 2 6 12 20 30 42 56 72 prev = 9


[&]         Capture everything by reference

[=, &x, &y] Capture everything by value but x and y which capured by reference

[&, x, y]   Capture everything by reference but x and y which capured by value




#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>

using namespace std;

struct X
{
    int s;
    vector<int> v;
    void print() const
    {
        for_each(v.begin(), v.end(), [](int n) { cout << n*s << " "; });
    }
};

int main()
{
    X x;        
    x.s = 2;
    for(int i = 0; i < 10; ++i)
        x.v.push_back(i);

    x.print();
    return 0;
}


$ g++ -std=gnu++0x l10.cpp
l10.cpp: In lambda function:
l10.cpp:15:60: error: ‘this’ was not captured for this lambda function



#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>

using namespace std;

struct X
{
    int s;  
    vector<int> v;
    void print() const
    {
        for_each(v.begin(), v.end(), [this](int n) { cout << n*s << " "; });
    }      
};                  

int main()
{
    X x;        
    x.s = 2;
    for(int i = 0; i < 10; ++i)
        x.v.push_back(i);
                        
    x.print();
    return 0;
}                        


- this always captured by value
- you can implicitly capture this with [=]



We can define "nullary" lambdas


#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>

using namespace std;

int main()
{
    vector<int> v;
    int i = 0;

    generate_n(back_inserter(v), 10, [&] { return i++; } );

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });
    return 0;
}


0 1 2 3 4 5 6 7 8 9



Or you can write [&]() { return i++; }

Lambdas can be stored in tr1::functional


#include <algorithm>
#include <functional>
#include <iostream>
#include <ostream>
#include <vector>

using namespace std;
//using namespace std::tr1;

void doit(const vector<int>& v, const function<void (int)>& f)
{
    for_each(v.begin(), v.end(), f);
    cout << endl;
}      

int main()
{
    vector<int> v;
    int i = 0;

    generate_n(back_inserter(v), 10, [&] { return i++; } );

    doit(v, [](int n) { cout << n << " "; });

    const function<void (int)>& ff = [](int n) { cout << n << " "; };
    doit(v, ff);

    return 0;
}


0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 7 8 9