オブジェクトでいっぱいの std::vector があり、それぞれに数値グループ識別子が関連付けられています。オブジェクトには、「サイズ」や「名前」などのプロパティもあります。
オブジェクトのベクトルを名前、サイズ、およびその他のプロパティで並べ替えながら、それらをグループ化したままにする必要があります (たとえば、上記のグループ識別子によって)。
この目標はどのように達成できますか?
オブジェクトでいっぱいの std::vector があり、それぞれに数値グループ識別子が関連付けられています。オブジェクトには、「サイズ」や「名前」などのプロパティもあります。
オブジェクトのベクトルを名前、サイズ、およびその他のプロパティで並べ替えながら、それらをグループ化したままにする必要があります (たとえば、上記のグループ識別子によって)。
この目標はどのように達成できますか?
STL を使用すると、独自の比較関数を簡単に挿入できます。最初にグループを比較し、次に他の属性を比較する比較関数を定義したいとします。
static bool CompareWidget(const Widget& w1, const Widget& w2)
{
if(w1.GetGroupNumber() != w2.GetGroupNumber())
return (w1.GetGroupNumber() < w2.GetGroupNumber());
if(w1.GetHeight() != w2.GetHeight())
return (w1.GetHeight() < w2.GetHeight();
/// etc
return false;
}
static void SortWidgetVector(WidgetVector& widgetVector)
{
std::sort(widgetVector.begin(), widgetVector.end(), CompareWidget);
}
オブジェクトのベクトルを名前、サイズ、およびその他のプロパティで並べ替えながら、それらをグループ化したままにする必要があります (たとえば、上記のグループ識別子によって)。
実際、私がやろうとしていることは逆です。まず、ベクターを二次属性 (名前、サイズなど) で並べ替え、すべてのベクター要素がグループ内に含まれるようにします。
グループ識別子でソートされていない中間状態でベクトルを使用する場合を除き、どのように考えても結果は同じである必要があります。そうでない場合、2 つの基準を使用した比較関数を使用できない理由がわかりません (グループ ID がプライマリ条件で、名前/サイズ/その他がセカンダリ条件です)。2 つの述語 (by_two_criteria) を組み合わせた一般的な比較オブジェクトを作成することもできます。
#include <vector>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <cstdlib>
template <class FirstCondition, class SecondCondition>
class by_two_criteria_t
{
FirstCondition first;
SecondCondition second;
public:
by_two_criteria_t(FirstCondition f, SecondCondition s): first(f), second(s) {}
template <class T>
bool operator()(const T& a, const T& b) const
{
return first(a, b) || (!first(b, a) && second(a, b));
}
};
template <class FirstCondition, class SecondCondition>
by_two_criteria_t<FirstCondition, SecondCondition> by_two_criteria(FirstCondition f, SecondCondition s)
{
return by_two_criteria_t<FirstCondition, SecondCondition>(f, s);
}
class X
{
int group;
int value;
public:
X(int g, int n): group(g), value(n) {}
friend bool compare_group(const X& a, const X& b);
friend bool compare_value(const X& a, const X& b);
friend std::ostream& operator<<(std::ostream& os, const X& x) { return os << x.group << ", " << x.value; }
};
bool compare_group(const X& a, const X& b) { return a.group < b.group; }
bool compare_value(const X& a, const X& b) { return a.value < b.value; }
X random_x()
{
return X(rand() % 10, rand() % 20);
}
int main()
{
using namespace std;
vector<X> vec;
generate_n(back_inserter(vec), 100, random_x);
sort(vec.begin(), vec.end(), by_two_criteria(compare_group, compare_value));
copy(vec.begin(), vec.end(), ostream_iterator<X>(cout, "\n"));
}
そして、お楽しみとして、 n 個の比較基準を組み合わせたファンクターを次に示します (C++0x は可変個引数テンプレートと新しいスタイルの初期化構文のみ)。
#include <functional>
template <class T, class ...Fun>
class n_criteria_t {};
template <class T, class Fun1, class ...FunN>
class n_criteria_t<T, Fun1, FunN...>: public std::binary_function<T, T, bool>
{
public:
n_criteria_t(Fun1 f1, FunN... fn): f1(f1), f2(fn...) {}
bool operator() (const T& a, const T& b) const
{
return f1(a, b) || (!f1(b, a) && f2(a, b));
}
private:
Fun1 f1;
n_criteria_t<T, FunN...> f2;
};
template <class T, class Fun1>
class n_criteria_t<T, Fun1>: public std::binary_function<T, T, bool>
{
public:
n_criteria_t(Fun1 f1): f1(f1) {}
bool operator() (const T& a, const T& b) const
{
return f1(a, b);
}
private:
Fun1 f1;
};
template <class T, class ...Fun>
n_criteria_t<T, Fun...> n_criteria(Fun... f)
{
return {f...};
}
グループ識別子をプライマリ ソート キーとして使用します。
たとえば、グループ、名前、サイズの順に並べ替えるには、次のような比較オブジェクトを使用できます。
class my_comparison : public std::binary_function< object, object, bool >
{
public:
inline bool operator()(
object const &left,
object const &right ) const
{
return ( left.group_id < right.group_id ? true :
left.group_id > right.group_id ? false :
left.name < right.name ? true :
left.name > right.name ? false :
left.size < right.size );
}
};
次に、このインスタンスを 3 番目のパラメーターとしてstd::sort()
関数に渡します。
partition または stable_partion を使用して、オブジェクトをいくつかのセットに分割し、個別に並べ替えることができます。どれだけ速くなったのか遅くなったのかはわかりませんが、コードが少し理解しやすくなるようです。
class GroupPredicate : std::unary_function<object, bool>
{
public:
GroupPredicate(INT group)
: m_group(group)
{
}
inline bool operator()(const object &object)
{
return object.group == m_group;
}
INT m_group;
};
class SizeSort : public std::binary_function<object, object, bool>
{
public:
inline bool operator()(const object &left, const object &right)
{
return left.size < right.size;
}
};
//...
std::vector<object> arVector;
for (INT i = 0; i < groupCount; ++i)
{
std::vector<object>::iterator pEndGroup = std::partition(pStartGroup, arVector.end(), GroupPredicate(i));
std::sort(pStartGroup, pEndGroup, SizeSort());
pStartGroup = pEndGroup;
}