반응형
find_if는 전달된 범위에서 지정된 조건을 만족하는 첫번째 요소를 리턴해줍니다.
함수 정의
함수의 정의를 보시면 순차 검색을 통해서 조건에 만족하는 iterator를 리턴합니다.
// 자세한 구현 사항은 컴파일러를 통해서 정의된 구문을 참조 바랍니다.
template<class _InIt, class _Pr>
_InIt find_if(_InIt _First, const _InIt _Last, _Pr _Pred) {
for (; _First != _Last; ++_First) {
if (_Pred(*_First))
break;
}
return _First;
}
매개변수 | 설명 |
_First | 검색할 범위의 첫번째 요소의 iterator |
_Last | 검색할 범위의 마지막 요소의 iterator |
_Pred | 검색 조건을 정의하는 사용자 정의 함수 객체입니다. bool(const T& arg)의 형식을 갖춰야 합니다. |
기본 사용 구문
아래의 예제는 컨테이너에서 5를 초과하는 요소를 찾는 간단한 예제 구문입니다.
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int> ints = { 1,2,3,4,5,6,7,8,9,10 };
// 5 초과 값을 가진 요소를 찾으시오
const auto findIt = find_if(begin(ints), end(ints), [](int value) { return 5 < value; });
std::cout << "findIt : " << *findIt << std::endl;
return 0;
}
// 출력 : 6
응용 사용 구문
이번에는 find_if를 실무에서 발생할 수 있는 간단한 사례를 들어서 설명 드립니다.
"주어진 AbilityTicket에서 weight 기반으로 랜덤 뽑기를 구현해주세요. " 다음과 같은 조건으로
개발 요청이 들어 온 경우 컨테이너에서 weight를 검사하여 찾는 구문에 find_if를 사용할 수 있습니다.
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
struct AbilityTicket
{
AbilityTicket(const std::string& name, uint16_t value, uint16_t weight)
: name(name), value(value), weight(weight)
{}
std::string name; // 이름
uint16_t value; // 값
uint16_t weight; // 비중
};
vector<AbilityTicket> AbilityLotteryContainer = {
{"attack", 5, 5},
{"defense", 5, 5},
{"critical", 2, 5},
{"reduction", 3, 3},
{"hp", 100, 10},
{"mp", 50, 10}
};
// 리턴값 : 0 ~ (base - 1), 간략한 랜덤 함수
const int getRandomValue(const int base)
{
return rand() % base;
}
int main()
{
srand((unsigned int)time(nullptr));
// 요청 : AbilityLotteryContainer에서 weight를 기반으로 랜덤 옵션 하나를 뽑아주세요
uint32_t totalWeight{ 0 };
std::vector<uint32_t> accumulateWeights;
accumulateWeights.reserve(AbilityLotteryContainer.size());
// ability별 가중도를 accumulateWeights 추가 합니다.
for (auto& abilityTicket : AbilityLotteryContainer)
{
totalWeight += abilityTicket.weight;
accumulateWeights.push_back(totalWeight);
}
// 랜덤 값 추출
const auto lot{ static_cast<uint32_t>(getRandomValue(totalWeight)) };
// lot 값을 기반으로 ability를 찾습니다.(이곳에서 find_if를 사용합니다.)
const auto findIt = std::find_if(begin(accumulateWeights), end(accumulateWeights),
[lot](const auto & accumulateWeight)
{ return lot < accumulateWeight; });
// 같은 기능을 lower_bound를 통해서 구현 할 수도 있습니다.
// const auto findIt2 = std::lower_bound(begin(accumulateWeights), end(accumulateWeights), lot + 1);
// 값을 찾지 못하였는지 체크
if (findIt == end(accumulateWeights)) {
std::cout << "not find" << '\n';
return 0;
}
// 찾은 인덱스를 구합니다.
const auto index = findIt - begin(accumulateWeights);
std::cout << "lot : " << lot << '\n';
std::cout << "find Ability : " << AbilityLotteryContainer[index].name << ", " << AbilityLotteryContainer[index].value << " index : " << index << '\n';
return 0;
}
데이터가 정렬되어 있다면 비슷한 기능을 lower_bound 를 통해서도 구현 할 수 있습니다.
lower_bound는 이진 탐색으로 구현되기에 검색 데이터가 많다면 find_if보다 빠른 검색 속도를 제공합니다.
성능차이가 궁금해서 제 로컬 환경에서 테스트 했을 때 결과를 공유 드리면 다음과 같습니다.
- Release 빌드, 최적화를 피하기 위해서 결과 데이터 저장
- 대략적인 성능 측정 및 테스트 도중에 오차가 있을수 있음을 감안 부탁 드립니다.
Ticket 갯수 | 성능차이( find_if와 lower_bound의 성능 측정) |
100개 | 약 2배 |
300개 | 약 3배 |
600개 | 약 6배 |
1200개 | 약 10배 |
참고
반응형
'c++ > c++' 카테고리의 다른 글
[c++] 반환값 최적화 (RVO, return-value-optimization) (0) | 2024.09.26 |
---|---|
[c++ 예제] 멀티스레드에 안전한 notify_queue 클래스 (0) | 2020.07.10 |
[c++] 키워드 (0) | 2020.03.08 |
[c++] vector (0) | 2019.10.03 |
[c++] chrono를 사용한 수행 시간 출력 클래스 (0) | 2019.09.20 |