C++ 에서 Lambda 와 Functional feature

목적

  • 람다를 ‘어떻게’ 쓰는지 에 대해 설명합니다.
  • 람다가 ‘뭔지’, ‘어디에’ 쓰는지에 대해 설명하지 않습니다.
    • 스스로 찾아보도록 합시다.
    • 나도 모름 ¯\( ͡° ͜ʖ ͡°)

C++ 에서 Lambda syntax

[=] ( ) mutable ->int { ... }
——— ——— ——————— ————— ———————
 1   2     3      4      5
  1. Capture
  2. Parameter 목록 (Optional?)
  3. Mutable specifier (Optional)
  4. Return type (Optional)
  5. 람다 본문

E~~~asy Example

void main() {
    int a = 1;
    auto add = [=](int b)->int{ return a+b; };
    std::cout<<"1+2:"<<add(2);
}

출력

1+2:3

Capture

  • [&] : lambda 함수 외부 변수를 참조로 가져옴 (call-by-ref)
  • [=] : lambda 함수 외부 변수를 값으로 가져옴 (call-by-val)
  • [x] : 변수 x 를 값으로 가져옴
  • [=, &x] : lambda 함수 외부 변수를 값으로 가져오되, 변수 x 는 참조로 가져옴
  • [x, &y, &z] : 변수 x 는 값으로, y, z 는 참조로 가져옴

Capture Demo

int x(5),y(6),z(7);
int* p = &z;
[=, x, &y, p](){
    printf("%d %d %d %d\n", x, y, z, *p);
    // x=15, z=17;  // call-by-val can not modified
    y=16, *p=27;    // p point to z
}();
printf("%d %d %d %d\n", x, y, z, *p);

출력

5 6 7 7
5 16 27 27

Capture Quiz

int a = 0;
int* p = &a;
[=, &p](){
    *p = 1;
    std::cout<<" inside lambda> a:"<<a<<", p:"<<*p<<std::endl;
}();
std::cout<<"outside lambda> a:"<<a<<", p:"<<*p<<std::endl;

출력

 inside lambda> a:?, p:?
outside lambda> a:?, p:?

Parameter list

auto pplus = [](int a, int b){
    printf("%d+%d=%d\n",a, b, a+b);
};

pplus(2, 3);

출력

2+3=5

Mutable specifier

int a=1, b=2,c=0;
[=](){c=a+b; std::cout<<"c : "<<c<<std::endl;}();
//출력
//> error: cannot assign to a variable captured by copy in a non-mutable lambda
//> [=](){c=a+b; std::cout<<"c : "<<c<<std::endl;}();
//>       ~^
int a=1, b=2,c=0;
[=]() mutable {c=a+b; std::cout<<"c : "<<c<<std::endl;}();
//출력
//> 3

Return type

  • 💦 Under construct


Lambda body

  • 이걸 굳이 설명할 필요가… ( ̄ ‘i  ̄;)

height:400px


다음장 부터는 뭘 좋아할지 몰라서 다 준비해본 것들임.

두서없음에 주의 💦

bg right:40% 80%


(C++ 의) Lambda 란?

  • 함수 객체와 동일한 익명함수 이다.
    • 함수 객체 (functor)
        struct Add { int operator()(int a, int b){ return a + b;} };
        Add adder;
        std::cout<<adder(1, 2);
      
      • 상태를 갖음
      • class 선언을 해야 함

Functor vs Lambda

class Add {
    public:
    int operator()(int a, int b) {
        return a + b;
    }
};
std::cout<<Add()(1, 2);

VS

std::cout<<[](int a, int b){ return a + b; }(1, 2);

this capture

  • class member variable 에 접근하기 위해서는 this capture 를 사용
// include string, memory, vector, algorithm

struct Sorted {
    Sorted(std::vector<int> v):v_(v){}
    std::function<int()> get(std::string order) {
        if(order=="asc") return [this](){ std::sort(v_.begin(), v_.end(), std::less<int>()); return v_[0]; };
        return [this](){ std::sort(v_.begin(), v_.end(), std::greater<int>()); return v_[0]; };
    }
    private:
    std::vector<int> v_;
};

int main() {
    Sorted sort({3, 1, 2, 4});
    std::cout<<"smallest : "<<sort.get("asc")()<<std::endl;
    std::cout<<"biggest  : "<<sort.get("dec")()<<std::endl;
}

this capture comparison

height:500px


Lambda 재귀

std::function<int(int)> f = [&f](int n)->int{ return n<=1 ? 1 : n*f(n-1); };
std::cout<<"factorial(5) => "<<f(5); // 5*4*3*2*1=>120
  • Lambda 를 재귀로 사용할 경우, 람다 객체 타입이 명시되어야 함
    • auto 사용시, capture 시점에 타입이 추론 되지 않기 때문에, 타입을 명시해야 함.
// std::functional( return_type <params, ..> ) // Example
std::functional(int<char,float,void*>) fn = [](char a, float b, void* c)->int{
    printf("%c, %f, %p", a, b, c);
};

Functional Programming Concept

  • 순수 함수 (Pure function)
  • 재귀 (Recustion, not loop)
  • 참조 투명성 (Referential Transparency)
  • 1급 객체 함수, 고차 함수 (First-class, Higher-order function)
  • 불변 변수 (Immutable)

1급 객체 (first class citizen) 란?

  1. All items can be the actual parameters of functions
  2. All items can be returned as results of functions
  3. All items can be the subject of assignment statements
  4. All items can be tested for equality. (💦)

Note. Primitive 변수는 어떤 언어에서나 1급 객체.


1급 객체 on C++

auto plus2factory = [](std::function<int(int)> f)->std::function<int(int)>{
    return [=](int y)->int{ return f(y) + 2; };
};

auto plus1 = [](int x)->int{ return x+1; };
auto plus2 = plus2factory(plus1);
std::cout<<"5+1+2 => "<<plus2(5)<<std::endl;
// 출력
// 5+1+2 => 8

C++ 14 이상

  • 앞서 예제에서 파라메터와 반환 타입을 auto 로 사용 가능
auto plus2factory = [](auto f)->auto{
    return [=](int y)->int{ return f(y) + 2; };
};

auto plus1 = [](int x)->int{ return x+1; };
auto plus2 = plus2factory(plus1);
std::cout<<"5+1+2 => "<<plus2(5)<<std::endl;
// 출력
// 5+1+2 => 8

Closure using Lambda

  • 일급 객체 함수(first-class functions)의 개념을 이용하여 스코프(scope)에 묶인 변수를 바인딩 하기 위한 일종의 기술
auto startAt = [](int x) { return [x](int y){ return x+y; }; };
auto closure1 = startAt(1);
auto closure2 = startAt(2);

std::cout<<"result of closure start at 2 => "<<closure2(1)<<std::endl;
std::cout<<"result of closure start at 1 => "<<closure1(1)<<std::endl;

References


comments powered by Disqus