DejaVu Temat założony przez niniejszego użytkownika |
[C++11] Praktyczne wykorzystanie wyrażeń lambda » 2014-10-21 01:52:36 Ponieważ obiło mi się o uszy, że na Politechnice Warszawskiej będą wkrótce prezentowane praktyczne przykłady zastosowania wyrażeń lambda z zastosowaniem C++11, to stwierdziłem, że podrzucę Wam przykład z implementacji projektu, który realizuję z drugim administratorem naszego forum:
template < class T > class WaitingList { public: bool pop_front( T & _outputItem ) { std::unique_lock < std::mutex > lock( m_cs ); m_event.wait( lock,[ & ] { return !m_container.empty() || !m_bWaitIfEmpty; } ); if( m_container.empty() ) return false; _outputItem = m_container.front(); m_container.pop_front(); lock.unlock(); return true; } private: mutable std::mutex m_cs; std::condition_variable m_event; typename std::list < T > m_container; bool m_bWaitIfEmpty; };
Iname, Moniko - Jeżeli macie coś ciekawego do dorzucenia w tym temacie to prosiłbym Was o zabranie głosu :) Macie ogromną wiedzę i może coś ciekawego wyczarujecie w tym temacie dla celów edukacyjnych :) Inne osoby, które coś wiedzą na ten temat i również miałyby coś ciekawego do dodania prosiłbym również o zabranie głosu i zamieszczenie przykładu. Generalnie chodzi o pokazanie sytuacji, w której faktycznie wyrażenie lambda upraszcza kod i jednocześnie jest to jakieś praktyczne rozwiązanie problemu, a nie prezentacja działania lambdy jako sztuka dla sztuki :) |
|
DejaVu Temat założony przez niniejszego użytkownika |
» 2014-10-21 02:16:54 Tu jeszcze jeden przykład, znacznie prostszy do zrozumienia: void CAboutForm::onRender( cmn::CCamera & _target ) { gui::CLabel lbl; gui::CControls kontrolki; kontrolki.push( & lbl ); float nextY = 100; auto print =[ & ]( const sf::Color & col, int fsize, int x, int stepy, const std::wstring & wstr ) { lbl.fontColor().set( col ); lbl.fontSize().set( fsize ); lbl.setPosition( x, nextY ); lbl.setText( wstr ); nextY += stepy + fsize; kontrolki.callRenderAll( _target ); }; const sf::Color gold( 0xCC, 0x99, 0x00 ); const sf::Color whit( sf::Color::White ); const int iFontH1 = 28; const int iFontH2 = 24; const int iFontH3 = 20; const int iFontSize = 16; const int iWciecie = 15; cmn::LanguageTranslator::Instance translator; std::lock_guard < std::mutex > lock( translator ); print( gold, iFontH1, 100, 20, L"Snake" ); print( whit, iFontSize, 115, 0, L"Snake v1.0" ); print( whit, iFontSize, 115, 20, L"\u00A9 Dynamic Development Team 2014" ); print( gold, iFontH3, 110, 0, translator->getText( L"Architect & Team Leader" ) ); print( whit, iFontSize, 110 + iWciecie, 20, L"Piotr \"DejaVu\" Szawdyński" ); print( gold, iFontH3, 110, 0, translator->getText( L"Senior Developer" ) ); print( whit, iFontSize, 110 + iWciecie, 20, L"Paweł \"pekfos\" Kozioł" ); print( gold, iFontH3, 110, 0, translator->getText( L"Sound & Graphics" ) ); print( whit, iFontSize, 110 + iWciecie, 20, L"Magdalena Zwolińska" ); print( gold, iFontH3, 110, 0, translator->getText( L"Junior Developers" ) ); print( whit, iFontSize, 110 + iWciecie, 0, L"Kamil \"akwes\" Piekara" ); print( whit, iFontSize, 110 + iWciecie, 20, L"Artur \"Mrowqa\" Jamro" ); }
Efekt: |
|
maly |
» 2014-10-21 09:39:17 Wadą takiego przedstawienia "życiowego rozwiązania" jest jego niekompletność, więc ten kto rozumie przykład i tak by go umiał napisać a początkujący bez możliwości pobawienia się kodem mogą nie zajarzyć o co w nim chodzi. Według mnie przykład mógłby być mniej życiowy ale za to kompilowalny. |
|
DejaVu Temat założony przez niniejszego użytkownika |
» 2014-10-21 09:57:17 @up: pierwszy kod się skompiluje :) |
|
maly |
» 2014-10-21 10:17:24 No kompiluje się ale nie jest testowalny pozatym lepszym przykładem jest ten drugi(jakiś taki bardziej życiowy) :) Kiedyś podrzuciłem znajomemu przykład zastosowania lambdy w jako tako życiowej formie, problem w tym że kiedy już wiedział jak, to zaczął bezmyślnie nadużywać takie rozwiązania i teraz niewiem czy mu pomogłem czy zaszkodziłem. #include <SFML/Graphics.hpp>
int main() { sf::RenderWindow window( sf::VideoMode( 800, 600 ), "SFML test" ); auto doButton =[ & ]( const sf::Vector2f & pos, const sf::Vector2f & size, const sf::Color & col, const sf::Vector2f & mousepos, bool mousepressed ) { sf::RectangleShape rc( size ); rc.setPosition( pos ); rc.setFillColor( col ); rc.setOutlineThickness( - 3 ); window.draw( rc ); return mousepressed && rc.getGlobalBounds().contains( mousepos ); }; sf::Color bckgcolor( { 127, 127, 127 } ); while( window.isOpen() ) { bool mousepressed = false; sf::Event event; while( window.pollEvent( event ) ) { if( event.type == sf::Event::Closed ) window.close(); if( event.type == sf::Event::MouseButtonPressed ) mousepressed = true; } auto mousepos = window.mapPixelToCoords( sf::Mouse::getPosition( window ) ); window.clear( bckgcolor ); if( doButton( { 10, 10 }, { 50, 50 }, sf::Color::Red, mousepos, mousepressed ) ) bckgcolor = sf::Color::Red; if( doButton( { 10, 100 }, { 70, 70 }, sf::Color::Green, mousepos, mousepressed ) ) bckgcolor = sf::Color::Green; if( doButton( { 100, 100 }, { 80, 80 }, sf::Color::Blue, mousepos, mousepressed ) ) bckgcolor = sf::Color::Blue; window.display(); } return 0; } |
|
Monika90 |
» 2014-10-22 16:17:38 DejaVu, już nie przesadzaj z tą wiedzą, Iname z pewnością ma, ale ja? Ale jak chcesz to proszę, oto co myślę o wyrażeniach lambda. Podstawowe i niebudzące kontrowersji zastosowanie wyrażeń lambda to argumenty dla standardowych algorytmów: find_if, equal, sort, itd. std::array < int, 8 > a = { 3, 4, 5, 1, 2, 3, 6, 5 }; auto it = std::find_if( a.begin(), a.end(),[]( auto x ) { return x % 2 == 0 && x > 5; } ); if( it != a.end() ) std::cout << "znaleziono element parzysty większy od 5" << std::endl;
Oczywiście, zawsze w takim przypadku należy się zastanowić czy nie będzie lepiej zamiast lambdy użyć nazwanej funkcji lub klasy, po to by uniknąć repetycji i podnieść poziom abstrakcji. Ale nie tylko ze standardowymi algorytmami wygodnie jest używać lambd. Biblioteki do programowania równoległego, Intel TBB i Microsoft PPL też sie do tego nadają. Tutaj przykłady http://msdn.microsoft.com/en-us/library/dd470426.aspxWreszcie można tworzyć własne algorytmy, czy wręcz własne struktury sterujące: template < class Int, class Func > void for_each_index_pair( Int n, Func f ) { if( n > 1 ) for( Int i = 0; i < n - 1; ++i ) for( Int j = i + 1; j < n; ++j ) f( i, j ); }
int main() { std::vector < double > v = { 1.1, - 2.2, 3.3, - 4.4, 0 }; for_each_index_pair( v.size(),[ & v ]( auto i, auto j ) { std::cout << "v[" << i << "] * v[" << j << "] = " << v[ i ] * v[ j ] << std::endl; } ); }
Drugie istotne zastosowanie to binding, czyli używanie lambdy zamiast std::bind i boost::bind. Przykład - obsługa zdarzeń GUI (nieistotne deklaracje pominięto): class Button { public: signal < void() > on_clicked; };
class Dialog { private: Button help_btn; void show_help() { } public: Dialog() { help_btn.on_clicked.connect([ this ] { show_help(); } ); } };
albo połączenie generatora liczb pseudolosowych z dystrybucją w jedną całość int main() { std::mt19937 urng { 65635263 }; std::discrete_distribution <> dist { 40, 25, 15, 10, 8, 2 }; auto gen =[ & ] { return dist( urng ); }; for( int i = 0; i < 100; ++i ) std::cout << gen() << ' '; std::array < int, 100 > a1, a2, a3; std::generate( a1.begin(), a1.end(), gen ); std::generate( a2.begin(), a2.end(), gen ); std::generate( a3.begin(), a3.end(), gen ); }
Trzecia kategoria to lambdy jako funkcje lokalne. Twój przykład z formatowaniem tekstu należy do tej kategorii. Ale to akurat nie jest dobry przykład na użycie lambdy, tu lepiej byłoby napisać klasę Formatter, czy Printer. Choćby dlatego że funkcja CAboutForm::onRender prawie na pewno nie jest jedynym miejscem w którym chcesz z takiej funkcjonalnośći skorzystać. Czwarta kategoria to wyrażenia lambda które są natychmiast wywoływane, pozwalają one np. na wstawienie instrukcji switch wewnątrz innego wyrażenia. Dla mnie to raczej kontrowersyjna sprawa. Można tego użyć do inicjalizacji stałej lokalnej. Przykład pochodzący od Herba Suttera: const int x =[ = ]->int { try { int ret = 0; for( int i = 2; i <= N; ++i ) { ret += Calc( i ); } return ret; } catch( XInitException ) { return 0; } }();
No i dzięki temu x może być stałą, a stałe są dobre bo ułatwiają rozumienie kodu. Jednak to chyba jest już przesada. Są jeszcze inne istotne zastosowania, ale to może później. |
|
DejaVu Temat założony przez niniejszego użytkownika |
» 2014-10-22 19:27:47 Super :) |
|
« 1 » |