© 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.
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.
find_if
.
#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;
}
for_each
.
#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;
}
transform
.
#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;
}
remove_if
.
#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;
}
for_each
met aanroep van een memberfunctie.
#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.
#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;
}
set
en for_each
.
#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:
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:
for_each(
w.begin(),
w.end(),
if_(bind(&set<char>::count, geraden, _1)) [
cout<<_1
]
.else_ [
cout<<constant('.')
]
);
string
en
transform
.
#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 ;-)