Narzędzia użytkownika

Narzędzia witryny


lambda

Różnice

Różnice między wybraną wersją a wersją aktualną.

Odnośnik do tego porównania

Both sides previous revision Previous revision
Next revision
Previous revision
lambda [2008/04/15 22:22]
przemo86
lambda [2008/04/16 23:27] (aktualna)
przemo86
Linia 1: Linia 1:
 +====== Biblioteka Boost Lambda ======
  
 +Biblioteka standardowa '​algorithm'​ udostępnia wiele przydatnych szablonów umożliwiających wykonywanie często potrzebnych algorytmów na kontenerach. Konstrukcje te umożliwiają np. wyszukiwanie,​ sortowanie, przeglądanie z modyfikacją itp. Problem w tym, że dla każdej operacji trzeba było implementować drobną klasę tzw.obiekt funkcyjny. Obiekt ten miał za zadanie dostarczyć odpowiedniego operatora czy funkcji która miała za zadanie określić logikę operacji na elementach kontenera. Następnie obiekt był przekazywany do szablonu. Taka metoda powodowała powstanie wiele obiektów funkcyjnych które niekiedy w dużym projekcie były wykorzystywane tylko raz. Biblioteka boost wprowadza mechanizm (wraz z mechanizmami pomocniczymi) który umożliwia konstruowanie tak zwanych wyrażeń lambda. Można je rozumieć jako nienazwane funkcje które są wykonywane w miejscu definicji.
  
-Witam wszystkich fanów C++Ponieważ późno udało mi się zalogować na stronę wiki, nie ma tu jeszcze ​żadnego ​ładnego menu skoków ​do przykładówPóki co plik z przykładami odnośnie biblioteki ​lambda jest podpięty pod tą stronę. Niedługo zredaguję tę stronę odsłaniając możliwości obiektów funkcyjnych tworzonych ​przez wyrażenia boost::lambda.+===== Zalety ===== 
 +Biblioteka boost charakteryzuje się następującymi zaletami: 
 +  * mniejsza ilość kodu. 
 +  * skupienie kodu w miejscu faktycznego wykorzystania. 
 +  * łatwość konserwacji kodu (nie trzeba badać szeregu obiektów funkcyjnych) 
 +  * zmniejszenie zapotrzebowania pamięci przez program. 
 + 
 +===== Wady ===== 
 +Biblioteka boost charakteryzuje się następującymi wadami: 
 +  * trudna i z początku ni jak nie intuicyjna składnia.  
 +  * drobna pomyłka programisty to szereg błędów i ostrzeżeń generowanych przez kompilator (efekt zaawansowanych mocno zagłębionych szablonów). 
 +  * zbyt "​fachowe"​ podejście do tworzenia wyrażeń może znacznie utrudnić zrozumienie kodu przez innych programistów. 
 + 
 +===== 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ł. 
 + 
 + 
 +==== 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 projekciea 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 ​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 ​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 elementemTaką 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|}}
lambda.1208290925.txt.gz · ostatnio zmienione: 2008/04/15 22:22 przez przemo86