[英]C++ equivalent to Python's cmp or Haskell's compare
Question: 题:
Is there a C++ equivalent for Python's cmp
or Haskell's compare
? 是否有C ++等效于Python的cmp
或Haskell的compare
?
compare
is like operator==
and operator<
in one. compare
就像operator==
和operator<
合而为一。 It returns LT
, EQ
, or GT
. 它返回LT
, EQ
或GT
。 But it's twice as fast as calling both operator==
and operator<
because it does it in one pass. 但这是同时调用operator==
和operator<
的两倍,因为它一次完成了。
More details: 更多细节:
At work, I often have structs that are used as keys for maps, for example: 在工作中,我经常有用作地图键的结构,例如:
struct RecordUsedAsAKey {
int field_a;
string field_b;
vector<float> field_c;
// operator< is needed for keys in maps.
bool operator<(const RecordUsedAsAKey& other) const;
};
bool RecordUsedAsAKey::operator<(const RecordUsedAsAKey& other) const {
if (field_a != other.field_a)
return field_a < other.field_a;
if (field_b != other.field_b)
return field_b < other.field_b;
return field_c < other.field_c;
}
One problem with RecordUsedAsAKey::operator<
is that it's unnecessarily slow. RecordUsedAsAKey::operator<
一个问题是它的速度不必要地慢。
string::operator!=
finds a different character, the program iterates over the equal characters again in the string::operator<
, when it could have skipped those.. 当string::operator!=
找到一个不同的字符时,程序可能会跳过这些字符时,再次在string::operator<
迭代相同的字符。 vector
's comparison. vector
的比较也一样。 If I had an equivalent to Haskell's compare
, my comparison method would had been more efficient: 如果我具有与Haskell的compare
相当的效果,那么我的比较方法将更加高效:
Ordering RecordUsedAsAKey::compare(const RecordUsedAsAKey& other) const {
Ordering t;
if ((t = field_a.compare(other.field_a)) != EQ)
return t;
if ((t = field_b.compare(other.field_b)) != EQ)
return t;
return field_c.compare(other.field_c);
}
This is more efficient because the string
's compare
method does only one pass on the string. 这是更有效的方法,因为string
的compare
方法仅对字符串执行一次传递。
Btw/mini-flame-war: in Haskell the whole code for the comparison would just be deriving Ord
. Btw / mini-flame-war:在Haskell中,用于比较的整个代码只是deriving Ord
。
You can easily implement it yourself, as a free function. 您可以轻松地将其实现为免费功能。
#include <string>
#include <vector>
enum order {
order_lt = -1,
order_eq,
order_gt
};
// General case, templated version.
template < typename T >
order compare(T left, T right) {
if (left < right)
return order_lt;
if (left == right)
return order_eq;
return order_gt;
}
// Specialization
order compare(const std::string& left, const std::string& right) {
return order(left.compare(right));
}
template < typename T >
order compare(const std::vector<T>& left, const std::vector<T>& right) {
order o = compare(left.size(), right.size());
if (o != order_eq)
return o;
for (size_t i = 0; i < left.size(); ++ i) {
o = compare(left[i], right[i]);
if (o != order_eq)
return o;
}
return order_eq;
}
Note: I edited the code to include a templated version for the general case (work provided that the operator< and operator== are defined for the type). 注意:我编辑了代码,以包括一般情况下的模板版本(只要为该类型定义了operator <和operator ==,就可以进行工作)。 I also kept some specialization as it can improve run time on some type (mainly containers). 我还保留了一些专业知识,因为它可以缩短某些类型(主要是容器)的运行时间。
Edit: Using std::string::compare
instead of strcmp
. 编辑:使用std::string::compare
而不是strcmp
。
Since map
semantics are in term of operator<
, and that in fact many operators implementations are in term of operator<
, probably something only in term of it is better. 由于map
语义是用operator<
,并且实际上许多运算符实现都是用operator<
,所以可能仅以它为代表的更好。
For instance: 例如:
template <typename T>
int compare(const T& x, const T& y)
{
if (x < y) return -1;
else if (y < x) return 1;
else return 0;
}
or, better, 或更好,
template <typename T, typename F>
int compare(const T& x, const T& y, F pred)
{
if (pred(x, y)) return -1;
else if (pred(y, x)) return 1;
else return 0;
}
template <typename T>
int compare(const T& x, const T& y)
{
return compare(x, y, std::less<T>());
}
so that you can use compare(k1, k2, mymap.key_comp())
if you need to. 因此,您可以根据需要使用compare(k1, k2, mymap.key_comp())
。
After your program works, and you are convinced that compare
is the bottleneck , you can specialize for the offending types. 程序运行后, 您确信compare
是瓶颈 ,因此您可以专门处理有问题的类型。 Do for instance 例如
template <typename C, typename T, typename A>
int compare(const std::basic_string<C, T, A>& x,
const std::basic_string<C, T, A>& y)
{
return x.compare(y);
}
if you are worried about efficiency for string types. 如果您担心字符串类型的效率。
If you are comparing sequences, you can use std::lexicographical_compare
. 如果要比较序列,则可以使用std::lexicographical_compare
。 However, you may want to reimplement it to handle the equality case, here is an optimized version for std::vector
: 但是,您可能需要重新实现它以处理相等情况,这是std::vector
的优化版本:
template <typename T, typename A, typename F>
int compare(const std::vector<T, A>& x,
const std::vector<T, A>& y, F pred)
{
std::vector<T, A>::const_iterator i = x.begin();
std::vector<T, A>::const_iterator j = y.begin();
while (i != x.end())
{
if (j == y.end()) return 1;
if (pred(*i, *j)) return -1
else if (pred(*j, *i)) return 1;
++i; ++j;
}
return j == y.end() ? 0 : -1;
}
simpler and more general version of Sylvain Defresne's answer: Sylvain Defresne的答案的更简单,更通用的版本:
template<typename T>
order compare(const T &left, const T &right) {
if (left < right)
return order_lt;
else if (left == right)
return order_eq;
return order_gt;
}
The std::string already has a compare member function that does what you want. std :: string已经具有执行所需功能的比较成员函数。
For other sequences, like std::vector, there is a std::mismatch function in <algorithm>
that scans two sequences side-by-side and returns iterators to the first two elements that differ. 对于其他序列,例如std :: vector, <algorithm>
中有一个std :: mismatch函数,该函数并排扫描两个序列,并将迭代器返回到不同的前两个元素。 From there, you only have to figure out if these two elements are less than or greater than each other. 从那里,您只需要确定这两个元素是否彼此小于或大于即可。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.