파셜 어플리케이션과 커링에 대해 알아본다. C++ 17 기준을 요한다.
파셜 어플리케이션
N개의 인자를 가진 함수에서 하나의 인자를 바인딩(결정)하여 N-1개의 인자를 가진 함수를 만들어내는 것.
두 개의 인자를 가지는 add 함수에 첫번째 함수 인자를 결정하여 increment 함수를 만든다.
auto add = [](const int first, const int second) { return first + second; };
//auto increment = partialApplication(add, 1);
// 위 코드는 다음과 동치이다.
auto increment = [](const int second) { return 1 + second; };
C++ 파셜 어플리케이션
기존 함수를 재사용하는 방법
TEST_CASE("Increments using manual partial application") {
auto increment = [](const int value) { return add(value, 1); };
CHECK_EQ(43, increment(42));
}
increment는 add 함수를 사용함으로써 파셜 어플리케이션을 만든다.
bind 함수 (functional 헤더)
bind 함수는 말 그대로 타겟 함수에 일부 인자들을 미리 결정하는 것이 가능하다.
using namespace std::placeholders;
TEST_CASE("Increments using bind") {
auto increment = bind(add, 1, _1);
CHECK_EQ(43, increment(42));
}
std::placeholders::_1, _2 와 같은 타입은 std::bind를 사용할 때 바인딩하고 나머지 결정 인수들을 표현한다.
일반 클래스 인스턴스의 멤버함수를 파셜 어플리케이션으로 만들기
class AddOperation {
private:
int first;
int second;
public:
AddOperation(int first, int second)
:
first(first), second(second) {
}
int add() { return first + second; }
};
TEST_CASE("Bind member method") {
AddOperation operation(41, 1);
auto add41And1 = bind(&AddOperation::add, operation);
CHECK_EQ(42, add41And1);
}
bind로 인스턴스를 넘긴다.
커링 Currying
N개의 인자를 가진 함수를 하나의 인자를 가진 N개 함수로 분해하는 과정을 커링이라고 하며 변수 캡처나 파셜 어플리케이션을 활용해 커링할 수 있다.
f(x1, x2, x3, ... , xn) = f(x1)(x2)...(xn)으로 표현 가능하다.
이를 일반화하여 n개의 인수를 가진 함수는 n-1개의 인수를 가진 함수와 1개의 인수를 가진 함수로 분해 가능하다.
f(x1, x2, ..., xn)
= f(x1,...,xn-1)*f(xn)
= f(x1,...,xn-2)*f(xn-1)*f(xn)
= ...
= f(x1)*f(x2)*...*f(xn-1)*f(xn)
하나씩 인수를 분해하는 과정을 람다와 bind를 통해서 만들 수 있다.
예를 들면 3개의 인수를 가진 함수는 2개의 인수를 가진 함수와 1개의 인수를 가진 함수로 분해한다.
auto simpleCurry3 = [](auto f) {
return [f](auto x, auto y) { return bind(f, x, y, _1); };
};
다시 2개의 인수를 가진 함수를 1개의 인수를 가진 두 함수로 분해한다.
auto simpleCurry2 = [](auto f) {
return [f](auto x) { return bind(f, x, _1); };
};
이제 3개의 인수를 가진 함수 addThree를 커링 시켜본다.
auto addThree = [](const int first, const int second, const int third) {
return first + second + third;
};
TEST_CASE("Add three with partial application curry") {
auto addThreeCurry = simpleCurry2(simpleCurry3(addThree));
CHECK_EQ(6, curry3(addThree)(1)(2)(3));
};
각각 simpleCurry를 잘 감싼 currying 람다 함수를 통해 간단히 표현할 수 있다.
auto curry2 = [](auto f) {
return simpleCurry2(f);
};
auto curry3 = [](auto f) {
return curry2(simpleCurry3(f));
};
auto addThree = [](const int first, const int second, const int third) {
return first + second + third;
};
TEST_CASE("Add three with partial application curry") {
auto addThreeCurry = curry3(addThree);
CHECK_EQ(6, curry3(addThree)(1)(2)(3));
};
'C++ > Advanced C++' 카테고리의 다른 글
[C++] Memory Pool (1) | 2024.03.31 |
---|---|
[Functional C++] 함수형 프로그래밍 (2) 합성 함수 (0) | 2022.10.04 |
[Functional C++] 함수형 프로그래밍 (1) 불변성과 순수 함수 (0) | 2022.10.03 |
[C++] Protobuf (Google Protocol Buffer) 라이브러리 (0) | 2022.09.27 |
[C++] Redis 사용하기 (0) | 2022.08.18 |