Sort Lambda Expression C++ by two condition - c++

I am trying to sort data which I have in list.
And I need that kind of sort
if a>b sort by a,b
else if a==b sort by c,d
I done this by it is not working.
l_name->sort([](type*& s1, type*& s2)
{
if (s1->a() > s2->b())
return s1->a() > s2->b()
else if(s1->a() == s2->b())
return s1->c() > s2->d();
});

You cannot sort with a comparison function like that, because the sorting rules it defines are internally inconsistent. In order to sort, X < Y must imply that Y < X is false.
Consider these two objects:
Name a b
---- - -
X 2 1
Y 2 1
No matter how you compare them, X > Y or Y > X, you would get true, because X.a > Y.b and Y.a > X.b.
Even X > X and Y > Y would produce true, which must never happen.
For that reason, you should define your comparison rules in terms of comparing the same attributes. Otherwise, you will break reflexivity and transitivity rules.

What if a < b? You can solve this problem more robustly and concisely:
l_name->sort([](type*& s1, type*& s2)
{
if (s1->a() != s2->b())
return s1->a() < s2->b();
return s1->c() < s2->d();
});

Related

what is the difference between "return num1<num2" and "return num2-num1" in comparator

I am learning how to write the comparator in C++. At first time, I return num1<num2, as a result I get a set in ascending order. Then I return num1>num2 and I get a set in descending order. Now I try to return num1-num2 which should equal to num1>num2 in my opinion, I get a set in descending order as predicted. But when I try to return num2-num1, I still get a set in descending order. How could it happen? Is there any difference between return num2-num1 and return num1<num2?
#include <iostream>
#include <set>
using namespace std;
struct myCmp{
bool operator()(const int& num1, const int& num2){
return num2-num1;
}
};
int main()
{
set<int,myCmp> st;
st.insert(1);
st.insert(2);
st.insert(3);
for(auto it=st.begin();it!=st.end();it++){
cout<<*it<<endl;
}
return 0;
}
Now I try to return num1-num2 which should equal to num1>num2 in my opinion
That is incorrect.
Is there any difference between return num2-num1 and return num1<num2?
Yes.
num2-num1 returns an integer value that is the result of subtracting the value of num1 from the value of num2. Since your comparator returns a bool, a result of 0 will be converted to false, and any other result will be converted to true.
num1<num2 returns a boolean value specifying whether or not num1 is actually less-than num2. Any value of num1 that is less-than the value of num2 will return true, all other values will return false.
So, as you can see, both approaches return completely different things.
std::set has the following requirement:
std::set is an associative container that contains a sorted set of unique objects of type Key. Sorting is done using the key comparison function Compare. Search, removal, and insertion operations have logarithmic complexity. Sets are usually implemented as red-black trees.
Everywhere the standard library uses the Compare requirements, uniqueness is determined by using the equivalence relation. In imprecise terms, two objects a and b are considered equivalent if neither compares less than the other: !comp(a, b) && !comp(b, a).
Using return num1<num2 (or return num1>num2) satisfies that requirement, but return num2-num1 breaks it.
For example, let's assume a = 1 and b = 2.
Using num1<num2, you get the following:
!comp(a, b) && !comp(b, a)
= !comp(1, 2) && !comp(2, 1)
= !(1 < 2) && !(2 < 1)
= !true && !false
= false && true
= one is less-than the other, so not equal, which is correct!
Using num1>num2, you get the following:
!comp(a, b) && !comp(b, a)
= !comp(1, 2) && !comp(2, 1)
= !(1 > 2) && !(2 > 1)
= !false && !true
= true && false
= one is less-than the other, so not equal, which is correct!
Using num2-num1, you get the following:
!comp(a, b) && !comp(b, a)
= !comp(1, 2) && !comp(2, 1)
= !(2 - 1) && !(1 - 2)
= !(1) && !(-1)
= !true && !true
= false && false
= neither is less-than the other, so equal, which is incorrect!
I try to return num1-num2 which should equal to num1>num2 in my opinion
Let's try that:
num1 = 5;
num2 = 10;
num1>num2 // false
num1-num2 // -5, true
So, no, your assumption is incorrect. All results when num1 != num2 are converted to true in a boolean context, and only when num1 == num2 will the result be converted to false.

Is it safe to sort a container which may contain infinities using quicksort?

I have realized that in order for quicksort to work, all the infinities need to be equal.
In other words, such a criterium is not enough:
class Entity
{
public:
float value() const;
bool valueIsInfinite() const;
};
class Criterium
{
bool operator()(Entity left, Entity right)const
{
if (left.valueIsInfinite())
return false;
return left.value() < right.value();
}
}
const Criterium criterium;
QVector<Entity> container;
qSort<container.begin(), container .end(), criterium>
This sorting fails, because not all infinities are equal according to the criterium. The unequalness depends on the order in which the entities enter the operator. I found out, that such a ordering fails.
I need something like this:
class Criterium
{
bool operator()(Entity left, Entity right)const
{
if (left.valueIsInfinite() && right.valueIsInfinite())
return false;
if (left.valueIsInfinite() && !right.valueIsInfinite())
return false;
if (!left.valueIsInfinite() && right.valueIsInfinite())
return true;
return left.value() < right.value();
}
}
But suppose that instead of
float Entity::value() const;
bool Entity::valueIsInfinite() const;
methods, I would like to use just
float Entity::value() const;
And have it return
std::numeric_limits<float>::infinity();
in cases where
bool Entity::valueIsInfinite() const;
would return true.
Now I tested this approach and it seems to work. But I am concerned about other ways in which an infinity may arise. For example:
float otherInfinity = exp(std::numeric_limits<float>::infinity());
This infinity seems to be the same. But I want to be sure. I know that C++ standard does not mention details of floating point arithmetic implementation, but if I use gcc, is it safe in all cases? I mean are all infinities created equal in gcc? Is it safe to sort a container of floats, which may contain infinities which have arisen on different occasions?
Without the presence of NaNs, infinities are fine with the regular operator <:
+∞ < +∞ is false: < is irreflexive;
if +∞ < x is true then x < +∞ is false for non-infinity values: < is antisymmetric;
if +∞ < x is true and x < y is true, then +∞ < y is true: < is transitive;
if +∞ is incomparable (not less, not greater) with x, and x is incomparable with y, then +∞ is incomparable with y: < displays transivity of equivalence.
(similar properties are valid for -∞)
Given those properties operator< on floats without NaNs is a strict weak ordering, and thus suitable for standard library style ordering operations.
However, with NaNs, the antisymmetry property is broken: NaN < 1 is false, and 1 < NaN is false too. You can solve this by ordering all NaNs either before or after all non-NaNs, in a manner similar to your proposed strategy:
struct Criterion
{
bool operator()(Entity left, Entity right)const
{
// NaNs come before non-NaNs
if (isnan(left.value()) && isnan(right.value()))
return false;
if (!isnan(left.value()) && isnan(right.value()))
return false;
if (isnan(left.value()) && !isnan(right.value()))
return true;
return left.value() < right.value();
}
}
(isnan can be found on the C++11 standard library, or implemented quite easily as return x != x;)
With this, we get NaN < 1 as true, and 1 < NaN as false, while the other properties still hold.
If you use this:
bool operator()(Entity left, Entity right)const
{
return !(left.valueIsInfinite() && right.valueIsInfinite())
&& left.value() < right.value();
}
Then the infinites are considered of the same order, since none comes before another one...

What is the difference between ! ( x < y ) and x >= y in C++?

Going through EASTL, I stumbled across a peculiar line of code. The following link shows the file with the line number of interest at 1870.
https://github.com/paulhodge/EASTL/blob/master/include/EASTL/algorithm.h
The code at that line is if(!(value < *i)). The comment says that "we always express value comparisons in terms of < or ==" without any explanation as to why this is so. There are also a few other areas where the same comment is placed but without any explanation.
Is there any benefit whatsoever to writing a comparison like that (maybe some context that I am overlooking)? If not, why did the author of EASTL deliberately wrote it in this particular fashion and even took the care to comment about it? Is consistency the only reason here?
It means you only need to provide < and == for container value types. It also means you reduce the amount of variability for those types (as all the algorithms use !(a<b) to mean a>=b and !(a==b) for a!=b); otherwise, you could have >= and != return inconsistent results.
In C++, you can overload the < operator so that it behaves differently than the opposite of >=, so they are not guaranteed to be equivalent.
Additionally, in any IEEE floating-point implementation, NaN < NaN is false, but so is NaN >= NaN, so !(NaN < NaN) is true even though NaN >= NaN is false.
I see at least one difference. If one of the numbers was QNAN (floating-point 0/0) then !(a < b) would've always return TRUE if any of a or b were QNAN, while it would've always returned false for a>=b
Using just the less-than operator, you can simulate all the other comparison operators. This makes it more consistent and allows you to use a single template parameter when you need to parameterize the comparison. The standard sorted containers and algorithms use std::less<T> as the default template comparator for example.
operation equivalent
x < y x < y
x > y y < x
x <= y !(y < x)
x >= y !(x < y)
x == y !(x < y) && !(y < x)
x != y (x < y) || (y < x)
For those operations where ordering is not important it's simpler and more efficient to use operator == instead.

if(x==y==z) works, if(x!=y!=z) does not

Why is:
if(x!=y!=z)
handled as:
x=1
y=1
z=2
??
I just noticed it today.
x != y and x == y return booleans.
You're comparing z to those booleans.
Neither of them will work they way you want them to.
It probably is parsed as if ((x!=y) !=z) which does not do what you think if (x!=y!=z) should do (but does not).
Likewise if (x==y==z) probably means if ((x==y)==z) to the compiler which is not what you want.
Enable the warnings given by your compiler. With GCC, that means gcc -Wall and it would tell you warning: suggest parentheses around comparison in operand of '=='
Recall that a boolean expression like x==y gives a zero (when false) or non-zero (when true) result. Writing ((x==y) + (z==t)) is very poor taste, but makes sense for the compiler.
x == y == z is equivalent to (x == y) == z. In this case, (1 == 1) == 2, or true == 2, which is false because true == 1, not 2.
x != y != z is equivalent to (x != y) != z. In this case, (1 != 1) != 2, or false != 2, which is true because false == 0, not 2.
C(++) relational operators aren't chained like in Python. If you want to check whether three numbers are all equal to each other, use (x == y) && (y == z).
if(x==y==z)
will not work untill the value u will use for z be 1 or 0 but u can take the value of x and y anything
As when u try with the value 1 or 0 then the if will take its parameters as
if((x==y)==z)
this is happening because it first evaluate whatever the value in x and y and the answer will be in boolean and then it checks with z which it expects to be boolean. so if (x==y) be and z is 1(true) then code will be executed else it wont.Same thing will happen with (x!=y!=z). try with z=1 or 0 and x,y be anything.

Unexpected output of C code

What would be the output of this program ?
#include<stdio.h>
#include<conio.h>
void main()
{
clrscr();
int x=20,y=30,z=10;
int i=x<y<z;
printf("%d",i);
getch();
}
Actually i=20<30<10, so the condition is false and the value of i should be 0 but i equals 1. Why?
This int i=x<y<z; doesn't work the way you intended.
The effect is int i=(x<y)<z;, where x<yis evaluated first, and the value true is then compared to z.
Pascal points out below that in C the result of the comparison is 1 instead of true. However, the C++ true is implicitly converted to 1 in the next comparison, so the result is the same.
The comparison operators don't work like that. Your program is equivalent to:
i = (x < y) < z;
which is equivalent to:
i = (x < y);
i = i < z;
After the first operation, i == 1. So the second operation is equivalent to:
i = 1 < 10;
You need to rewrite your statement as:
i = (x < y) && (y < z);
The < operator has left-to-right associativity. Therefore x<y<z will do (x<y)<z. The result of the first parenthesis is 1, 1 is smaller than 10, so you'll get 1.
That's not how it works. It's better to see with parenthesis:
int i = (x<y)<z;
Now, first x<y is evaluated. It's true, 20<30, and true is 1 as an integer. 1<z is then true again.
Its precedence is from left to right. Thats is why it is like
20<30 = true
1<10 TRUE
SO FINALLY TRUE
Actually < is left-associative, so first, 20<30 is evaluated (giving 1 usually), then 1 is less than 10.
The output of "1" is correct. This is evaluated as (20<30) < 10, which is 1 < 10, which is 1.
The problem is that you are comparing a boolean value to an integer value which in most cases doesn't make sense.
< is evaulated from left to right, so 20<30 is true, or one, which is less than 10.
The operator < associates from left to right.
So x < y < z is same as ( x < y ) < z
Your expression evaluates as:
( x < y ) < z
= ( 20 < 30 ) < 10
= ( 1 ) < 10
= 1