Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[C++11] Praktyczne wykorzystanie wyrażeń lambda

Ostatnio zmodyfikowano 2014-10-22 19:27
Autor Wiadomość
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:

C/C++
// Szablon, który umożliwia automatyczne usypianie wątku podczas
// oczekiwania na dane (bo kontener jest pusty).
template < class T >
class WaitingList
{
public:
    //(...) tu sporo innego kodu :)
   
    // Kasuje element z początku listy. Jeżeli kontener
    // z danymi jest pusty, to metoda usypia wątek z którego została ona
    // wywołana. Metoda zwraca wartość false wtedy, gdy
    // wywołano z dowolnego wątku metodę unlock
    // oraz w kontenerze nie ma już więcej danych do
    // odczytania. W każdym innym wypadku metoda zwróci wartość true.
    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;
   
}; //class WaitingList

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 :)
P-119138
DejaVu
Temat założony przez niniejszego użytkownika
» 2014-10-21 02:16:54
Tu jeszcze jeden przykład, znacznie prostszy do zrozumienia:
C/C++
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:
P-119139
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.
P-119144
DejaVu
Temat założony przez niniejszego użytkownika
» 2014-10-21 09:57:17
@up: pierwszy kod się skompiluje :)
P-119145
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.
C/C++
#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;
}
P-119146
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.
C/C++
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.aspx

Wreszcie można tworzyć własne algorytmy, czy wręcz własne struktury sterujące:
C/C++
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):
C/C++
class Button
{
public:
    signal < void() > on_clicked; //klasa sygnału np. z biblioteki boost.signals2
};

class Dialog
{
private:
    Button help_btn;
   
    void show_help() { /*akcja wykonywana po kliknięciu przycisku*/ }
   
public:
    Dialog()
    {
        help_btn.on_clicked.connect([ this ] { show_help(); } );
    }
};

albo połączenie generatora liczb pseudolosowych z dystrybucją w jedną całość
C/C++
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:
C/C++
const int x =[ = ]->int {
    try {
        int ret = 0;
        for( int i = 2; i <= N; ++i ) { // this could be some
            ret += Calc( i ); // arbitrarily long code
        } // needed to initialize x
        return ret;
    } catch( XInitException ) { // that could throw
        return 0; // if so, use default
    }
}();
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.
P-119203
DejaVu
Temat założony przez niniejszego użytkownika
» 2014-10-22 19:27:47
Super :)
P-119216
« 1 »
  Strona 1 z 1