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:20]
przemo86
lambda [2008/04/16 23:27] (aktualna)
przemo86
Linia 18: Linia 18:
 ===== 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 ==== ==== Pierwszy rzut oka na boost::​lambda ====
Linia 29: Linia 30:
 using namespace boost::​lambda;​ using namespace boost::​lambda;​
  
-(std::cout << _2 << " " << _1 << " " << _3 << "​\n"​) // Definicja wyrażenia  +(std::cout << _2 << " " << _1 << " " << _3 << "​\n"​)       ​// Definicja wyrażenia  
-(" zadna lala,","​Zadna panna,",​ " nie zastapi terminala!\n"​); ​  // Argumenty wywołania.+("​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.
  
-<\code>+ 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.1208373627.txt.gz · ostatnio zmienione: 2008/04/16 21:20 przez przemo86