Różnice między wybraną wersją a wersją aktualną.
Both sides previous revision Previous revision Next revision | Previous revision | ||
lambda [2008/04/16 21:14] przemo86 |
lambda [2008/04/16 23:27] (aktualna) przemo86 |
||
---|---|---|---|
Linia 16: | Linia 16: | ||
* zbyt "fachowe" podejście do tworzenia wyrażeń może znacznie utrudnić zrozumienie kodu przez innych programistów. | * zbyt "fachowe" podejście do tworzenia wyrażeń może znacznie utrudnić zrozumienie kodu przez innych programistów. | ||
- | ==== Przykłady ==== | + | ===== Przykłady ===== |
Poniższe przykłady pokazują (w niewielkim stopniu) możliwości tych wyrażeń. Należy w tym miejscu zwrócić uwagę, że często wyrażenia lambda wykorzystują mechanizmy pomocnicze zdefiniowane w innych bibliotekach których nagłówki należy dołączyć. Standardowo dołączamy bibliotekę boost\lambda\lambda.hpp. W przykładach będą pokazane nagłówki które dodatkowo należy dołączyć by kod się skompilował. | Poniższe przykłady pokazują (w niewielkim stopniu) możliwości tych wyrażeń. Należy w tym miejscu zwrócić uwagę, że często wyrażenia lambda wykorzystują mechanizmy pomocnicze zdefiniowane w innych bibliotekach których nagłówki należy dołączyć. Standardowo dołączamy bibliotekę boost\lambda\lambda.hpp. W przykładach będą pokazane nagłówki które dodatkowo należy dołączyć by kod się skompilował. | ||
+ | ==== Pierwszy rzut oka na boost::lambda ==== | ||
+ | W wyrażeniach lambda argumenty oznaczamy jako _X gdzie X może być cyfrą od 1 do 9. Bibloteka boost umożliwia zmianę nazwy _X na dowolną poprzez użycię boost::lambda::placeholderX_type. Jednak nie zaleca sie tego robić ze względu na możliwość skonfudowania programistów uczestniczących w projekcie, a przyzwyczajonych do standardowego oznaczenia. | ||
+ | |||
+ | <code cpp> | ||
+ | |||
+ | #include <boost/lambda/lambda.hpp> | ||
+ | |||
+ | using namespace boost::lambda; | ||
+ | |||
+ | (std::cout << _2 << " " << _1 << " " << _3 << "\n") // Definicja wyrażenia | ||
+ | ("zadna lala,","Zadna panna,", "nie zastapi terminala!\n"); // Argumenty wywołania. | ||
+ | |||
+ | </code> | ||
+ | |||
+ | Kod ten możemy odczytać jako: Wywołaj w tym konkretnym miejscu funkcję która wyprowadzi na standardowe wyjście argumenty w kolejności 2,1,3. | ||
+ | |||
+ | |||
+ | ==== Elementy kontenerów ==== | ||
+ | Jednym z głównych powodów stworzenia boost::lambda było umożliwienie szybszego kodowania operacji na kontenerach. Załóżmy, że mamy kolekcje obiektów i chcemy na każdym z nich wykonać funkcję. Funkcja ta dodatkowo będzie wykonywana w programie tylko z argumentami pochodzącymi z tego kontenera i tylko w jednym miejscu. Załóżmy, że mamy: | ||
+ | |||
+ | <code cpp> | ||
+ | |||
+ | void funkcja_globalna (const float i) | ||
+ | { | ||
+ | std::cout << "\nvoid funkcja_globalna : " << i; | ||
+ | } | ||
+ | |||
+ | struct Example | ||
+ | { | ||
+ | void funkcja_klasy (const float i) const | ||
+ | { | ||
+ | std::cout << "\nvoid Example::funkcja_klasy : " << i; | ||
+ | } | ||
+ | |||
+ | ~Example(){}; | ||
+ | }; | ||
+ | |||
+ | std::vector<float> vec_test; | ||
+ | |||
+ | </code> | ||
+ | |||
+ | Dawniej problem trzeba było rozwiązać tak: | ||
+ | |||
+ | <code cpp> | ||
+ | |||
+ | #include <algorithm> | ||
+ | #include <functional> | ||
+ | |||
+ | Example ex; | ||
+ | Example * ex_ptr = &ex; | ||
+ | |||
+ | std::cout << "Uzycie std::ptr_fun dla wiazania funkcji globalnej."; | ||
+ | std::for_each(vec_test.begin(), vec_test.end(), std::ptr_fun(funkcja_globalna)); | ||
+ | |||
+ | std::cout << "Wiazanie z metoda klasy za pomoca mem_fun_ref (dla obiektu ex)."; | ||
+ | std::for_each(vec_test.begin(), vec_test.end(), std::bind1st(std::mem_fun_ref(&example::funkcja_klasy), ex)); | ||
+ | |||
+ | std::cout << "Wiazanie z metoda klasy za pomoca mem_fun (dla wskazania do obiektu ex)."; | ||
+ | std::for_each(vec_test.begin(), vec_test.end(), std::bind1st(std::mem_fun(&example::funkcja_klasy), ex_ptr)); | ||
+ | </code> | ||
+ | Przy używaniu metod z obecnego standardu musimy rozróżnić wiązania dla obiektów i dla wskazań na te obiekty. Wyrażenia boost::lambda oraz boost::bind "domyślają się" z czym mają do czynienia. Zatem powyższy kod możemy zastąpić nieco lepszym używającym boost:bind : | ||
+ | |||
+ | <code cpp> | ||
+ | #include <algorithm> | ||
+ | #include <boost/lambda/lambda.hpp> | ||
+ | #include <boost/lambda/bind.hpp> | ||
+ | |||
+ | Example ex; | ||
+ | Example * ex_ptr = &ex; | ||
+ | |||
+ | std::cout << "Uzycie boost::bind dla funkcji globalnych."; | ||
+ | std::for_each(vec_test.begin(), vec_test.end(), bind(&funkcja_globalna,_1)); | ||
+ | |||
+ | std::cout << "Uzycie boost::bind dla funkcji obiektow klasy."; | ||
+ | std::for_each(vec_test.begin(), vec_test.end(), bind(&example::funkcja_klasy, ex ,_1)); | ||
+ | |||
+ | std::cout << "Uzycie boost::bind dla funkcji wskazan do obiektow klasy."; | ||
+ | std::for_each(vec_test.begin(), vec_test.end(), bind(&example::funkcja_klasy, ex_ptr ,_1)); | ||
+ | |||
+ | </code> | ||
+ | |||
+ | Natomiast dzięki wyrażeniom lambda możemy ominąć pisanie klasy lub funkcji globalnej i zdefiniować przetwarzanie w miejscu wołania: | ||
+ | |||
+ | <code cpp> | ||
+ | #include <algorithm> | ||
+ | #include <boost/lambda/lambda.hpp> | ||
+ | |||
+ | Example ex; | ||
+ | Example * ex_ptr = &ex; | ||
+ | |||
+ | using namespace boost::lambda; | ||
+ | |||
+ | std::cout << "Uzycie wyrazenia lambda dla obiektow (czy tez wskazan do nich) wprost z std::vector.\n"; | ||
+ | std::for_each(vec_test.begin(), vec_test.end(), std::cout << constant("Wyrazenie boost::lambda: ") <<_1 << constant("\n") ); | ||
+ | |||
+ | </code> | ||
+ | |||
+ | W kodzie powyżej została użyta funkcja constant() która przekształca łańcuch tekstu na postać rozumianą przez wyrażenie lambda. | ||
+ | |||
+ | Mając przykładowy wektor vec_test chcemy posortować jego elementy malejąco. Dzięki wyrażeniom lambda nie musimy tworzyć obiektów funkcyjnych. Możemy warunek napisać wprost: | ||
+ | |||
+ | <code cpp> | ||
+ | #include <algorithm> | ||
+ | #include <boost/lambda/lambda.hpp> | ||
+ | |||
+ | using namespace boost::lambda; | ||
+ | |||
+ | std::sort(vec_test.begin(), vec_test.end(), (_1 > _2) ); | ||
+ | |||
+ | std::cout << "Posortowany vector : \n"; | ||
+ | std::for_each(vec_test.begin(), vec_test.end(), std::cout << constant("Wyrazenie boost::lambda: ") <<_1 << constant("\n") ); | ||
+ | |||
+ | </code> | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ==== Konstrukcje sterujące oraz pętle ==== | ||
+ | Przekazywane do szablonów standardowych algorytmów funkcje są zazwyczaj bardziej skomplikowane. Biblioteka boost::lambda umożliwia standardowe konstrukcje sterujące wymienione poniżej. | ||
+ | |||
+ | === if_else === | ||
+ | |||
+ | <code cpp> | ||
+ | #include <algorithm> | ||
+ | #include <boost/lambda/lambda.hpp> | ||
+ | #include <boost/lambda/if.hpp> | ||
+ | |||
+ | using namespace Boost::lambda; | ||
+ | |||
+ | std::cout << "Uzycie konstrukcji if_else_then(warunek, instrukcje_dla_true, instrukcje_dla_false)." | ||
+ | << "Alternatywnie if_(warunek)[instrukcje_dla_true].else_[instrukcje_dla_false].\n"; | ||
+ | |||
+ | for_each(vec_test.begin(), vec_test.end(), | ||
+ | if_then_else(_1 > 3 , | ||
+ | std::cout << constant("\n(if_then_else) Liczba : ") << _1 << constant( " jest wieksza niz 3!\n") , | ||
+ | std::cout << constant("\n(if_then_else) Liczba : ") << _1 << constant( " jest mniejsza lub rowna 3!\n"))); | ||
+ | </code> | ||
+ | |||
+ | === Switch === | ||
+ | |||
+ | <code cpp> | ||
+ | #include <boost/lambda/lambda.hpp> | ||
+ | #include <boost/lambda/switch.hpp> | ||
+ | |||
+ | using namespace Boost::lambda; | ||
+ | |||
+ | std::cout << "Uzycie konstrukcji switch.\n" ; | ||
+ | (switch_statement( | ||
+ | _1, | ||
+ | case_statement<0> | ||
+ | (std::cout << constant("Wybrales 0 \n")), | ||
+ | case_statement<1> | ||
+ | (std::cout << constant("Wybrales 1 \n")), | ||
+ | default_statement | ||
+ | (std::cout << constant("Nie mam pojecia co wybrales!\n"))) | ||
+ | ) ((make_const(666))); | ||
+ | |||
+ | </code> | ||
+ | |||
+ | Użycie (make_const(666)) jest konieczne. Szablon tej funkcji jest następujący i jest zdefiniowany w bibliotece boost: | ||
+ | <code cpp> | ||
+ | template <class T> inline const T& make_const(const T& t) { return t; } | ||
+ | </code> | ||
+ | Przekazanie samego 666 mogłoby sprowokować błąd ponieważ 666 jest typu int i nie może mieć kwalifikatora const. Szablon natomiast potrzebuje const &. | ||
+ | |||
+ | === Petla while === | ||
+ | |||
+ | <code cpp> | ||
+ | #include <boost/lambda/lambda.hpp> | ||
+ | #include <boost/lambda/loops.hpp> | ||
+ | |||
+ | using namespace Boost::lambda; | ||
+ | |||
+ | std::cout << "Uzycie konstrukcji (while_loop(warunek, instrukcje))(argumenty)\n\n"; | ||
+ | |||
+ | int start = 5; | ||
+ | int end = 10; | ||
+ | (while_loop(_1 <= _2, | ||
+ | (++_1, std::cout << _2 << constant(" - ") << _1 << constant(" = ") << _2 -_1 << constant("\n") ))) | ||
+ | (start,end); | ||
+ | |||
+ | </code> | ||
+ | |||
+ | === Petla do while === | ||
+ | |||
+ | <code cpp> | ||
+ | #include <boost/lambda/lambda.hpp> | ||
+ | #include <boost/lambda/loops.hpp> | ||
+ | |||
+ | using namespace Boost::lambda; | ||
+ | |||
+ | |||
+ | (do_while_loop( _1 == 0 , std::cout << constant ("Uzycie konstrukcji (do_while_loop(warunek, instrukcje))(argumenty).\n")))(make_const(1)); | ||
+ | |||
+ | (do_[ | ||
+ | _1 == 0 , std::cout << constant ("Uzycie konstrukcji (do_[instrukcje].while_(warunek))(argumenty).\n") | ||
+ | ].while_(_1 == 0))(make_const(1)); | ||
+ | </code> | ||
+ | |||
+ | |||
+ | === Petla for === | ||
+ | |||
+ | <code cpp> | ||
+ | #include <boost/lambda/lambda.hpp> | ||
+ | #include <boost/lambda/loops.hpp> | ||
+ | |||
+ | using namespace Boost::lambda; | ||
+ | |||
+ | std::cout << "Uzycie konstrukcji (for_ (war_poczatkowe,warunek, instrukcja iteracji)[instrukcje])(argumenty) : \n"; | ||
+ | int i = 0; | ||
+ | |||
+ | (for_ (var(i)=0 , var(i) < _1, ++var(i)) | ||
+ | [ | ||
+ | var(std::cout) << var(i) << constant("^2 = ") << make_const( var(i)*var(i) ) << constant("\n") | ||
+ | ] | ||
+ | )(make_const (4)); | ||
+ | </code> | ||
+ | |||
+ | Ponieważ powyższe wyrażenie jest wyrażeniem lambda więc aby ono zrozumiało zmienne zewnętrzne należy je stworzyć jego elementem. Taką konwersje tworzy funkcja var(). | ||
+ | |||
+ | |||
+ | ==== Rzutowanie i wyjątki ==== | ||
+ | Biblioteka boost::lambda umożliwia wprowadzenie rzutowania w wyrażeniu lambda jak również genrowanie i przechwytywanie wyjątków. Oto przykład prezentujący konstrukcję: | ||
+ | |||
+ | <code cpp> | ||
+ | #include <boost/lambda/lambda.hpp> | ||
+ | #include <boost/lambda/casts.hpp> | ||
+ | #include <boost/lambda/exceptions.hpp> | ||
+ | |||
+ | struct ExampleDerived : public Example | ||
+ | { | ||
+ | void funkcja_klasy_pochodnej (float i) const | ||
+ | { | ||
+ | std::cout << "void ExampleDerived::funkcja_klasy_pochodnej : " << i << std::endl; | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | using namespace Boost::lambda; | ||
+ | |||
+ | ExampleDerived ex_derv; | ||
+ | |||
+ | // Rzutowanie w tym przykładzie nie powiedzie się i zostanie wygenerowany wyjątek, a następnie | ||
+ | // przechwycony i obsłużony. | ||
+ | |||
+ | std::cout << "Uzycie rzutowania i wykorzystanie mechanizmu wyjatkow w wyrazeniach lambda.\n"; | ||
+ | (try_catch( | ||
+ | bind(&example_derived::funkcja_klasy_pochodnej, ll_dynamic_cast<example_derived&>(*_1),_2), | ||
+ | catch_exception<std::bad_cast>(bind(&example::funkcja_klasy,_1,_2))))(ex_ptr, make_const(66.6f)); | ||
+ | |||
+ | std::cout << std::endl; | ||
+ | // Rzutowanie w tym przykładzie powiedzie się. Przedstawiono dodatkowo schemat zapisu przechwytywania | ||
+ | // wyjątków różnych typów oraz dowolonego wyjątku. | ||
+ | |||
+ | (try_catch( | ||
+ | bind(&example_derived::funkcja_klasy_pochodnej, ll_dynamic_cast<example_derived&>(*_1),_2), | ||
+ | catch_exception<std::bad_cast>(bind(&example::funkcja_klasy,_1,_2)), | ||
+ | catch_exception<std::exception>(), | ||
+ | catch_all()))(make_const(&ex_derv), make_const(77.7f)); | ||
+ | |||
+ | </code> | ||
+ | ===== Zakończenie i plik z przykładami ===== | ||
+ | Wyrażenia lambda to przydatne konstrukcje które nie raz mogą zaoszczędzić czas, zmniejszyć ilość kodu czy zajętą przez program pamięć. Mankamentem jest składnia wyrażeń której należy się po prostu nauczyć i przećwiczyć na wielu przykładach. | ||
+ | |||
+ | Powyższe przykłady można wypróbować pobierając plik: | ||
+ | {{boost_lambda.cpp|}} |