boost::lambda (SOPX3).

© Harry Broeders.

Deze pagina is bestemd voor studenten van de Haagse Hogeschool - TH Rijswijk/Academie voor Engineering groep EH3 C&D.

De boost library is te vinden op http://www.boost.org/. Een eenvoudige uitleg over boost::lambda kun je vinden op: http://www.awprofessional.com/articles/article.asp?p=400651&rl=1. Uitgebreide uitleg kun je vinden op: http://www.boost.org/doc/html/lambda.html.

Voorbeelden.

Deze voorbeelden zijn getest met DevC++. Om deze voorbeelden te compileren moet je het path naar het directory waar je de boost library hebt geplaatst opgeven bij Tools, Compiler Options, tabblad Directories, tabblad C++ Includes. Vergeet niet op de Add button te klikken.

Voorbeeld uit bovengenoemd artikel.

lambda.cpp

Voorbeeld met find_if.

find_if_lambda.cpp

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

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

using namespace std;
using namespace boost::lambda;

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);
   list<int>::iterator r;
// Zoeken met behulp van een functie 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 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 boost lambda functie-object als zoekvoorwaarde. 
   r=find_if(l.begin(), l.end(), _1>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>

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

using namespace std;
using namespace boost::lambda;

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(), cout<<_1<<" "<<_1<<" "); 
   cout<<endl;
   cin.get();
   return 0;
}

Voorbeeld met transform.

transform_lambda.cpp

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

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

using namespace std;
using namespace boost::lambda;

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(), _1+_2);
   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>

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

using namespace std;
using namespace boost::lambda;

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);
   vector<int>::iterator end(remove_if(v.begin(), v.end(), !(_1%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>
#include <boost/lambda/bind.hpp>

using namespace std;
using namespace boost::lambda;

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 bind
   for_each(kennel.begin(), kennel.end(), bind(&Hond::blaf, _1)); 
   cout<<endl;
   cin.get();
   return 0;
}

Het is nu niet meer nodig om de standaard template functie mem_fun te gebruiken omdat de boost::lambda template functie bind ook gebruikt kan worden om de receiver van een memberfunctie te "binden" waardoor een "gewone" functie (eigenlijk functie object) ontstaat dat door for_each kan worden aangeroepen.

Het nadeel van mem_fun is dat mem_fun alleen werkt als de container pointers bevat (als de container objecten bevat moet mem_fun_ref worden gebruikt) en als de memberfunctie geen parameters heeft. Bij het gebruik van bind kan zowel een pointer als een object gebruikt worden om de receiver te binden en kunnen ook eventuele parameters gebonden worden.

mem_func2_lamda.cpp

#include <iostream>
#include <list>
#include <algorithm>
#include <string>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

using namespace std;
using namespace boost::lambda;

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 ";
   }
};

int main() {
   list<Hond*> kennel;
   kennel.push_back(new Tekkel);
   kennel.push_back(new StBernard);
   kennel.push_back(new Tekkel);
// blaffen zonder naam
   for_each(kennel.begin(), kennel.end(), cout<<bind(&Hond::blaf, _1));
   cout<<endl;
// alle honden heten "Fikkie"
   for_each(kennel.begin(), kennel.end(), bind(&Hond::geefNaam, _1, "Fikkie")); 
// blaffen met naam
   for_each(kennel.begin(), kennel.end(), cout<<bind(&Hond::naam, _1)<<":"<<bind(&Hond::blaf, _1));
   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 <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/lambda/if.hpp>

using namespace std;
using namespace boost::lambda;

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(), if_then_else(bind(&set<char>::count, geraden, _1), cout<<_1, cout<<constant('.')));
      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 set geraden kan nu gewoon lokaal in de functie main worden gedefinieerd. De syntax van de lambda expressie die in de for_each als derde parameter wordt ingevuld is behoorlijk complex. Hieronder is deze expressie nogmaals weergegeven met links naar de gedetailleerde beschrijving van de verschillende benodigde lambda constructies.

if_then_else(bind(&set<char>::count, geraden, _1), cout<<_1, cout<<constant('.'))

Deze expressie kan ook op een alternatieve manier worden opgegeven:

galgje1a_lambda.cpp

if_(bind(&set<char>::count, geraden, _1))[cout<<_1].else_[cout<<constant('.')]

Door nu een iets andere opmaak te kiezen kunnen we de for_each overzichtelijker maken:

galgje1b_lambda.cpp

for_each(
    w.begin(),
    w.end(),
    if_(bind(&set<char>::count, geraden, _1)) [
        cout<<_1
    ]
    .else_ [
        cout<<constant('.')
    ]
);

Voorbeeld galgje met gebruik van string en transform.

galgje3_lambda.cpp

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

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/lambda/if.hpp>

using namespace std;
using namespace boost::lambda;

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(), if_then_else_return(_1==c, _1, _2));
   }
   while (geraden!=w);
   cout<<"Je hebt het woord "<<w<<" geraden."<<endl;
   cin.get(); cin.get();
   return 0;
}

De syntax van de lambda expressie die in de transform als vijfde parameter wordt ingevuld is anders dan je zou verwachten. Je zou verwachten:

_1==c?_1:_2

Deze syntax kon echter in de boost::lambda library niet worden toegepast omdat de conditionele operator ?: niet te overloaden is. Ja, ik weet dat jullie docent steeds loopt te roepen: "in C++ kan alles" .... maar dit kan dus echt niet! Hieronder is deze expressie nogmaals weergegeven met een links naar de gedetailleerde beschrijving van de benodigde lambda constructie if_then_else_return.

if_then_else_return(_1==c, _1, _2)

De bedenker van C++ heeft overigens in 1998 al een voorstel gedaan om de mogelijkheden van operator overloading uit te breiden! Dit voorstel kun je vinden op: http://www.research.att.com/~bs/whitespace98.pdf. Let ook op de datum ;-)