Reputation: 193
I came across the old question on is_sorted here: std::is_sorted and strictly less comparison?
after stumbling upon a use case where monotonicity check using a comparator of pairs failed the tests. I thought giving the lambda as below was in line with the requirements: it would return true (only if) both elements of lhs and rhs were sorted
bool
check_pair_sorted(std::vector<std::pair<int, int>>& vec)
{
return std::is_sorted(vec.begin(), vec.end(),[](auto& lhs, auto& rhs){
return (lhs.first < rhs.first) && (lhs.second < rhs.second);
});
}
However, the true intended function is possible if I split the check like so
bool check_separate_sorted(std::vector<std::pair<int, int>>& vec)
{
return std::is_sorted(vec.begin(), vec.end(),[](auto& lhs, auto& rhs){
return (lhs.first < rhs.first) ; })
&& std::is_sorted(vec.begin(), vec.end(),[](auto& lhs, auto& rhs){
return (lhs.second < rhs.second) ; });
}
Looking at the answers in the questions above, where it is suggested an early-return of false might be the preferred implementation, shouldn't the requirement also be posed likewise?
comp - comparison function object (i.e. an object that satisfies the requirements of Compare) which returns false if the first argument is not less than (i.e. is not ordered before) the second.
It's possible that I might be missing something in terms of ordering here, but just can't seem to wrap my head around it at the moment.
Upvotes: 1
Views: 713
Reputation: 21
std::is_sorted is clearly defined in terms of comparison function - operator<() or some functional object - lambda in TS`s example.
However, if the comparison function is constructed in such a way, that 2 elements don't have clear relation - i.e. both a < b and b < a are true - then the result of any sorting or ordering operation can not be fully defined.
This is the real source of confusion.
Upvotes: 1
Reputation: 21818
Your check_pair_sorted
is doing a different thing than check_separate_sorted
and that is, I presume, the source of confusion.
It comes down to the precise wording of how std::is_sorted
is defined. For every two elements a[i]
and a[j]
where i
<j
:
a[j] < a[i]
is never truea[i] < a[j]
is always true(naturally, a clever algorithm avoids making all O(n^2) comparisons)
Those two bullets above are not the same!
For simple numbers, as in the other SO question, sequence 1 2 3 3 4 5
satisfies the first point, but it does not satisfy the latter.
Something similar happens in your case. Consider a sequence of two pairs: { {1,2}, {2,1} }
. The elements of the sequence are different, but they are not comparable by your first lambda comparator - it returns false
in both directions. So, for the purpose of the algorithm, those two elements are treated as equivalent, even if they are not strictly equal.
So, is_sorted
inside your check_pair_sorted
for the sequence { {1,2}, {2,1} }
will return true
, as it checks the first bullet point and not the second.
Note, that the same equivalency issue can affect other STL structures and algorithms. For example, if you have a std::set
, you would not be able to have both {1,2}
and {2,1}
elements in it using your first lambda as a comparator.
Upvotes: 3
Reputation: 27577
For lexical sorting you need:
return (lhs.first < rhs.first) || ((lhs.first == rhs.first) && lhs.second < rhs.second));
Upvotes: 1