Functional Programming Introduction
- C++ => OOP + High Performance
-> high performance를 위해서 function programming 지원(C++11의 lambda expression)
-> functional programing은 함수를 변수처럼 다루는 것
-> call-back, state 등이 없어서 side effect가 없다
- javascript 예시 (functional programming이 발전한 언어)
-> 해당 구현의 장점은 plus3이라는 함수는 "오직" 3을 더하는 용도로만 쓸 수 있고, 내부 변수에는 접근이 불가능 하다.
-> 함수 자체를 다른 함수에게 넘겨줄 수 있는 기능이 가능하다.
- C++에서 javascript 예시와 유사하게 구현한 예시
-> function object를 이용
#include<iostream>
class Plus
{
public:
explicit Plus(int a)
:localVar{ a }
{}
int operator() (int x)const
{
return localVar + x;
}
private:
int localVar;
};
int main()
{
Plus plus3{ 3 }; //function object
Plus plus5{ 5 };
std::cout << plus3(10) << std::endl; //13
std::cout << plus5(10) << std::endl; //15
return 0;
}
Lambda Expression
- function object와 lambda function은 어셈블리 코드로 분석하면 같은 동작을 수행한다.(같은 메모리 공간 차지)
- lambda function : [capture](paramter){body} 형식으로 구성
#include<iostream>
class Plus
{
public:
explicit Plus(int a)
:localVar{ a }
{}
int operator() (int x)const
{
return localVar + x;
}
private:
int localVar;
};
int main()
{
Plus plus3{ 3 }; //function object
auto lambdaPlus3 =
[localVar = 3](int x) //lambda function
{
return localVar + x;
};
}
- capture 모드의 종류
- capture by value
-> lambda function 안에서 쓰는 variable이나 object를 value로 캡쳐
-> deep copy
- capture by reference
-> capture하려는 값이 너무 큰 경우 reference를 이용하면 효율적이다.
- capture by this
int three{ 3 }; // local variable
auto lambdaPlus3 = [three](int x) // capture by value
{
return x + three; //three : value로 capture(복사)
};
VERYLARGEOBJECT object;
auto lambdaLarge = [&object](int x) // capture by reference
{
return x + object;
};
- = 와 & 를 이용해서 자동적으로 캡쳐해주는 기능
-> capture할 값이 너무 복잡하거나 많은 경우가 아니라면 explicit하게 적어주는 방식이 좋다.
auto lambda1 = [=](int x) //value
{
return x + three;
};
auto lambda2 = [&](int x) //reference
{
return x + object;
};
Lambda This
- lambda expression function body안에서는 바깥 스코프로는 접근 불가능하다.
- automatic reference capture를 이용하면 접근 가능해진다.
- automatic reference capture :
[&]()
- automatic reference capture의 경우 lambda function body안에서 member variable, member function을 call 한다면 컴파일러가 알아서 this로 캡쳐를 한다.
[this]()
#include<iostream>
class Cat
{
public:
explicit Cat(int age)
:mAge{age}
{}
void speak() const
{
std::cout << "CAT" << std::endl;
}
void test() const
{
auto lambda = []()
{
std::cout << "lambdaFunction" << std::endl;
std::cout << mAge << std::endl; //컴파일 에러
speak(); //컴파일 에러
};
lambda();
}
private:
int mAge;
};
int main()
{
Cat kitty{ 1 };
kitty.test();
return 0;
}
- this 캡쳐로 동작하는 방식
-> 위의 main 함수의 동작이 문제없이 동작한다.
auto lambda = [&]() // auto lambda = [this]()와 같은 의미
{
std::cout << "lambdaFunction" << std::endl;
std::cout << this->mAge << std::endl;
this->speak();
};
lambda();
Lambda expression + STL
#include<iostream>
#include<vector>
#include<algorithm>
int main()
{
std::vector<int>nums{ 1,2,3,4,5,6,7,8,9,10 };
auto lambdaAdd10 = [](int& n)
{
n += 10;
};
int n = 10;
lambdaAdd10(n);
std::cout << n << std::endl; //20
//for_each
std::for_each(nums.begin(), nums.end(), lambdaAdd10);
for (int num : nums)
{
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
- lambda function과 for_each() 함수를 합쳐서 표현하는 방식
std::for_each(nums.begin(), nums.end(), [](int& n)
{
n += 10;
});
Higher order function
- Higher order function : 함수를 argument로 받는 혹은 함수를 결과로 내는 function
- 사용한 STL 함수는 차후에 정리할 예정
#include<iostream>
#include<algorithm>
#include<vector>
#include<numeric>
int main()
{
std::vector<int> nums{ 1,2,3,4,5,6,7,8,9,10 };
//========================================================
//홀수 걸러줌
auto filterOdd = [](int n)
{
return n % 2 == 1;
};
nums.erase(std::remove_if(nums.begin(), nums.end(), filterOdd),nums.end());
/*nums.erase(std::remove_if(nums.begin(), nums.end(), [](int n)
{
return n % 2 == 1;
}), nums.end());*/
//========================================================
//내림차순 sorting
std::sort(nums.begin(), nums.end(), [](int a, int b)
{
return a > b;
});
//========================================================
//5에 가까운 수부터 sorting
std::sort(nums.begin(), nums.end(), [](int a, int b)
{
return std::abs(a - 5) < std::abs(b - 5);
});
//========================================================
//reduce 이용해서 합 구현
int sum = std::reduce(nums.begin(), nums.end(), 0, [](int a, int b)
{
return a + b;
});
std::cout << sum << std::endl;
//========================================================
//reduce -> 곱 구현
int multi = std::reduce(nums.begin(), nums.end(), 1, [](int a, int b)
{
return a * b;
});
std::cout << multi << std::endl;
//========================================================
}
std::function
- functionObj와 lambda function은 변수처럼 가리킬 수 있다.
- freeFunction은 함수처럼 호출은 가능하지만 변수처럼 가리킬 수 없다.
-> function pointer 와 std::function을 통해서 구현
#include<iostream>
#include<functional>
#include<vector>
class FunctionObj
{
public:
void operator() (int i)
{
std::cout << "functionObj " << i << std::endl;
}
};
void freeFunction(int i)
{
std::cout << "freefunction " << i << std::endl;
}
int main()
{
//일반 function
freeFunction(10); //freefunction 10 출력
//function object
FunctionObj functionObj;
functionObj(10); //functionObj 10 출력
//lambda function
auto lambdaFunc = [](int i)
{
std::cout << "lambda function " << i << std::endl;
};
lambdaFunc(10); //lambdaFunc 10 출력
return 0;
}
- function pointer
-> function pointer를 통해 일반 function도 argument로 넣을 수 있다.
...
void runFunction(int i, void (*fnPtr)(int))
{
(*fnPtr)(i);
}
...
int main()
{
void(*fnPtr)(int);
fnPtr = freeFunction();
(*fnPtr)(20); // freeFunction 20 출력
}
- std::function
-> #include <functional> 필요
-> function pointer, function object, lambda function등 모두를 넣을 수 있다.
-> 함수를 object처럼 쓸 수 있다.
...
void runFunctionBetter(int i, const std::function<void(int)> &fn)
{
fn(i);
}
void runFunctions(const std::vector<std::function<void(int)>>& functions)
{
int i = 0;
for (const auto& fn : functions)
{
fn(++i);
}
}
...
int main()
{
void(*fnPtr)(int);
fnPtr = freeFunction;
runFunctionBetter(10, fnPtr); //freeFunction 10 출력
runFunctionBetter(20, functionObj);//functionObj 20 출력
runFunctionBetter(30, lambdaFunc);//lambdaFunc 30 출력
std::vector<std::function<void(int)>> functions;
functions.emplace_back(freeFunction);
functions.emplace_back(functionObj);
functions.emplace_back(lambdaFunc);
runFunctions(functions);
return 0;
}