C++0x lambda functions (ALDAT).

© Harry Broeders.

Deze pagina is bestemd voor studenten van de Haagse Hogeschool - Academie voor Technology, Innovation & Society Delft groep ECV.

De C++ standaard staat op het punt om vernieuwd te worden. Zie: http://en.wikipedia.org/wiki/C%2B%2B0x . In deze nieuwe versie van de standaard zijn lambda expressies en closures opgenomen, zie: Lambda_Expressions_and_Closures.pdf

Voorbeelden.

De voorbeelden zijn getest met Microsoft Visual C++ 2010 Express Edition. Deze versie van Visual C++ bied support voor lambda functies, zie: http://msdn.microsoft.com/en-us/library/dd293608.aspx

Voorbeeld uit bovengenoemd Microsoft artikel.

lambda.cpp

Voorbeeld met find_if.

find_if_lambda.cpp

#include <iostream>
#include <list>
#include <algorithm>
#include <functional>

using namespace std;

bool ispos(int i) {
   return i>=0;
}

class IsPos {
public:
   bool operator()(int i) const {
      return i>=0;
   }
};

int main() {
   list<int> l;
   l.push_back(-3);
   l.push_back(-4);
   l.push_back(3);
   l.push_back(4);
// Zoeken met behulp van een functie als zoekvoorwaarde.
   auto r=find_if(l.begin(), l.end(), ispos);
   if (r!=l.end())
      cout<<"Het eerste positieve element is: "<<*r<<endl;
// Zoeken met behulp van een functie-object als zoekvoorwaarde.
   r=find_if(l.begin(), l.end(), IsPos());
   if (r!=l.end())
      cout<<"Het eerste positieve element is: "<<*r<<endl;
// Zoeken met behulp van een standaard functie-object als zoekvoorwaarde.
   r=find_if(l.begin(), l.end(), bind2nd(greater_equal<int>(),0));
   if (r!=l.end())
      cout<<"Het eerste positieve element is: "<<*r<<endl;
// Zoeken met behulp van een lambda functie-object als zoekvoorwaarde. 
   r=find_if(l.begin(), l.end(), [](int i) {
      return i>=0;
   });
   if (r!=l.end())
      cout<<"Het eerste positieve element is: "<<*r<<endl;
   cin.get();
   return 0;
}

Voorbeeld met for_each.

for_each_lambda.cpp

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>

using namespace std;

int main() {
   vector<int> v;
   v.push_back(-3);
   v.push_back(-4);
   v.push_back(3);
   v.push_back(4);
   ostream_iterator<int> iout(cout, " ");
   copy(v.begin(), v.end(), iout);
   cout<<endl;
   for_each(v.begin(), v.end(), [](int i) { 
      cout<<i<<" "<<i<<" ";
   });
   cout<<endl;
   cin.get();
   return 0;
}

Voorbeeld met transform.

transform_lambda.cpp

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

using namespace std;

int telop(int i, int j) {
   return i+j;
}

int main() {
   vector<int> v;
   v.push_back(-3);
   v.push_back(-4);
   v.push_back(3);
   v.push_back(4);
   vector<int> w;
   w.push_back(1);
   w.push_back(2);
   w.push_back(3);
   w.push_back(4);
   ostream_iterator<int> iout(cout, " ");
   copy(v.begin(), v.end(), iout);
   cout<<endl;
// Bewerking opgeven met een functie.
   transform(v.begin(), v.end(), w.begin(), v.begin(), telop);
   copy(v.begin(), v.end(), iout);
   cout<<endl;
// Bewerking opgeven met standaard functie-objecten.
   transform(v.begin(), v.end(), w.begin(), v.begin(), plus<int>());
   copy(v.begin(), v.end(), iout);
   cout<<endl;
// Bewerking opgeven met boost lambda functie-objecten. 
   transform(v.begin(), v.end(), w.begin(), v.begin(), [](int i1, int i2) {
      return i1 + i2;
   });
   copy(v.begin(), v.end(), iout);
   cout<<endl;
   cin.get();
   return 0;
}

Voorbeeld met remove_if.

remove_lambda.cpp

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

using namespace std;

int main() {
   vector<int> v;
   for (int i(0); i<10; ++i) {
      v.push_back(i*i);
   }
   ostream_iterator<int> out(cout, " ");
   cout<<"Na initialisatie:"<<endl;
   copy(v.begin(), v.end(), out);
   auto end(remove_if(v.begin(), v.end(), [](int i) { 
      return !(i%2);
   }));
   cout<<endl<<"Na remove (tot returned iterator):"<<endl;
   copy(v.begin(), end, out);
   cout<<endl<<"Na remove (hele vector):"<<endl;
   copy(v.begin(), v.end(), out);
   v.erase(end, v.end());
   cout<<endl<<"Na erase (hele vector):"<<endl;
   copy(v.begin(), v.end(), out);
   cin.get();
   return 0;
}

Voorbeeld for_each met aanroep van een memberfunctie.

mem_func_lamda.cpp

#include <iostream>
#include <list>
#include <algorithm>
#include <functional>

using namespace std;

class Hond {
public:
   virtual ~Hond() {
   }
   virtual void blaf() const =0;
};

class Tekkel: public Hond {
public:
   virtual void blaf() const {
      cout<<"Kef kef ";
   }
};

class StBernard: public Hond {
public:
   virtual void blaf() const {
      cout<<"Woef woef ";
   }
};

int main() {
   list<Hond*> kennel;
   kennel.push_back(new Tekkel);
   kennel.push_back(new StBernard);
   kennel.push_back(new Tekkel);
// aanroepen van een memberfunctie via mem_fun
   for_each(kennel.begin(), kennel.end(), mem_fun(&Hond::blaf));
   cout<<endl;
// aanroepen van een memberfunctie via lambda functie 
   for_each(kennel.begin(), kennel.end(), [](Hond* h) {
      h->blaf();
   });
   cout<<endl;
   cin.get();
   return 0;
}

mem_func2_lamda.cpp

#include <iostream>
#include <list>
#include <algorithm>
#include <string>

using namespace std;

class Hond {
private:
   string n;
public:
   virtual ~Hond() {
   }
   string naam() const {
      return n;
   }
   void geefNaam(const string& s) {
       n=s;
   }
   virtual string blaf() const =0;
};

class Tekkel: public Hond {
public:
   virtual string blaf() const {
      return "Kef kef ";
   }
};

class StBernard: public Hond {
public:
   virtual string blaf() const {
      return "Woef woef ";
   }
};

// handige template:
template<typename Container, typename Functor> void for_all(Container& c, Functor f) {
   for_each(c.begin(), c.end(), f);
}

int main() {
   list<Hond*> kennel;
   kennel.push_back(new Tekkel);
   kennel.push_back(new StBernard);
   kennel.push_back(new Tekkel);
// blaffen zonder naam
   for_all(kennel, [](Hond* h) {
      cout<<h->blaf();
   });
   cout<<endl;
// alle honden heten "Fikkie" 
   for_all(kennel, [](Hond* h) {
      h->geefNaam("Fikkie");
   });
// blaffen met naam
   for_all(kennel, [](Hond* h) {
      cout<<h->naam()<<":"<<h->blaf();
   });
   cout<<endl;
   cin.get();
   return 0;
}

Voorbeeld galgje met gebruik van set en for_each.

galgje1_lambda.cpp

#include <iostream>
#include <string>
#include <set>
#include <algorithm>
#include <iterator>

using namespace std;

int main() {
   string w("galgje");
   set<char> letters, geraden;
   copy(w.begin(), w.end(), inserter(letters, letters.begin()));
   do {
      for_each(w.begin(), w.end(), [&](char c) {
         if (geraden.count(c))
            cout<<c;
         else
            cout<<'.';
      });
      cout<<endl<<"Raad een letter: ";
      char c;
      cin>>c;
      geraden.insert(c);
   }
   while (!includes(geraden.begin(), geraden.end(), letters.begin(), letters.end()));
   cout<<"Je hebt het woord "<<w<<" geraden."<<endl;
   cin.get(); cin.get();
   return 0;
}

De notatie [&] geeft aan dat alle variabelen die niet in de lambda functie gedefinieerd zijn maar wel in de lambda functie gebruikt worden (in dit geval de set geraden) als referentie aan de lambda functie "gekoppeld" moeten worden. Een verzameling "gekoppelde" variabelen wordt een closure genoemd.

De set geraden kan nu gewoon lokaal in de functie main worden gedefinieerd. De lambda functie die in de for_each als derde parameter wordt ingevuld kan ook op een alternatieve manier worden opgegeven:

galgje1a_lambda.cpp

      for_each(w.begin(), w.end(), [&](char c) {
         cout<<(geraden.count(c)?c:'.');
      });

Voorbeeld galgje met gebruik van string en transform.

galgje3_lambda.cpp

#include <iostream>
#include <string>
#include <set>
#include <algorithm>

using namespace std;

int main() {
   string w("galgje");
   string geraden(w.length(), '.');
   do {
      cout<<geraden<<endl<<"Raad een letter: ";
      char c;
      cin>>c;
      transform(w.begin(), w.end(), geraden.begin(), geraden.begin(), [=](char w_char, char geraden_char) {
         return w_char==c?w_char:geraden_char;
      });
   }
   while (geraden!=w);
   cout<<"Je hebt het woord "<<w<<" geraden."<<endl;
   cin.get(); cin.get();
   return 0;
}

De notatie [=] geeft aan dat alle variabelen die niet in de lambda functie gedefinieerd zijn maar wel in de lambda functie gebruikt worden (in dit geval het karakter c) als waarde aan de lambda functie "gekoppeld" moeten worden. Een verzameling "gekoppelde" waarden wordt een closure genoemd.

In plaats van de operator ?: kunnen we in de lambda functie ook een if else statement gebruiken. Omdat de lambda functie dan uit meerdere statements bestaat moeten we het returntype opgeven door middel van -> char.

galgje3a_lambda.cpp

#include <iostream>
#include <string>
#include <set>
#include <algorithm>

using namespace std;

int main() {
   string w("galgje");
   string geraden(w.length(), '.');
   do {
      cout<<geraden<<endl<<"Raad een letter: ";
      char c;
      cin>>c;
      transform(w.begin(), w.end(), geraden.begin(), geraden.begin(), [=](char w_char, char geraden_char) -> char {
         if (w_char==c)
            return w_char;
         else
            return geraden_char;
      });
   }
   while (geraden!=w);
   cout<<"Je hebt het woord "<<w<<" geraden."<<endl;
   cin.get(); cin.get();
   return 0;
}

Nog een voorbeeld met for_each.

for_each_som_even_lambda.cpp

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>

using namespace std;

int main() {
   vector<int> v;
   for (int i(1); i<11; ++i)
      v.push_back(i);
   ostream_iterator<int> iout(cout, " ");
   copy(v.begin(), v.end(), iout);
   cout<<endl;
   int somEven = 0;
   for_each(v.begin(), v.end(), [&somEven](int i) {
      if (i%2 == 0)
         somEven += i;
   });
   cout<<"Som van de even getallen is: "<<somEven<<endl;
   cin.get();
   return 0;
}

De notatie [&somEven] geeft aan dat de variabele somEven die niet in de lambda functie gedefinieerd is maar wel in de lambda functie gebruikt wordt als referentie aan de lambda functie "gekoppeld" moeten worden. Als alternatief kan ook de notatie [&] gebruikt worden.