다음과 같이 코드를 짜면, memoization DP를 전역 변수 없이, 불필요하게 많은 함수 인자 없이 안전하게 짤 수 있다.
#include <bits/stdc++.h>
int main() {
std::vector memo(100, std::optional<int>());
std::function<int(uint32_t)> fibonacci = [&fibonacci, &memo](uint32_t i) -> int {
if (memo[i].has_value()) {
return memo[i].value();
}
return i <= 1 ? i : memo[i].emplace(fibonacci(i - 1) + fibonacci(i - 2));
};
std::cout << fibonacci(10) << std::endl;
}; // 55
이 코드에 적용된 여러 가지 트릭은 다음과 같다.
1. 템플릿 타입 추론(C++17) : memo의 타입은 std::vector<std::optional<int>>지만, <> 안에 있는 것을 적어줄 필요 없이 std::vector만 적어도 memo 생성자의 2번째 인자 타입으로부터 컴파일러가 알아서 타입을 생성해 준다.
마찬가지로 다음과 같이 3중 벡터를 간편하게 짤 수도 있다.
std::vector memo(100, std::vector(100, std::vector(100, -1)));
2. 람다 재귀함수 : 람다함수로도 재귀함수를 짤 수 있다. 람다의 타입을 std::function으로 명시해 준 뒤, 캡처 리스트에 본인의 참조자를 넣어 준다.
3. std::optional<T>(C++17) : 값이 있을 수도 있고 없을 수도 있는 상황을 나타내는 객체. 사실 fibonacci 같이 우리가 잘 알고 있는 재귀함수들은 memo의 initialization으로 -1 같은 음수 값을 넣어 줘도 괜찮지만, 함수의 리턴값의 범위를 잘 모른다면 std::optional<T> 객체를 써 주는 것이 안전할 것이다.
'Computer Science > C++' 카테고리의 다른 글
충격!) C++에서 Rust의 블록 표현식과 유사한 구문을 사용할 수 있다?! (0) | 2024.03.04 |
---|---|
[C++] 객체지향, 제네릭 세그먼트 트리 라이브러리 구현하기 (0) | 2023.10.03 |
C++ 버전이 바뀌면서 의미가 달라진 키워드들 (0) | 2023.01.09 |
[C++] TMP로 컨테이너 내부 타입 정보 얻어오기 [템플릿 메타프로그래밍] (0) | 2022.10.22 |
[C++] 프록시(Proxy) 디자인 패턴 (feat. bitset) (0) | 2022.09.18 |
댓글