给定一个对象列表,创建一个仿函数对象作为比较器的最简洁方法是什么,这样比较器就会尊重列表中对象的顺序。保证列表中的对象是唯一的,列表包含可能对象的整个空间。
例如,假设我们有:
const std::vector<std::string> ordering {"dog", "cat", "mouse", "elephant"};
现在我们想要一个函数作为比较器,比如地图:
using Comparator = std::function<bool(const std::string&, const std::string&>;
using MyMap = std::map<std::string, int, Comparator>;
我有一个解决方案,但这不是我所说的那样:
const auto cmp = [&ordering] (const auto& lhs, const auto& rhs)
{
const std::array<std::reference_wrapper<const std::decay_t<decltype(lhs)>, 2> values {lhs, rhs};
return *std::find_first_of(std::cbegin(ordering), std::cend(ordering),
std::cbegin(values), std::cend(values),
[] (const auto& lhs, const auto& rhs) {
return lhs == rhs.get();
}) == lhs;
};
是否有一些不那么冗长的东西?
答案 0 :(得分:3)
您可以使用:
const std::vector<std::string> ordering {"dog", "cat", "mouse", "elephant"};
struct cmp
{
bool operator()(const std::string& lhs, const std::string& rhs)
{
return (std::find(ordering.begin(), ordering.end(), lhs) <
std::find(ordering.begin(), ordering.end(), rhs));
}
};
using MyMap = std::map<std::string, int, cmp>;
在http://ideone.com/JzTNwt处查看它。
答案 1 :(得分:1)
KISS。使用具有明确语义的可重用原语构建解决方案。
order_by
进行投影A->B
并使用A
上的排序返回B
上的排序。它可选择在B
上进行排序(此处未使用):
template<class F, class Next=std::less<>>
auto order_by(F&& f, Next&& next = {}) {
return [f=std::forward<F>(f), next=std::forward<Next>(next)]
(auto&& lhs, auto&& rhs)->bool
{
return next(f(lhs), f(rhs));
};
}
index_in
接受一个容器c
并返回一个接受一个元素的函数,并在c
中确定其索引:
template<class C>
auto index_in( C&& c ) {
return [c=std::forward<C>(c)](auto&& x){
using std::begin; using std::end;
return std::find( begin(c), end(c), x ) - begin(c);
};
}
template<class T>
auto index_in( std::initializer_list<T> il ) {
return [il](auto&& x){
using std::begin; using std::end;
return std::find( begin(il), end(il), x ) - begin(il);
};
}
然后我们将它们组成:
auto cmp = order_by( index_in(std::move(ordering) ) );
每个组件都可以独立测试和验证,而组合物显然可以&#34;作品。我们还可以重写index_in
以使用比线性更快的查找,例如从键到索引的映射,并且我们有两个可以相互进行单元测试的实现。
我发现order_by
非常有用。 index_in
我以前从未使用过。
这个技巧使得在一行上构造结果map
,而不是存储cmp
,实际上,因为最终描述简短明了。
template<class T>
using Comparator = std::function<bool(T const&, T const&>;
using MyMap = std::map<std::string, int, Comparator<std::string>>;
MyMap m = order_by( index_in({"dog", "cat", "mouse", "elephant"}) );
看起来也很漂亮。
这是第二种方法。
我们可以将一些工作移到lambda之外。
template<class T0, class...Ts>
std::array< std::reference_wrapper<T0>, sizeof...(Ts)+1 >
make_ref_array( T0& t0, Ts&... ts ) {
return {std::ref(t0), std::ref(ts)...};
}
然后我们可以从第一原则而不是算法编写lambda:
Comparator cmp = [&ordering] (const auto& lhs, const auto& rhs)
{
if (lhs==rhs) return false; // x is never less than x.
for (auto&& e:ordering)
for (auto&& z:make_ref_array(lhs, rhs))
if (e==z.get())
return std::addressof(z.get())==std::addressof(lhs);
return false;
};
得到的lambda稍微冗长。
如果您使用基于范围的算法,它也可能会有所帮助。
在我的两个解决方案中,列表中不的所有元素都大于列表中的任何元素,并且彼此相等。
答案 2 :(得分:1)
跳过算法并写一个for循环:
auto cmp = [&ordering](auto const& lhs, auto const& rhs) {
for (auto const& elem : ordering) {
// check rhs first in case the two are equal
if (elem == rhs) {
return false;
}
else if (elem == lhs) {
return true;
}
}
return false;
};
技术上可能比你的解决方案更长,但我觉得它更容易阅读。
或者,根据订购的大小,可以将两者都投入到地图中:
std::unordered_map<std::string, int> as_map(std::vector<std::string> const& ordering)
{
std::unordered_map<std::string, int> m;
for (auto const& elem : ordering) {
m.emplace(elem, m.size());
}
return m;
}
auto cmp = [ordering_map = as_map(ordering)](auto const& lhs, auto const& rhs){
auto left = ordering_map.find(lhs);
auto right = ordering_map.find(rhs);
return left != ordering_map.end() && right != ordering_map.end() &&
left->second < right->second;
};
答案 3 :(得分:0)
这与R. Sahu的答案基本相同。但是因为我已经输入了...我在这里主张的主要是将ordering
保持在cmp
内部,假设你不需要外部。
const auto cmp = [ordering = vector<string>{ "dog", "cat", "mouse", "elephant" }](const auto& lhs, const auto& rhs) { return find(cbegin(ordering), cend(ordering), lhs) < find(cbegin(ordering), cend(ordering), rhs); };
将ordering
封装在cmp
中会使读者在将ordering
更改为小时时必须查看的范围。 (就我个人而言,我不会将cmp
构造为Rvalue,我只是因为同样的原因而将其转移directly into the constructor ...虽然它变得有点像一个简单的单行:
map<string, int, function<bool(const string&, const string&)>> myMap([ordering = vector<string>{ "dog", "cat", "mouse", "elephant" }](const auto& lhs, const auto& rhs) { return find(cbegin(ordering), cend(ordering), lhs) < find(cbegin(ordering), cend(ordering), rhs); });
答案 4 :(得分:0)
我建议您将密钥设置为包含密钥(在本例中为std::string
)和数组中的索引的结构类型。
像
struct Key
{
std::string str;
size_t index;
};
using MyMap = std::map<Key, int, Comparator>;
struct Comparator
{
bool operator()(const Key &a, const Key &b) const
{
return a.index < b.index;
}
};