2020. 11. 30. 23:11ㆍ컴퓨터 수업/C++
람다(Lambda의 의미란?)
수학적으로 어떤 중간과정 변수를 의미한다.
함수 포인터
함수도 인자로 주고받을 수 있다. 이때 꼭 함수 포인터라고 하지 않아도 좋지만,
포인터로 유사 pass by reference를 구현할 수도 있다는 것을 알았으면 좋겠다.
Call by Value
#include <iostream>
using namespace std;
int sum(int x, int y) { return x + y; }
int mult(int x, int y) { return x * y; }
int abcd(int a, int b, int c) { return a + b + c; }
int evaluate(int(f)(int, int), int x, int y) {
return f(x, y);
}
int main() {
cout << evaluate(sum, 2, 3) << endl;
cout << evaluate(mult, 2, 3) << endl;
//cout << evaluate(&abcd,2,3)<<endl; // error
}
evaluate는 인자로 int (f) (int, int) 를 받는데
이는 (반환형) (변수(함수)명) ( 파라미터 1, 파라미터 2 )인 variable과 유사하게 기능하는 function이다. 즉, arbitrary function이 되는 것이다.
Call by Reference ( 유사 reference이며, 포인터 변수를 이용할 것)
#include <iostream>
using namespace std;
int sum(int x, int y) { return x + y; }
int mult(int x, int y) { return x * y; }
int abcd(int a, int b, int c) { return a + b + c; }
int evaluate(int(*f)(int, int), int x, int y) {
return (*f)(x, y);
}
int main() {
cout << evaluate(&sum, 2, 3) << endl;
cout << evaluate(&mult, 2, 3) << endl;
//cout << evaluate(&abcd,2,3)<<endl; // error
}
당연하게도 &를 이용한 Call by reference도 제대로 기능한다.
람다 펑션
키워드는 "로컬"이다. 로컬 하게 임시로 사용하고자 하는 함수가 필요하거나,
closure를 이용한, Generic 한 변수 호출이 필요하고자 하면... -> 이건 사실 감이 잘 안 오네.
참고로 lambda function을 이용해야 하거나, 함수를 인수로 넘겨야 되는 상황이 오면,
함수는 포인터 변수로 주고받는 것이 안전하다.
예를 들어,
void(f)(int x, int y){cout << x * y << endl;};
은 에러가 난다. f가 포인터 변수 형식이 아니기 때문이다.
기본형
#include <iostream>
using namespace std;
int sum(int x, int y) { return x + y; }
int mult(int x, int y) { return x * y; }
int abcd(int a, int b, int c) { return a + b + c; }
int evaluate(int(*f)(int, int), int x, int y) {
return (*f)(x, y);
}
int main() {
// lambda 함수 : 이름없이 일회용으로 사용되는 함수
// - form: [](입력변수) -> 리턴타입 {본문}
// ex) sum(): [](int x, int y) -> int { return x + y; }
//실제 사용
cout << evaluate([](int x, int y)-> int{return x + y;}, 2 , 3) << endl;
}
// lambda 함수 : 이름 없이 일회용으로 사용되는 함수
// - form: [ ](입력 변수)입력변수 -> 리턴타입 {본문}
// ex) sum(): [ ](int x, int y) -> int { return x + y; }
Simplified form
#include <iostream>
using namespace std;
int sum(int x, int y) { return x + y; }
int mult(int x, int y) { return x * y; }
int abcd(int a, int b, int c) { return a + b + c; }
int evaluate(int(*f)(int, int), int x, int y) {
return (*f)(x, y);
}
int main() {
// simplified lambda 함수표현 : [] (입력변수) {본문}
// ex) mult(): [](int x, int y){ return x * y;}
//실제 사용
cout << evaluate([](int x, int y){return x*y;},2,3) << endl;
}
여기서 짚고 넘어가야 할 것이 있다.
반환형 추론에서 decltype이라는 키워드를 사용해 본 적이 있는데, 이는 반환형이 어떤지 모르는 템플릿 함수에 대해서 사용하였다. 예시는 다음과 같다.
template<class T, class U>
auto sum(T t, U u)->decltype(t+u){
return t + u;
}
짚어야 할 것)
->는 어떻게 사용되는 것인가?
함수는 포인터 변수로 담을 수 있다. 즉 1-depth 까지는 존재하는 변수로 이루어져 있는 것이다.
그런데, 그 함수에 접근하는 operator가 -> 이다.
따라서 밑의 decltype에서 ->라는 operator는 sum이라는 함수에 접근하여 (어쩌면 class와 유사하게 동작한다, 물론 c++은 그렇지 않지만 파이썬에선 그러니) 반환 type을 declare 한다.
마찬가지로 위의 Simplified 하지 않은 lambda function도 마찬가지다.
[ ] (int x, int y)라는 임의의 함수에 접근하여 (->)
그 값 자체를 int {return x + y ;}라는 값으로 바꾼 것이다.
람다 펑션의 생성과 호출을 동시에
[](int x, int y){cout << x * y << endl;}(2,3);
람다 펑션을 anonymous struct나 class라고 생각하고, 변수에 대입.
int(*f)(int,int) = [](int x, int y){ return x*y;};
줄이기
auto f = [](int x, int y){ return x*y;};
그리고 그 사용
cout << evaluate(f,2,3) << endl;
cout << f(2,3) << endl;
Closure [ ]
Summary
[ ] : closure 외부 변수를 lambda 함수 내부로 전달
[a] : 변수 a를 pass by value로 lambda 함수 내부에 전달
[&a] : 변수 a를 pass by reference로 lambda 함수 내부에 전달
[=] : 모든 외부 변수를 pass by value로 전달
[&] : 모든 외부변수를 pass by reference로 전달
전반적인 사용법
1. 일반 사용법
int main() {
int a = 10, b = 20;
[](int x){
cout << "x = " << x << endl;
}(10);
cout << "main::a = " << a << endl;
return 0;
}
2. [a] 사용법
a는 읽기 전용으로 불러왔기 때문에, 수정할 수 없다. (check red_underLine)
3. [&a] 사용법
int main() {
int a = 10, b = 20;
[&a](int x){
cout << "x = " << x << endl;
a = 1000;
cout << "a = " << a << endl;
}(10);
cout << "main::a = " << a << endl;
return 0;
}
4. [&a, b] 사용법
int main() {
int a = 10, b = 20;
[&a, b](int x){
cout << "x = " << x << endl;
a = 1000;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}(10);
cout << "main::a = " << a << endl;
return 0;
}
발생하는 문제.
closure에 인수를 집어넣어 lambda function을 만들게 되면,
lambda function은 더 이상 함수 포인터형 변수가 아니다. 이때부터는 객체 포인터를 갖게 된다.
C++의 단점 중 하나로 불리는데, 파이썬의 경우에는 일반 변수가 없고 모두 객체이다.
그러나 C++의 경우는 일반 변수와 포인터 변수, 그리고 포인터 객체 등 모두 나누어져 있기 때문에 이런 것을 보완할 방법이 필요하다.
따라서, evaluate의 인자 중 arbitrary function pointer를 받는 곳을 수정할 필요가 있는데
이를 C++의 STL 중 하나인 <functional> 이 해결해 준다.
이런 해결점을 포함한 코드는 다음과 같다.
#include <iostream>
#include <functional>
using namespace std;
int evaluate(function<int(int)> f, int x) {
return f(x);
}
int main() {
int a = 10, b = 20;
cout << evaluate([a](int x){return x*a;},2);
return 0;
}
'컴퓨터 수업 > C++' 카테고리의 다른 글
#include <algorithm> (0) | 2020.12.01 |
---|---|
템플릿★ (0) | 2020.11.19 |
템플릿(임시) (0) | 2020.11.19 |
virtual , 업/다운 캐스팅, 정적타입바인딩, 추상클래스 ★ (0) | 2020.11.10 |
C++ 상속 (0) | 2020.11.05 |