I am trying to obtain a sorting of class objects according to two of its members in such a way: sort according to a member. If two objects have the same value (or if the difference is below some threshold) then search according to the member b. I am using multiset with that.
Problem is, that when another object comes, is checked according to sorting class, and fulfills to return true (to be put higher than the object that is compared to), it might also fulfill with another one being one step higher. How can I introduce here, that it should check with as many previous objects, and go as high as is only possible? (to obtain really well sorted multiset!)
Below how I wrote the sorting class. Please provide me with some nice idea!
And I had problems to introduce indentations for this part of code, anyone can help me to edit please? it's unreadable.
std::multiset<classA , classA ::SortClass> mydata;
class SortClass
{
public:
bool operator() (const classA &pt1, const classA &pt2)
{
if(pt1.a < pt2.a)
{
if(abs(pt1.a-pt2.a) < 0.01)
{
if(pt1.b > pt2.b)
return true;
else
return false;
}
else
return true;
}
else
return false;
}
};
EDIT:
Paste this and see what I want and cannot achieve. I want it sorted increasing with m_a and decreasing with m_b if m_a are the same (you can put there equality or inequality, does not work either)
#include <iostream>
#include <set>
using namespace std;
class classA
{
protected:
double m_b;
double m_a;
public:
double get_b() {return m_b;}
double get_a() {return m_a;}
void set_b(double b) {m_b = b;}
void set_a(double a) {m_a = a;}
class SortClass
{
public:
bool operator() (const classA &pt1, const classA &pt2)
{
if(pt1.m_a < pt2.m_a)
{
if(pt2.m_a - pt1.m_a == 0)
return (pt1.m_b > pt2.m_b);
else
return (pt1.m_a < pt2.m_a);
}
else
return false;
}
};
};
int main()
{
std::multiset<classA, classA::SortClass> mydata;
classA objA;
for(int i=0; i<100;i++)
{
objA.set_a(rand() %100);
objA.set_b(rand() %10);
mydata.insert(objA);
}
return 0;
}
Let's stop using true, and false and return the bool expressions. It make your code so much easier to read.
bool sortClassA (const classA &pt1, const classA &pt2)
{
if(pt2.m_a == pt1.m_a) {
return pt1.m_b > pt2.m_b;
}
return pt1.m_a > pt2.m_a;
};
Also, you created this issue you have with the threshold. Simple lower the threshold to something close to zero and the issue will go away, else you'll have to live with it. I don't see any other way.
Unfortunately, thresholds don't work well with STL ordered containers. A requisite for the ordering criterion is that it defines a strict weak ordering; this means, amongst other things, that if a == b and b == c, then a == b (== meaning here equivalent; this is, neither greater nor lesser than, not necessarily equal).
We might have pt1.a = 0.004, pt2.a = 0.01 and pt3.a = 0.016; according to your criterion, pt1.a == pt2.a and pt2.a == pt3.a, but pt1.a =\= pt3.a. Your ordering criterion does not meet the prerequisites for std::multiset and you cannot use it.
Related
I am trying to access std::map with simple custom key, but while most of the time this works, every once in a while, depending on the values given, it will fail to access the mapped value.
Here I baked a test program, that shows the issue in more detail:
#include <map>
#include <cstdint>
#include <cassert>
struct key_type
{
uint32_t a;
uint32_t b;
bool operator<(const key_type& value) const
{
if (value.a < a)
return true;
if (value.b < b)
return true;
return false;
}
key_type(uint32_t a, uint32_t b) : a(a), b(b)
{}
};
std::map<key_type, int*> test;
int get_int(uint32_t a, uint32_t b)
{
if (test.count(key_type(a, b)) == 0)
{
int* r = new int;
assert(r != nullptr);
key_type key = key_type(a, b);
test[key] = r;
assert(test[key] != nullptr);
}
return *test[key_type(a,b)];
}
Now I try to call get_int with two different sets of arguments. The first case works as expected.
int main(int argc, char* argv[])
{
get_int(2, 4);
get_int(3, 4);
get_int(4, 5);
get_int(2, 1);
get_int(120, 1);
return 0;
}
Now if I change the set of values a bit, everything explodes.
int main(int argc, char* argv[])
{
get_int(2, 4);
get_int(3, 4);
get_int(4, 5);
get_int(120, 1);
return 0;
}
The "assert(test[key] != nullptr);" fails. While I can circumvent the actual problem, but I would like to know what happens here under the surface that causes this behaviour?
Your comparison operator does not make much sense. The complement to
(value.a < a)
includes also cases where value.a > a.
If you make the entire body of the comparison operator:
return std::make_pair(a, b) < std::make_pair(value.a, value.b);
even better would be to use std::tie:
return std::tie(a, b) < std::tie(value.a, value.b);
Your operator< does not impose a Strict Weak Ordering™. Therefore, your attempt to use the map is undefined behaviour.
Basically, the operator doesn't actually produce a single ordering that orders all values of that type.
Consider:
bool operator<(const key_type& value) const
{
if (value.a != a)
return value.a < a;
if (value.b != b)
return value.b < b;
return false;
}
Your ordering is loosely weak. Read this article from wikipedia and this one from Wolfram.
I hope you understand the importance of these articles but regardless look at the following case, according to your algorithm
(3,2) < (4,1) returns true
and
(4,1) < (3,2) returns true
the std::map requires strong ordering and the above will cause undefined behaviours.
To fix you must do the following
if a < value.a return true;
if a > value.a return false;
if b < value.b return true;
return false;
#include<bits/stdc++.h>
using namespace std;
struct segment{
int a;
int b;
int c;
bool const operator<(const segment &o) const {
return a < o.a;
}
};
int main()
{
map<segment,int> myMap;
map<segment,int>::iterator it;
struct segment x,y,z;
x.a=2;
x.b=4;
x.c=6;
y.a=2;
y.b=5;
y.c=8;
z.a=2;
z.b=4;
z.c=6;
myMap[y]++;
myMap[z]++;
myMap[x]++;
for( it =myMap.begin(); it != myMap.end(); it++)
cout<<(*it).first.a<<" "<<(*it).second<<endl;
return 0;
}
it gives result as
2 3
but i want it to print
2 1
2 2
In short I want to increment the value of the map if exactly the same struct instance is fed instead of making a new copy
IMO the best way to compare multiple members is using std::tie as it is much harder to mess up:
bool const operator<(const segment &o) const {
return std::tie(a, b, c) < std::tie(o.a, o.b, o.c);
}
Edit: Would just like to add this link to cppreference as the example there is almost exactly your question.
You can change your less operator to:
bool const operator<(const segment &o) const {
return a < o.a || (a == o.a && b < o.b) || (a==o.a && b==o.b && c < o.c) ;
}
This compares the values in the order of a, b, c.
But you can change it anyway you want to compare the structure.
As far as your map is concerned, there is only one unique object here. In terms of the comparison you specified, and the implied equivalence, x == y and y == z. Why? Neither of them is smaller than the other, so, according to STL logic by comparison, they must be equivalent.
Perhaps you're looking for a std::multimap.
Alternatively, if you want to define inequality (and hence implied equivalence) in terms of all the members, you could do something like this:
#include <tuple>
bool const operator<(const segment &o) const {
return std::make_tuple(a, b, c) < std::make_tuple(o.a, o.b, o.c);
}
P.S. You should avoid including stuff from bits, as you're including stuff from the implementation. Instead, try to use stuff like
// See? no bits.
#include <map>
Quite often I have two variables foo1 and foo2 which are numeric types. They represent the bounds of something.
A user supplies values for them, but like a recalcitrant musician, not necessarily in the correct order!
So my code is littered with code like
if (foo2 < foo1){
std::swap(foo2, foo1);
}
Of course, this is an idiomatic sort with two elements not necessarily contiguous in memory. Which makes me wonder: is there a STL one-liner for this?
I suggest to take a step back and let the type system do the job for you: introduce a type like Bounds (or Interval) which takes care of the issue. Something like
template <typename T>
class Interval {
public:
Interval( T start, T end ) : m_start( start ), m_end( end ) {
if ( m_start > m_end ) {
std::swap( m_start, m_end );
}
}
const T &start() const { return m_start; }
const T &end() const { return m_end; }
private:
T m_start, m_end;
};
This not only centralizes the swap-to-sort code, it also helps asserting the correct order very early on so that you don't pass around two elements all the time, which means that you don't even need to check the order so often in the first place.
An alternative approach to avoid the issue is to express the boundaries as a pair of 'start value' and 'length' where the 'length' is an unsigned value.
No, but when you notice you wrote the same code twice it's time to write a function for it:
template<typename T, typename P = std::less<T>>
void swap_if(T& a, T& b, P p = P()) {
if (p(a, b)) {
using std::swap;
swap(a, b);
}
}
Â
std::minmax returns pair of smallest and largest element. Which you can use with std::tie.
#include <algorithm>
#include <tuple>
#include <iostream>
int main()
{
int a = 7;
int b = 5;
std::tie(a, b) = std::minmax({a,b});
std::cout << a << " " << b; // output: 5 7
}
Note that this isn't the same as the if(a < b) std::swap(a,b); version. For example this doesn't work with move-only elements.
if the data type of your value that you're going to compare is not already in c++. You need to overload the comparison operators.
For example, if you want to compare foo1 and foo2
template <class T>
class Foo {
private:
int value; // value
public:
int GetValue() const {
return value;
}
};
bool operator<(const Foo& lhs, const Foo& rhs) {
return (lhs.GetValue() < rhs.GetValue());
}
If your value is some type of int, or double. Then you can use the std::list<>::sort member function.
For example:
std::list<int> integer_list;
int_list.push_back(1);
int_list.push_back(8);
int_list.push_back(9);
int_list.push_back(7);
int_list.sort();
for(std::list<int>::iterator list_iter = int_list.begin(); list_iter != int_list.end(); list_iter++)
{
std::cout<<*list_iter<<endl;
}
Imagine the following scenario:
class A
{
int a[50];
int* GetAPtr() { return a; };
};
...
A b;
if(b.GetAPtr()[22] == SOME_RANDOM_DEFINE) do_this_and_that();
Is this kind of access considered bad practice? b.GetAPtr()[22]
To clarify my situation:
1. I cannot use new/malloc in this case, the array muste be static
2. This is meant to encapsulate older C code that uses multiple arrays where this comes extremly handy
3. I know that returning a pointer can possibly return a NULL pointer, we do not talk about that issue here
If you really need such const expression you could make it into a function:
class A
{
int a[50];
bool check_this_and_that() { return a[22] == SOME_RANDOM_DEFINE; };
};
...
A b;
if(b.check_this_and_that()) do_this_and_that();
magic numbers are bad in general but inside a class logic it's more forgiveable and outsiders don't have to see this.
Yes, it is bad practice, because you have no way of knowing how long the array is. You could follow the idiomatic standard library approach and return begin and end pointers, pointing to the first and one-past-last elements.
class A
{
int a[50];
int* begin() { return &a[0]; };
int* end() { return &a[50]; };
const int* begin() const { return &a[0]; };
const int* end() const { return &a[50]; };
size_t size() const { return 50; } // this could be handy too
};
As well as giving you the tools to iterate over the elements like you would over a standard library container, this allows you to check whether any pointer to an element of the array is < v.end(). For example
it* it = b.begin() + 22;
if(it < b.end() && *it == SOME_RANDOM_DEFINE) do_this_and_that();
This makes it trivial to use standard library algorithms:
A b;
// fill with increasing numbers
std::iota(b.begin(), b.end());
// sort in descending order
std::sort(s.begin(), s.end(), std::greater<int>());
// C++11 range based for loop
for (auto i : b)
std::cout << i << " ";
std::endl;
GetAPtr is a method for accessing a private data member. Now ask yourself what are the advantages of b.GetAPtr()[22] over b.a[22]?
Encapsulating data is a good way to maintain constraints on and between data members. In your case there is at least a correlation between the a array and its length 50.
Depending on the use of A you could build a interface providing different access patterns:
class A {
int a[50];
public:
// low level
int atA(unsigned i) const { return a[i]; }
// or "mid" level
int getA(unsigned i) const { if(i >= 50) throw OutOfRange(); return a[i]; };
// or high level
bool checkSomething() const { return a[22] == SOME_RANDOM_DEFINE; }
};
Assume I'm given two unsigned integers:
size_t A, B;
They're loaded out with some random numbers, and A may be larger, equal, or smaller than B. I want to loop from A to B. However, the comparison and increment both depend on which is larger.
for (size_t i = A; i <= B; ++i) //A <= B
for (size_t i = A; i >= B; --i) //A >= B
The obvious brute force solution is to embed these in if statements:
if (A <= B)
{
for (size_t i = A; i <= B; ++i) ...
}
else
{
for (size_t i = A; i >= B; --i) ...
}
Note that I must loop from A to B, so I can't have two intermediate integers and toss A and B into the right slots then have the same comparison and increment. In the "A is larger" case I must decrement, and the opposite must increment.
I'm going to have potentially many nested loops that require this same setup, which means every if/else will have a function call, which I have to pass lots of variables through, or another if/else with another if/else etc.
Is there any tricky shortcut to avoid this without sacrificing much speed? Function pointers and stuff in a tight, often repeated loop sound extremely painful to me. Is there some crazy templates solution?
My mistake, originally misinterpreting the question.
To make an inclusive loop from A to B, you have a tricky situation. You need to loop one past B. So you work out that value prior to your loop. I've used the comma operator inside the for loop, but you can always put it outside for clarity.
int direction = (A < B) ? 1 : -1;
for( size_t i = A, iEnd = B+direction; i != iEnd; i += direction ) {
...
}
If you don't mind modifying A and B, you can do this instead (using A as the loop variable):
for( B+=direction, A != B; A += direction ) {
}
And I had a play around... Don't know what the inlining rules are when it comes to function pointers, or whether this is any faster, but it's an exercise in any case. =)
inline const size_t up( size_t& val ) { return val++; }
inline const size_t down( size_t& val ) { return val--; }
typedef const size_t (*FnIncDec)( size_t& );
inline FnIncDec up_or_down( size_t A, size_t B )
{
return (A <= B) ? up : down;
}
int main( void )
{
size_t A = 4, B = 1;
FnIncDec next = up_or_down( A, B );
for( next(B); A != B; next(A) ) {
std::cout << A << endl;
}
return 0;
}
In response to this:
This won't work for case A = 0, B = UINT_MAX (and vice versa)
That is correct. The problem is that the initial value for i and iEnd become the same due to overflow. To handle that, you would instead use a do->while loop. That removes the initial test, which is redundant because you will always execute the loop body at least once... By removing that first test, you iterate past the terminating condition the first time around.
size_t i = A;
size_t iEnd = B+direction;
do {
// ...
i += direction;
} while( i != iEnd );
size_t const delta = size_t(A < B? 1 : -1);
size_t i = A;
for( ;; )
{
// blah
if( i == B ) { break; }
i += delta;
}
What are you going to do with the iterated value?
If this is going to be some index in an array, you should use the relevant iterator or reverse_iterator class, and implement your algorithms around these. Your code will be more robust, and easier to maintain or evolve. Besides, a lot of tools in the standard library are built using these interfaces.
Actually, even if you don't, you may implement an iterator class which returns its own index.
You can also use a little bit of metaprogramming magic to define how your iterator will behave according to the order of A and B.
Before going further, please consider that this would only work on constant values of A and B.
template <int A,int B>
struct ordered {
static const bool value = A > B ? false: true;
};
template <bool B>
int pre_incr(int &v){
return ++v;
}
template <>
int pre_incr<false>(int &v){
return --v;
}
template <int A, int B>
class const_int_iterator : public iterator<input_iterator_tag, const int>
{
int p;
public:
typedef const_int_iterator<A,B> self_type;
const_int_iterator() : p(A) {}
const_int_iterator(int s) : p(s) {}
const_int_iterator(const self_type& mit) : p(mit.p) {}
self_type& operator++() {pre_incr< ordered<A,B>::value >(p);return *this;}
self_type operator++(int) {self_type tmp(*this); operator++(); return tmp;}
bool operator==(const self_type& rhs) {return p==rhs.p;}
bool operator!=(const self_type& rhs) {return p!=rhs.p;}
const int& operator*() {return p;}
};
template <int A, int B>
class iterator_factory {
public:
typedef const_int_iterator<A,B> iterator_type;
static iterator_type begin(){
return iterator_type();
}
static iterator_type end(){
return iterator_type(B);
}
};
In the code above, I defined a barebone iterator class going accross the values from A to B. There's simple metaprogramming test to determine whether A and B are in ascending order, and pick the correct operator (++ or --) to go through the values.
Finally, I also defined a simple factory class to hold begin and end iterators methods, Using this class let you have only one single point of declaration for your dependent type values A and B (I mean here that you only need to use A and B once for this container, and the iterators generated from there will be depending on these same A and B, thus simplifying code somewhat).
Here I provide a simple test program, outputing values from 20 to 11.
#define A 20
#define B 10
typedef iterator_factory<A,B> factory;
int main(){
auto it = factory::begin();
for (;it != factory::end();it++)
cout << "iterator is : " << *it << endl;
}
There might better ways of doing this with the standard library though.
The issue of using O and UINT_MAX for A and B was brought up. I think it should be possible to handle these cases by overloading the templates using these particular values (left as an exercise for the reader).
size_t A, B;
if (A > B) swap(A,B); // Assuming A <= B, if not, make B to be A
for (size_t i = A; A <= B; ++A) ...