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/16 21:03]
przemo86
lambda [2008/04/16 23:27] (aktualna)
przemo86
Linia 5: Linia 5:
 ===== Zalety ===== ===== Zalety =====
 Biblioteka boost charakteryzuje się następującymi zaletami: Biblioteka boost charakteryzuje się następującymi zaletami:
-* mniejsza ilość kodu. +  ​* mniejsza ilość kodu. 
-* skupienie kodu w miejscu faktycznego wykorzystania. +  * skupienie kodu w miejscu faktycznego wykorzystania. 
-* łatwość konserwacji kodu (nie trzeba badać szeregu obiektów funkcyjnych) +  * łatwość konserwacji kodu (nie trzeba badać szeregu obiektów funkcyjnych) 
-* zmniejszenie zapotrzebowania pamięci przez program.+  * zmniejszenie zapotrzebowania pamięci przez program.
  
 ===== Wady ===== ===== Wady =====
 Biblioteka boost charakteryzuje się następującymi wadami: Biblioteka boost charakteryzuje się następującymi wadami:
-* trudna i z początku ​nie jak nie intuicyjna składnia. +  ​* 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). +  * 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.+  * 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>​
 +
 +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|}}
lambda.1208372618.txt.gz · ostatnio zmienione: 2008/04/16 21:03 przez przemo86