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

Next revision
Previous revision
Last revision Both sides next revision
lambda [2008/04/15 22:20]
przemo86 utworzono
lambda [2008/04/16 23:24]
przemo86
Linia 1: Linia 1:
-Witam wszystkich fanów C++Ponieważ późno udało mi się zalogować na stronę wiki, nie ma tu jeszcze żadnego ​ładnego menu i skoków ​do przykładówPóki co plik przykładami odnośnie biblioteki lambda jest podpięty pod tą stronę. Niedługo zredaguję tę stronę odsłaniająmożliwości obiektów ​funkcyjnych tworzonych ​przez wyrażenia boost::​lambda.+====== Biblioteka Boost Lambda ====== 
 + 
 +Biblioteka standardowa '​algorithm'​ udostępnia wiele przydatnych szablonów umożliwiających wykonywanie często potrzebnych algorytmów na kontenerachKonstrukcje 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 razBiblioteka boost wprowadza mechanizm (wraz 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. 
 + 
 +===== 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 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>​ 
 + 
 + 
 +==== 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>​ 
 +===== Plik z przykładami ===== 
 +Powyższe przykłady można wypróbować pobierając plik: 
 +{{lambda.cpp|}}
lambda.txt · ostatnio zmienione: 2008/04/16 23:27 przez przemo86