Σάββατο 24 Ιουλίου 2010

Lambdas expression c++ 0x

Σερφάροντας στο νετ έπεσα πάνω σε ένα άρθρο το οποίο έλεγε οτι έρχεται το νέο standar της c++ με όνομα "c++ 0x". Λεό από μέσα μου "εδώ είσαι". Μέτα από αρκετή ώρα, κόλλησε το μάτι μου στο lambda expression, απίστευτο! η c++ υποστηρίζει lambdas.


Λοιπόν τι είναι το lambda, είναι μια συνάρτηση χωρίς όνομα.
Η σύνταξη της είναι

[] { }
Το [] λέει στο compiler οτι ακολουθεί μια lambda.
Το {} είναι το σώμα της συνάρτησης.
Η δήλωση της μπορεί να γίνει με δυο τρόπους.
1) με χρήση του template function (header "functional")
function<void(void)> f = []{};
2) με το keyword auto.
auto f = [] { };
Ενα παράδειγμα χρήσης μιας lambda
#include <iostream>

#include <functional>
using std::function;
using std::cout;
using std::cin;
using std::endl;

void execute_lambda(function<void(void)> l)
{
l();
}
int main(int a,char** b)
{
execute_lambda([]
{
cout<<"hello from lambda!"<<endl;
});

cin.get();
return 0;
}

Θα μου πεις, "σίγα το πράμα που κάνει αυτό το μαραφέτι". Συμφωνώ! Αυτή είναι η απλούστερη σύνταξη, την έγραψα για να γίνει εύκολα κατανοητή.
Τι άλλο μας προσφέρει,
1 μπορεί να επιστρέψει μια μεταβλητή,
2 μπορεί να πάρει κάποια παράμετρο

Για να πάρουμε κάτι απο μια lambda πρέπει να υπάρχει μεσα στο σώμα της το keyword return
[]{ return 1;};
Παράδειγμα, μια Λ που επιστρέφει την λέξη hello
#include <iostream>

#include <functional>
using std::function;
using std::cout;
using std::cin;
using std::endl;


int main(int a,char** b)
{
auto f = []{ return "hello";};
cout<<f();
cin.get();
return 0;

}
Ο τύπος βγαίνει αυτόματα, εάν δεν είναι γνωστός ο τύπος μέσα στο body τότε βάζουμε το σύμβολο -> το οποίο θα αναλύσω πιο κάτω.

Και τώρα πάμε στο δύσκολου μέρος της Λ. Η Διαχείριση των παραμέτρων και μεταβλητών.
Για να εισάγουμε κάποια παράμετρο θα πρέπει να δούμε πως είναι η σύνταξή της Λ με παραμέτρους. Στην ουσία μπαίνει άλλο ενα σύμβολο, οι παρενθέσεις, η νέα μας Λ είναι ετσι [](){}; .
Μπορούμε να εισάγουμε παραμέτρους με δυο τρόπους.
1) by value [](int i){};
2) by ref [](int& i){};
Με το by value δημιουργούμε ενα αντίγραφο της παραμέτρου, αρα ο,τι και να κάνουμε μέσα στη Λ δεν θα αλλάξει το i. Παράδειγμα μια Λ που επιστρέφει το τετράγωνο της μεταβλητής
#include <iostream>
#include <functional>
using std::function;
using std::cout;
using std::cin;
using std::endl;


int main(int a,char** b)
{
int i = 3;
auto f = [](int i)
{
return i*i;
};
cout<<f(i);
cin.get();
return 0;
}

Με το by ref περνούμε την διεύθυνση της παράμετρο, αρα η παράμετρος μπορεί να αλλάξει κατάσταση μέσου της Λ. Παράδειγμα μια Λ που αλλάζει την παράμετρο.
#include <iostream>
#include <functional>
using std::function;
using std::cout;
using std::cin;
using std::endl;


int main(int a,char** b)
{
int i = 3;
auto f = [](int& i)
{
i = 100000;
};
f(i);
cout<<i;
cin.get();
return 0;
}

Αυτά με τις παραμέτρους. Τώρα τι κάνουμε με τις μεταβλητές; Μια Λ είναι ένα πρότυπο το οποιο μπορεί να εκτελεστεί οπού να'ναι, με αποτέλεσμα να βλέπει μονό global vars. Δηλαδή στο παρακάτω παράδειγμα
#include <iostream>

#include <functional>
using std::function;
using std::cout;
using std::cin;
using std::endl;

void foo(function<void(void)> f)
{
f();
}
int main(int a,char** b)
{
int i = 3;
foo([]
{
i = 100000;
});
cin.get();
return 0;

}
Αυτό δεν γίνετε compile επειδή στη main έχουμε τη κατασκευή της Λ και η εκτέλεση της γίνετε στη foo, στη foo ωμός δεν υπάρχει κάποια μεταβλητή i αρα πάπαλα. Φυσικά υπάρχει τρόπος να περάσεις μεταβλητές στο scop μιας Λ και αυτο γίνεται με 4 τρόπους. Όλα by value, όλα by ref, επιλεγμένα by value επιλεγμένα by ref.

1) Όλα by value η Λ βλέπει όλο το scop στο οποίο βρίσκετε/κατασκευάζεται, οτι χρησιμοποιεί το έχει ως αντίγραφο και η σύνταξη της είναι η εξής
[=]{};
πχ


#include <iostream>
#include <functional>
using std::function;
using std::cout;
using std::cin;
using std::endl;

void foo(function<void(void)> f)
{
f();
}
int main(int a,char** b)
{
int i = 3;
foo([=]
{
cout<<i;
});
cin.get();
return 0;

}
2) Όλα by ref η Λ βλέπει όλο το scop στο οποίο βρίσκετε/κατασκευάζεται, οτι χρησιμοποιεί μπορεί να το αλλάξει και η σύνταξη της είναι η εξής
[&]{};
πχ


#include <iostream>
#include <functional>
using std::function;
using std::cout;
using std::cin;
using std::endl;

void foo(function<void(void)> f)
{
f();
}
int main(int a,char** b)
{
int i = 3;
foo([&]
{
i=1000;
});
cout<<i;
cin.get();
return 0;

}
3) Επιλεγμένα by value, μπορείς να πεις ποιες μεταβλητές θέλεις η σύνταξη της είναι
[όνομα-μεταβλητής]{};
πχ
#include <iostream>

#include <functional>
using std::function;
using std::cout;
using std::cin;
using std::endl;

void foo(function<void(void)> f)
{
f();
}
int main(int a,char** b)
{
int i = 3;
foo([i]
{
cout<<i;
});

cin.get();
return 0;

}

4) Επιλεγμένα by ref, μπορείς να πεις ποιες μεταβλητές θέλεις η σύνταξη της είναι
[&όνομα-μεταβλητής]{};
πχ


#include <iostream>
#include <functional>
using std::function;
using std::cout;
using std::cin;
using std::endl;

void foo(function<void(void)> f)
{
f();
}
int main(int a,char** b)
{
int i = 3;
foo([&i]
{
i = 1000;
});
cout<<i;
cin.get();
return 0;

}
Επίσης μπορείς να κάνεις και συνδυασμούς
[&i,y,x,&z]{};

Αυτά και με τους παραμέτρους και μεταβλητές.

Τελευταία δυνατότητα της Λ είναι ο καθορισμός του τύπου επιστροφής. Και η πλήρη σύνταξη της είναι η εξής
[scope_define](args_define)->type_define{body_define}
πχ


#include <iostream>
#include <functional>
using std::function;
using std::cout;
using std::cin;
using std::endl;

class base{};
class drived:public base{};
int main(int a,char** b)
{
auto f = []()->base*
{
return new drived();
};
base *b = f();
cin.get();
return 0;

}


Τέλος μια υλοποίηση τύπου where και χρήση lambdas
#include <iostream>

#include <functional>
#include <vector>
#include <string>
using std::function;
using std::cout;
using std::cin;
using std::endl;
using std::vector;
using std::string;
struct Person
{
string Name;
int Age;
Person(string name,int age)
{
Name = name;
Age = age;
}
};
template<class T>
vector<T> where_(vector<T> p,function<bool(T)> f)
{
vector<T> v;
auto inF = p.begin(),inL = p.end();
for(;inF!=inL;++inF)
if(f(*inF))
v.push_back(*inF);
return v;
}
int main(int a,char** b)
{
vector<Person> p;
p.push_back( Person("Alex",20));
p.push_back( Person("Manos",33));
p.push_back( Person("Elen",18));
p.push_back( Person("Maria",20));
p.push_back( Person("Mitsos",20));

vector<Person> only20age = where_<Person>(p,[](Person p)->bool
{
if(p.Age == 20)
return true;
else
return false;
});
for(unsigned i =0 ; i < only20age.size();i++)
cout<<only20age[i].Name<<endl;
cin.get();
return 0;

}

Δεν είναι απίστευτο;


2 σχόλια:

  1. Γραψτε και κανα σχολια καλοι μου ανθρωποι! Αρνητικο θετικο οτι να'ναι δεν εχω προβλημα.

    ΑπάντησηΔιαγραφή
  2. Πολυ καλος!

    Τα lambda είναι τρομερά. Τα έχω χρησιμοποιήσει πολύ στην C# (έχουν διαφορετική σύνταξη εκεί βεβαια) όπου πραγματικά αν τα συνηθίσεις μετά δε κανεις βήμα χωρίς αυτά.

    ΑπάντησηΔιαγραφή