これはC++ 14の回答です。すべてがC++ 11に変換できますが、それほど簡単ではありません。
template<class F, class Base=std::less<>>
auto order_by(F&& f, Base&& b={}) {
return
[f=std::forward<F>(f), b = std::forward<Base>(b)]
(auto const& lhs, auto const& rhs)
->bool
{
return b(f(lhs), f(rhs));
};
}
order_by
投影および任意比較関数オブジェクトを受け取り、std::less<>
または比較関数オブジェクトのいずれか、その後の投影を適用比較関数オブジェクトを返します。
ソートや検索に便利です.C++アルゴリズムは比較関数オブジェクトを必要とするため、投影は簡単に記述できます。
template<class A, class B>
struct binary_overload_t:A,B{
using A::operator();
using B::operator();
binary_overload_t(A a, B b):A(std::move(a)), B(std::move(b)) {}
};
template<class A, class B>
binary_overload_t< A, B >
binary_overload(A a, B b) {
return { std::move(a), std::move(b) };
}
binary_overload
は、関数オブジェクトをオーバーロードできます。
template<class T>
struct valid_range_t {
T start, finish;
};
これは有効な範囲を表します。私はちょうどstd::pair
を使うことができましたが、私は意味のあるタイプを好みます。今
template<class T, class V>
struct ranged_value_t {
valid_range_t<T> range;
V value;
};
template<class T, class It>
auto find_value(It begin, It end, T const& target)
-> decltype(std::addressof(begin->value))
{
// project target into target
// and a ranged value onto the lower end of the range
auto projection = binary_overload(
[](auto const& ranged)->T const& {
return ranged.range.finish;
},
[](T const& t)->T const& {
return t;
}
);
//
auto it = std::upper_bound(begin, end,
target,
order_by(projection)
);
if (it == end) return nullptr;
if (target < it->range.start) return nullptr;
return std::addressof(it->value);
}
find_value
は非重複範囲に配置ranged_value_t
型構造にイテレータのペアをとります。
次に、(半開きの)範囲にtarget
が含まれている最初の(したがって唯一の)値のエントリへのポインタを返します。
ranged_value_t<int, char> table[]={
{{0,40}, 'T'},
{{41,55}, 'D'},
{{56,70}, 'P'},
{{71,80}, 'A'},
{{81,90}, 'E'},
{{91,101}, 'O'}
};
auto* ptr = find_value(std::begin(table), std::end(table), 83);
if (ptr) std::cout << *ptr << "\n"; else std::cout << "nullptr\n";
Live example。
選択肢を超えるこの回答の利点:
- 私たちは、不要な値オブジェクトを作成しないでください。値オブジェクトが高価であるか、大規模であるか、または非自明で構成可能である場合、これは重要です。
- テーブルを作成するための構文は単純です
- テーブルはほぼすべての形式で使用できます。あなたはファイルからそれらを解析することができます。
find_value
関数は単に反復子を取ります(ランダムアクセスを優先します)。
- 下限を省略できるようにして追加することができます。
valid_range_t
にフラグを追加するか(オプションで使用)、s
にチェックするとfind_value
に消費し、使いやすくするにはvalid_range_t
にコンストラクタを追加する必要があります。
ハーフオープン間隔とクローズ間隔の両方をサポートするように拡張すると、少しの作業が必要になります。私はそれを2番目のチェックとしてfind_value
にハックするように誘惑されるでしょう。
オーバーラッピング間隔にも少し時間がかかります。私は開始時にlower_boundを行い(s
)、終了時にupper_boundを行う(f
)。
私はこの種のものがデータ駆動型設計に最も適していることを発見しました。これをC++コードでハードコーディングするのは悪い計画です。代わりに、構成を消費し、その構成によって検証され駆動されるコードを記述します。
これはこのテーブルかルックアップテーブルのいずれかですが、この方法は6レコードしかないので大丈夫です。しかし、マジックナンバーをリファクタリングしたいかもしれません。 –
次の条件を否定する必要はありません。 'else'があります。もう一度、' return'があるので、そこにいる必要はありません。 – LogicStuff
@LogicStuff恐らく明白です。 –