[모던C++입문] 3.9 람다

    3.9 람다(Lamda) (c++11)

    //함수를 인수로 즉시 전달할 수 있다
    fin_diff([](double x) {return sin(x) + cos(x);}, 1., 0.001)
    
    //람다 표현식을 재사용하기 위해 변수에 저장할 수도 있다.
    auto sc_l = [](double x) { return sin(x) + cos(x); }
    
    //리턴타입을 명시적으로 선언하고자 한다면
    [](double x) -> double { return sin(x) + cos(x); }

    캡처

    • 람다 표현식은 자체 매개변수 또는 이전에 캡처된 매개변수만 사용 가능
    //람다를 매개변수화 할때 단순한 연산을 삽입 가능하나
    //매개변수가 많을때는 생산적이지 못함
    a = fin_diff([]double x) {return sin(2.5*x);, 1., 0.001);
    b = fin_diff([]double x) {return sin(3.0*x);, 1., 0.001);
    c = fin_diff([]double x) {return sin(3.5*x);, 1., 0.001);
    
    //다음과 같은 상황에서는 변수나 상수에 접근이 불가
    double phi = 2.5;
    auto sin_phi = [](double x) { return sin(phi * x); };   //오류

    값에 의한 캡처

    • 람다를 호출할 시점의 값을 사용
    • 캡처된 변수는 const 한정된 operator와 같기에 변경할 수 없다
    //람다에서 phi 변수를 사용하려면 캡쳐를 해야한다
    double phi = 2.5;
    auto sin_phi = [phi](double x) { return sin(phi * x); };
    
    //여러 변수를 캡처하고 싶다면 쉼표로 구분
    double xi = 0.2;
    auto sin2 = [phi, xi](double x){ return sin(phi * x) + cos(x) * xi; };
    
    //sin2 를 펑터 클래스로 작성한다면
    struct lambda_f
    {
        lambda_f(double phi, double xi) : phi(phi), xi(xi) {}
        double operator(){double x) const
        {
            return sin(phi * x) + cos(x) * xi;
        }
        const double phi, xi;
    };
    
    phi = 2.5; xi = 0.2;
    auto px = [phi, xi](double x) { return sin(phi * x) + cos(x) * xi; };
    phi = 3.5; xi = 1.2;
    a = fin_diff(px, 1., 0.001);    //여전히 phi=2.5, xi = 0.2이다
    
    auto l_inc = [phi](double x) { phi += 0.6; return phi; };   //오류
    
    //캡처된 값을 수정하려면 람다를 mutable로 한정해야 한다.
    auto l_mut = [phi](double x) mutable { phi += 0.6; return phi; };

    레퍼런스에 의한 캡처

    • 변수를 레퍼런스로 캡처할 수도 있다.
    phi = 2.5; xi = 0.2;
    auto px = [&phi, &xi](double x) { return sin(phi * x) + cos(x) * xi; };
    phi = 3.5; xi = 1.2;
    a = fin_diff(px, 1., 0.001);    //이제는 phi=3.5, xi = 1.2이다
    • [=] : 복사를 통해 모든 변수를 캡처
    • [&] : 레퍼런스를 통해 모든 변수를 캡처
    • [=, &a, &b, &c] : 복사를 통해 모든 변수를 캡처하되 a,b,c는 레퍼런스를 통해 캡처
    • [&, a, b, c] : 레퍼런스를 통해 모든 변수를 캡처하되 a,b,c는 복사를 통해 캡처
      • 그러나 모든 변수를 캡처하는 기능은 부실 레퍼런스의 위험을 증가시키고
      • 정적 변수나 멤버 변수를 무시한다.

    일반화된 캡처

    • 캡처의 일반화는 일반화된 캡처(Init Capture)를 통해 이루어진다.
    • 일반화된 캡처는 변수를 클로저로 옮기고 새로운 이름을 부여할 수 있다.
    auto F = make_uniqu<Mat>(Mat{ {1., 0.5}, {0.5, 1./3.} });
    
    //unique_ptr은 복사할수 없으므로 클로저가 소유할수 있게 옮겨야한다.
    auto apply_hilbert = [F = move(F)](const Vec& x) { return Vec(*F * x); };
    
    int x = 4;
    auto y = [&r = x, x = x + 1]()
    {
      r += 2;        //x = 6
      return x + 2;  //여기서 x = x+1 = 5이고 따라서 7을 반환
    }();
    
     cout << x << endl;
     cout << y << endl;
     cout << x << endl;
     //6 7 6 이 출력됨
    auto y = [&r = x, x = r + 1] {};  //오류 : 컨텍스트에 r이 없음

    제네릭 람다 (C++14)

    • auto 키워드를 사용함으로 리턴 타입을 명시적으로 선언하지 않아도 된다.
    template<typename T
    void reverse_sort(T& c)
    {
    	sort(begin(c), end(c), [](auto x, auto y) { return x > y); }); 
    }
    반응형

    댓글

    Designed by JB FACTORY