순서 사전 체인 (예 : std :: sort)
문제
함수 포인터, 함수 객체 (또는 람다 부스트 람다)를 std :: 정렬하여 정렬하려는 컨테이너 요소의 엄격한 약한 순서를 정의 할 수 있습니다.
그러나 때로는 (여러 번 이번에도 쳤을 정도로) "원시적"비교를 체인 할 수 있기를 원합니다.
연락처 데이터를 나타내는 객체 모음을 정렬하는 경우 사소한 예입니다. 때때로 당신은 분류하고 싶을 것입니다
last name, first name, area code. 다른 시간
first name, last name- 아직 다른 시간
age, first name, area code... 등
이제 각 사례에 대해 추가 기능 객체를 쓸 수 있지만, 특히 각 비교가 덜 사소한 경우 건조 원칙을 위반합니다.
비교 함수의 계층 구조를 작성할 수 있어야하는 것 같습니다. 낮은 레벨은 단일, 원시적 비교 (예 : 이름 <이름)를 수행하면 더 높은 레벨을 연속적으로 호출합니다 (아마 && 단락 평가를 사용하려면 복합 기능을 생성합니다.
이 접근법의 문제점은 STD :: 정렬이 이진 술어를 가져옵니다. 술어는 부울 만 반환 할 수 있다는 것입니다. 따라서 구성하는 경우 "거짓"이 평등을 나타내는 지 또는 더 큰지 알 수 없습니다. 당신은 당신의 하위 레벨 predicates가 3 개의 상태로 INT를 반환하게 할 수 있지만, std :: 정렬과 함께 사용되기 전에 더 높은 수준의 곤경에 처한 사람들을 포장해야합니다.
전혀, 이것들은 극복 할 수없는 문제가 아닙니다. 그것은 단지 더 어려워 보입니다. 그리고 확실히 도우미 라이브러리 구현을 초대합니다.
따라서 여기서 기존 도서관 (STD 또는 부스트 라이브러리라면)을 도울 수있는 도서관 (esp.)을 아는 사람이 있습니까?
업데이트
일부 의견에서 언급했듯이 - 나는 이것을 관리하기 위해 수업의 구현을 계속 작성했습니다. 상당히 최소화되어 있으며 일반적으로 몇 가지 문제가있을 수 있습니다. 그러나 관심있는 사람이라면 누구나 수업이 여기에 있습니다.
그리고 일부 도우미 기능 (템플릿 Args를 지정할 필요가 없음)은 다음과 같습니다.
해결책
그렇게 작은 체인 시스템을 구축 할 수 있습니다.
struct Type {
string first, last;
int age;
};
struct CmpFirst {
bool operator () (const Type& lhs, const Type& rhs) { return lhs.first < rhs.first; }
};
struct CmpLast {
bool operator () (const Type& lhs, const Type& rhs) { return lhs.last < rhs.last; }
};
struct CmpAge {
bool operator () (const Type& lhs, const Type& rhs) { return lhs.age < rhs.age; }
};
template <typename First, typename Second>
struct Chain {
Chain(const First& f_, const Second& s_): f(f_), s(s_) {}
bool operator () (const Type& lhs, const Type& rhs) {
if(f(lhs, rhs))
return true;
if(f(rhs, lhs))
return false;
return s(lhs, rhs);
}
template <typename Next>
Chain <Chain, Next> chain(const Next& next) const {
return Chain <Chain, Next> (*this, next);
}
First f;
Second s;
};
struct False { bool operator() (const Type& lhs, const Type& rhs) { return false; } };
template <typename Op>
Chain <False, Op> make_chain(const Op& op) { return Chain <False, Op> (False(), op); }
그런 다음 사용하십시오 :
vector <Type> v; // fill this baby up
sort(v.begin(), v.end(), make_chain(CmpLast()).chain(CmpFirst()).chain(CmpAge()));
마지막 줄은 약간의 장점이지만 의도 한 것이 분명하다고 생각합니다.
다른 팁
이것을 처리하는 한 가지 기존의 방법은 여러 패스로 정렬하고 안정적인 종류를 사용하는 것입니다. 그것을주의해라 std::sort
일반적으로 ~ 아니다 안정적인. 그러나 거기 있습니다 std::stable_sort
.
즉, 나는 트리 스테이트를 반환하는 함수 주위에 래퍼를 쓸 것입니다 (더 적은, 평등, 더 큰).
당신은 이것을 시도 할 수 있습니다 :
용법:
struct Citizen {
std::wstring iFirstName;
std::wstring iLastName;
};
ChainComparer<Citizen> cmp;
cmp.Chain<std::less>( boost::bind( &Citizen::iLastName, _1 ) );
cmp.Chain<std::less>( boost::bind( &Citizen::iFirstName, _1 ) );
std::vector<Citizen> vec;
std::sort( vec.begin(), vec.end(), cmp );
구현:
template <typename T>
class ChainComparer {
public:
typedef boost::function<bool(const T&, const T&)> TComparator;
typedef TComparator EqualComparator;
typedef TComparator CustomComparator;
template <template <typename> class TComparer, typename TValueGetter>
void Chain( const TValueGetter& getter ) {
iComparers.push_back( std::make_pair(
boost::bind( getter, _1 ) == boost::bind( getter, _2 ),
boost::bind( TComparer<TValueGetter::result_type>(), boost::bind( getter, _1 ), boost::bind( getter, _2 ) )
) );
}
bool operator()( const T& lhs, const T& rhs ) {
BOOST_FOREACH( const auto& comparer, iComparers ) {
if( !comparer.first( lhs, rhs ) ) {
return comparer.second( lhs, rhs );
}
}
return false;
}
private:
std::vector<std::pair<EqualComparator, CustomComparator>> iComparers;
};
std::sort
안정적인 종류는 일반적으로 안정적인 것보다 느리기 때문에 안정성이 보장되지 않습니다 ... 따라서 안정적인 종류를 여러 번 사용하는 것은 성능 문제를위한 레시피처럼 보입니다 ...
그리고 그렇습니다. 정렬이 술어를 요구하는 것은 정말 부끄러운 일입니다. 나는 Tristate 함수의 벡터를 받아들이는 기능을 만드는 것 외에 다른 방법을 본다 ...
사슬 솔루션은 장점입니다. std :: logical_와 함께 boost :: bind를 사용하여 정렬 술어를 구축 할 수도 있습니다. 자세한 내용은 링크 된 기사를 참조하십시오. Boost 바인드 라이브러리가 C ++ 프로그램을 향상시킬 수있는 방법
C ++ 11의 변수 템플릿은 더 짧은 옵션을 제공합니다.
#include <iostream>
using namespace std;
struct vec { int x,y,z; };
struct CmpX {
bool operator() (const vec& lhs, const vec& rhs) const
{ return lhs.x < rhs.x; }
};
struct CmpY {
bool operator() (const vec& lhs, const vec& rhs) const
{ return lhs.y < rhs.y; }
};
struct CmpZ {
bool operator() (const vec& lhs, const vec& rhs) const
{ return lhs.z < rhs.z; }
};
template <typename T>
bool chained(const T &, const T &) {
return false;
}
template <typename CMP, typename T, typename ...P>
bool chained(const T &t1, const T &t2, const CMP &c, P...p) {
if (c(t1,t2)) { return true; }
if (c(t2,t1)) { return false; }
else { return chained(t1, t2, p...); }
}
int main(int argc, char **argv) {
vec x = { 1,2,3 }, y = { 2,2,3 }, z = { 1,3,3 };
cout << chained(x,x,CmpX(),CmpY(),CmpZ()) << endl;
return 0;
}