C++ 에서 Lambda 와 Functional feature
목적
- 람다를 ‘어떻게’ 쓰는지 에 대해 설명합니다.
- 람다가 ‘뭔지’, ‘어디에’ 쓰는지에 대해 설명하지 않습니다.
- 스스로 찾아보도록 합시다.
- 나도 모름 ¯\( ͡° ͜ʖ ͡°)/¯
C++ 에서 Lambda syntax
[=] ( ) mutable ->int { ... }
——— ——— ——————— ————— ———————
1 2 3 4 5
- Capture
- Parameter 목록 (Optional?)
- Mutable specifier (Optional)
- Return type (Optional)
- 람다 본문
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  ̄;)
다음장 부터는 뭘 좋아할지 몰라서 다 준비해본 것들임.
두서없음에 주의 💦
(C++ 의) Lambda 란?
- 함수 객체와 동일한 익명함수 이다.
- 함수 객체 (functor)
struct Add { int operator()(int a, int b){ return a + b;} }; Add adder; std::cout<<adder(1, 2);
- 상태를 갖음
- class 선언을 해야 함
- 함수 객체 (functor)
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
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) 란?
- All items can be the actual parameters of functions
- All items can be returned as results of functions
- All items can be the subject of assignment statements
- 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;