I asked me how the cmp function in std::sort and std::is_sorted is defined.
here are two documentations for is_sorted_until how say it should be operator< :
en.cppreference.com
cplusplus.com
But i think there should be a problem with equal elements.
The list {1,1,1} should not be sorted because 1<1==false.
But there is an example which says:
...
int *sorted_end = std::is_sorted_until(nums, nums + N);
...
1 1 4 9 5 3 : 4 initial sorted elements
but that should return 1 if < is used like documented.
It would work with <=, but that is not the way it is documented.
I'm really confused.
The comparison is required to define a strict weak ordering. A strict weak ordering defines a set of equivalence classes from the incomparability relation, i.e., if x < y is false, and y < x is false too (i.e. x and y cannot be compared with <), x and y are considered equivalent. These equivalence classes have a total order, and that's the total order resulting from the sort functions.
In the example given, {1,1,1} has only a single equivalence class, the one composed of {1,1,1}.
is_sorted_until finds the first element x[i] for which x[i] < x[i-1] is true.
To be exact, it's neither < nor <=, it is defaulted to std::less. That one in turn calls < for most types, except where it is specialized. For example, < for pointers does not generally give a strict ordering, while std::less does.
It does indeed use operator< unless you provide a custom comparison. But the definition of "sorted" is not a[n] < a[n+1] (which we might call "strictly sorted"), but !(a[n+1] < a[n]); so equal elements are considered sorted. This is equivalent to using <=, but (in common with all other standard algorithms) doesn't require that operator to be defined.
In general, all ordered comparisons must define a "strict weak ordering". "Strict" means that the comparison must be false for equivalent objects; so < is valid, while <= is not.
If you look at the example implementation, < is used for checking if the next element is less than the previous one:
if (*next < *first)
return next;
If it is, then the order is broken, and the function returns. I. e. the logic is reversed - the algorithm does not terminate if the next element is equal to the previous.
Related
Definition:
Let < be a binary relation where a < b means "a is less than b".
Let > be a binary relation where a > b means "a is greater than b".
So, we assume < and > have meanings we usually use in a daily life. Though, in some programming languages (e.g. C++), we can overload them to give them different definitions, hereafter we don't think about that.
Context:
As far I read mathematical definition of strict weak ordering (e.g. Wikipedia), I think both < and > satify it. However, all examples I saw in many websites refer only to <. There is even a website which says
what they roughly mean is that a Strict Weak Ordering has to behave the way that "less than" behaves: if a is less than b then b is not less than a, if a is less than b and b is less than c then a is less than c, and so on.
Also, in N4140 (C++14 International Standard), strict weak ordering is defines as
(§25.4-4) If we define equiv(a, b) as !comp(a, b) && !comp(b, a), then the requirements are that comp and equiv both be transitive relations
where comp is defined as
(§25.4-2) Compare is a function object type (20.9). The return value of the function call operation applied to an object of type Compare, when contextually converted to bool (Clause 4), yields true if the first argument of the call is less than the second, and false otherwise. Compare comp is used throughout for algorithms
assuming an ordering relation.
Question:
Does ">" satisfy strict weak ordering? I expect so, but have no confidence.
Does greater operator “>” satisfy strict weak ordering?
The mathematical strict greater than relation is a strict weak ordering.
As for the operator in C++ langauge: For all integers types: Yes. In general: No, but in most cases yes. Same applies to strict less than operator.
As for the confusing quote, "is less than" in that context intends to convey that means that the the end result of the sort operation is a non-decreasing sequence i.e. objects are "less" or equal to objects after them. If std::greater is used as comparison object, then greater values are "lesser" in order.
This may be confusing, but is not intended to exclude strict greater than operator.
what is the case where > doesn't satisfy strict weak ordering?
Some examples:
Overloaded operators that don't satisfy the properties.
> operator on pointers that do not point to the same array has unspecified result.
> does not satisfy irreflexivity requirement for floating point types in IEEE-754 representation unless NaNs are excluded from the domain.
Even if the standard refers to "less than" for arbitrary Compare functions, that only implies "less than" in the context of the ordering.
If I define an ordering by comparison function [](int a, int b) { return a > b; }, then an element is "less than" another in this ordering if its integer value is greater. That's because the ordering I've created is an ordering of the integers in reverse order. You shouldn't read < as "less than" in orderings. You should read it as "comes before".
Whenever x < y is a strict weak ordering then x > y is also a strict weak ordering, just with the reverse order.
Why does STL work with a comparison function that is strict weak ordering? Why can't it be partial ordering?
A partial order would not be sufficient to implement some algorithms, such as a sorting algorithm. Since a partially ordered set does not necessarily define a relationship between all elements of the set, how would you sort a list of two items that do not have an order relationship within the partial order?
Simply, a strict weak ordering is defined as an ordering that defines a (computable) equivalence relation. The equivalence classes are ordered by the strict weak ordering: a strict weak ordering is a strict ordering on equivalence classes.
A partial ordering (that is not a strict weak ordering) does not define an equivalence relation, so any specification using the concept of "equivalent elements" is meaningless with a partial ordering that is not a strict weak ordering. All STL associative containers use this concept at some point, so all these specifications are meaningless with a partial ordering that is not a strict weak ordering.
Because a partial ordering (that is not a strict weak ordering) does not necessarily define any strict ordering, you cannot "sort elements" in the common sense according to partial ordering (all you can do is a "topological sort" which has weaker properties).
Given
a mathematical set S
a partial ordering < over S
a value x in S
you can define a partition of S (every element of S is either in L(x), I(x) or G(x)):
L(x) = { y in S | y<x }
I(x) = { y in S | not(y<x) and not(x<y) }
G(x) = { y in S | x<y }
L(x) : set of elements less than x
I(x) : set of elements incomparable with x
G(x) : set of elements greater than x
A sequence is sorted according to < iff for every x in the sequence, elements of L(x) appear first in the sequence, followed by elements of I(x), followed by elements of G(x).
A sequence is topologically sorted iff for every element y that appears after another element x in the sequence, y is not less than x. It is a weaker constraint than being sorted.
It is trivial to prove that every element of L(x) is less than any element of G(x). There is no general relation between elements of L(x) and elements of I(x), or between elements of I(x) and elements of G(x). However, if < is a strict weak ordering, than every element of L(x) is less than any element of I(x), and than any element of I(x) is less than any element of G(x).
If < is a strict weak ordering, and x<y then any element of L(x) U I(x) is less then any element I(y) U G(y): any element not greater than x is less than any element not less that y. This does not necessarily hold for a partial ordering.
Quoting the answer given here:
Because internally, these algorithms implement "is equal to" as !(a < b) && !(b < a).
If you used <= to implement the less-than operator, then the above
would return false when a == b. A broken equality check will screw up
nearly any algorithm.
Similarly, they implement "is not equal to" as (a < b) || (b < a)
, and once again, if you implemented the < operator using <=, then it
will return true when they are equal to each other, when in fact they
are not equal. So the equality check is broken in both directions.
The whole point of limiting the library to a less-than operator is
that all of the logical operators can be implemented in terms of it:
<(a, b): (a < b)
<=(a, b): !(b < a)
==(a, b): !(a < b) && !(b < a)
!=(a, b): (a < b) || (b < a)
>(a, b): (b < a)
>=(a, b): !(a < b)
This works as long as your provided operator meets the conditions of a
strict weak ordering. The standard <= and >= operators do not.
You cannot perform binary search with partial ordering. You cannot create a binary search tree with partial ordering. What functions/datatypes from algorithm need ordering and can work with partial ordering?
I am reading the "Templates and Generic Programming" part in C++ Primer(5th Edition) but I got confused by some of the stuff there.
When talking about "Writing Type-Independent Code" at P655 & P656, the author stated that
"The tests in the body use only < comparisons " , because "by writing the code using only the < operator, we reduce the requirements on types that can be used with our compare function. Those types must support <, but they need not also support >." .
Are there any types that support < but not > ? If so, why < has the superiority over > ? I have searched on Google for some time but I failed to get the answer. Could anybody give me some examples or some referral links?
If so, why < has the superiority over >?
Mere convention. The character '<' comes first in ASCII, and less-than over a partially-ordered set is a primitive from which the comparisons may be constructed (i.e., the set is partitioned into equivalence groups).
If we have a, b, and operator <:
a < b if operator <( a, b )
a > b if operator <( b, a )
a ≤ b if ! operator <( b, a )
a = b if ! operator <( a, b ) && ! operator <( b, a )
etc.
C++ has a number of such conventions which are used to describe how types behave. These are often called concepts, and an upcoming language extension, capital-C Concepts, will allow you to query and specify such things as whether the less-than operator defines a partial ordering.
Are there any types that support < but not >?
Yes, many. The convention is to define operator < and then forget about > because it would be redundant. You can use something like std::relops to automatically define > in terms of <, but overall it's easier just to avoid writing the > operator in the first place.
All the parts of the standard library that depend on ordering, such as std::sort and std::map, will never use >.
In some languages (like Haskell), there is this concept of minimal complete definition. For example, if you have defined a new class (e.g. for rational numbers) with a total order, it is sufficient to define the <= operator. The other operators can be (in some cases automatically) defined in terms of the minimal definition:
x > y can be defined as ! (x <= y)
x == y can be defined as x <= y && y <= x
x >= y can be defined as x > y || x == y
x < y can be defined as ! (x >= y)
In this case, the minimal definition has only one operator. In general, you only have to define the operators in a minimal complete definition, and doing so will reduce redundant definition of the other operators that can be "inferred". Implementing only the minimal definition also reduces the chances that you introduce errors or inconsistency while implementing the non-essential operators (like >) in your case. The point is that it is often beneficial to implement a minimal set of operators.
Regarding why < is preferred over >, I think this is rather a matter of choice and convention. Strictly speaking, one could have chosen to implement any minimal definition. In the case of total order types, each of {<=} and {>=} is minimal complete set. So those types must support one of these sets, but they need not also support the rest of them.
There are cases where a type might use operator < for no other reason than to use as a sorting key. It's not necessarily that the order is meaningful, it's just to support the very templated APIs that you're describing. For instance, std::map will keep all its elements sorted based on operator < unless you give it a different predicate. You may not care exactly what that order is, you just want to be able to have fast (O(log n)) lookups. The order may be completely meaningless to the class otherwise. (In that case, you'd probably want a free function binary version of operator < instead of making it a member, or provide an explicit predicate).
If other operations are meaningful to your class, then by all means implement them. It will make your users' lives much simpler. And you can always implement them in terms of operator < if it simplifies the logic.
The only reason I can think of that they used operator < instead of operator > is that it makes the order a bit more natural (assuming your language is written left-to-right). If you store the integers 1 through 5 in a map, they'll be ordered 1 < 2 < 3 < 4 < 5. So in the case of operator <(T left, T right) if the predicate returns true, the left operand will be more literally "to the left" of the right operand.
And after one more pass over the text you quoted, it sounds to me they're speaking more about the person writing the algorithm that uses operator < rather than the person writing the class that implements operator <. If you're writing an algorithm that needs to work with other people's code, and you want them to give you a way to order their elements, you should only require operator <. The point is to simplify work at the "client" side.
We can pass a function as <(less) operator to STL data structures such as set, multiset, map, priority_queue, ...
Is there a problem if our function acts like <=(less_equal)?
Yes, there is a problem.
Formally, the comparison function must define a strict weak ordering, and <= does not do that.
more specifically, the < is also used to determine equivalence (x and y are equivalent iff !(x < y) && !(y < x)). This does not hold true for <= (using that operator would have your set believe that objects are never equivalent)
From Effective STL -> Item 21. Always have comparison functions return false for equal
values.
Create a set where less_equal is the comparison type, then insert 10 into the set:
set<int, less_equal<int> > s; // s is sorted by "<="
s.insert(10); //insert the value 10
Now try inserting 10 again:
s.insert(10);
For this call to insert, the set has to figure out whether 10 is already present. We know
that it is. but the set is dumb as toast, so it has to check. To make it easier to
understand what happens when the set does this, we'll call the 10 that was initially
inserted 10A and the 10 that we're trying to insert 10B.The set runs through its internal data structures looking for the place to insert 10B. It ultimately has to check 10B to see if it's the same as 10A. The definition of "the same"
for associative containers is equivalence, so the set tests to see whether
10B is equivalent to 10A. When performing this test, it naturally uses the set's
comparison function. In this example, that's operator<=, because we specified
less_equal as the set's comparison function, and less_equal means operators. The set
thus checks to see whether this expression is true:
!(10A<= 10B)&&!(10B<= 10A) //test 10Aand 10B for equivalence
Well, 10A and 10B are both 10, so it's clearly true that 10A <= 10B. Equally clearly, 10B
<= 10A. The above expression thus simplifies to
!!(true)&&!(true)
and that simplifies to
false && false
which is simply false. That is, the set concludes that 10A and 10B are not equivalent,
hence not the same, and it thus goes about inserting 10B into the container alongside
10A. Technically, this action yields undefined behavior, but the nearly universal
outcome is that the set ends up with two copies of the value 10, and that means it's not
a set any longer. By using less_equal as our comparison type, we've corrupted the
container! Furthermore, any comparison function where equal values return true will
do the same thing. Equal values are, by definition, not equivalent!
There is indeed a problem.
The comparison function should satisfy strict weak ordering which <= does not.
Why does STL work with a comparison function that is strict weak ordering? Why can't it be partial ordering?
A partial order would not be sufficient to implement some algorithms, such as a sorting algorithm. Since a partially ordered set does not necessarily define a relationship between all elements of the set, how would you sort a list of two items that do not have an order relationship within the partial order?
Simply, a strict weak ordering is defined as an ordering that defines a (computable) equivalence relation. The equivalence classes are ordered by the strict weak ordering: a strict weak ordering is a strict ordering on equivalence classes.
A partial ordering (that is not a strict weak ordering) does not define an equivalence relation, so any specification using the concept of "equivalent elements" is meaningless with a partial ordering that is not a strict weak ordering. All STL associative containers use this concept at some point, so all these specifications are meaningless with a partial ordering that is not a strict weak ordering.
Because a partial ordering (that is not a strict weak ordering) does not necessarily define any strict ordering, you cannot "sort elements" in the common sense according to partial ordering (all you can do is a "topological sort" which has weaker properties).
Given
a mathematical set S
a partial ordering < over S
a value x in S
you can define a partition of S (every element of S is either in L(x), I(x) or G(x)):
L(x) = { y in S | y<x }
I(x) = { y in S | not(y<x) and not(x<y) }
G(x) = { y in S | x<y }
L(x) : set of elements less than x
I(x) : set of elements incomparable with x
G(x) : set of elements greater than x
A sequence is sorted according to < iff for every x in the sequence, elements of L(x) appear first in the sequence, followed by elements of I(x), followed by elements of G(x).
A sequence is topologically sorted iff for every element y that appears after another element x in the sequence, y is not less than x. It is a weaker constraint than being sorted.
It is trivial to prove that every element of L(x) is less than any element of G(x). There is no general relation between elements of L(x) and elements of I(x), or between elements of I(x) and elements of G(x). However, if < is a strict weak ordering, than every element of L(x) is less than any element of I(x), and than any element of I(x) is less than any element of G(x).
If < is a strict weak ordering, and x<y then any element of L(x) U I(x) is less then any element I(y) U G(y): any element not greater than x is less than any element not less that y. This does not necessarily hold for a partial ordering.
Quoting the answer given here:
Because internally, these algorithms implement "is equal to" as !(a < b) && !(b < a).
If you used <= to implement the less-than operator, then the above
would return false when a == b. A broken equality check will screw up
nearly any algorithm.
Similarly, they implement "is not equal to" as (a < b) || (b < a)
, and once again, if you implemented the < operator using <=, then it
will return true when they are equal to each other, when in fact they
are not equal. So the equality check is broken in both directions.
The whole point of limiting the library to a less-than operator is
that all of the logical operators can be implemented in terms of it:
<(a, b): (a < b)
<=(a, b): !(b < a)
==(a, b): !(a < b) && !(b < a)
!=(a, b): (a < b) || (b < a)
>(a, b): (b < a)
>=(a, b): !(a < b)
This works as long as your provided operator meets the conditions of a
strict weak ordering. The standard <= and >= operators do not.
You cannot perform binary search with partial ordering. You cannot create a binary search tree with partial ordering. What functions/datatypes from algorithm need ordering and can work with partial ordering?