i have to sort a vector. The vector contains pointers to objects of class "student".
the rating metric looks like this:
best finalgrade
if same finalgrade, less attempts
if same attempts, less id
students with 0 attempts are worse than students with 2 attempts and finalgrade 5, sort students with 0 attempts by less id
student looks like this:
private:
std::string name_;
int id_;
int attempts_; //max 2, if 0, exam not taken
int grade1_;
int grade2_;
int finalGrade_; // grade 5 for failed exam but still better than 0 attempts in rating
my problem is that i dont know how to handle attempts. because best number of attempts is 1 and its better than 2 attempts. but 2 attempts are better than 0 in the rating.
i hope you understand my problem and can help me. thx :)
There is a function available in the STL, called std::sort Which can take a comparator function, (either a function pointer, or a function object).
The comparator function has to return a boolean that says whether the first element should appear strictly before the second element.
Here's what I came up with for the comparator:
struct compare_student {
inline bool
operator() (Student *left, Student *right) const
{
if(left->attempts_ == 0 && right->attempts_ == 0)
return left->id_ < right->id_;
else if(left->attempts_ == 0)
return false;
else if(right->attempts_ == 0)
return true;
return
left->finalGrade_ < right->finalGrade_ || // Compare final grade
left->finalGrade_ == right->finalGrade_ && ( // If final grade same:
left->attempts_ < right->attempts_ || // Compare attempts
left->attempts_ == right->attempts_ && ( // If attempts same:
left->id_ < right->id_ // Compare id's
)
);
}
};
Obviously all your fields are private, so you will need to use their accessor methods rather than just accessing them directly, but here's how you use it:
vector<Student *> students {...};
std::sort(students.begin(), students.end(), compare_student{});
std::sort is not stable, this means if two elements are considered equal, then it is not necessarily the case that they will keep their relative order, which may be important to you. If it is, then there is also a function called std::stable_sort which does have such a guarantee, and is used in exactly the same way:
std::stable_sort(students.begin(), students.end(), compare_students{});
EDIT Notes on the implementation
compare_students is a class that has only one, public member, so rather than do this:
class compare_student {
public:
...
};
I shortened it to this:
struct compare_student {
...
};
(the two are equivalent in C++, a struct is just a class with default public access.)
Then as to what inline bool operator() means.
inline is a hint to the compiler that this function can be inlined, that is to say, replace the call with the code itself.
bool is the return type of the function.
operator() is the name of the function, It is a special-case function that gets called when you treat an object like a function:
Student *a, *b;
compare_student comparator {};
comparator(a, b); // calls comparator::operator()(a, b)
Sounds like the first line of your comparison function should be
if (left.attempts_ == 0)
From there, check whether right.attempts_ == 0 as well. If yes, check the IDs.
If no, then right has a grade and left doesn't, right is better.
Continue on with the rest of the rules. Eventually you can use rules like if (left.attempts_ < right.attempts_) but only after you've dealt with 0 attempts.
Related
I have a big problem..I'm beginner with programming and I try to make a program in C++ for a football league ranking which have to sort the teams depending on their points.
Can someone help with an idea, please?
I created a struct for the teams with the name and the number of points.
How can I sort the teams?P.S. Sorry for my bad English.
This is my code:
#include <iostream>
#include <algorithm>
using namespace std;
//I created a struct for the team.
struct team
{
char name;
int pct;
}v[20];
int main()
{ int i,sw,aux;
for(i=1;i<=4;i++)//read the names
{
cout<<"Team "<<i<<endl;
cin>>v[i].name;
}
for(i=1;i<=4;i++)//get the points
{
cout<<"Team "<<v[i].name<<" points"<<endl;
cin>>v[i].pct;
}
//bubble sort(not working)
do
{
sw=0;
for(i=1;i<=4;i++)
{
if(v[i].pct<v[i+1].pct)
aux=v[i].pct;
v[i].pct=v[i+1].pct;
v[i+1].pct=aux;
sw=1;
}
}while(sw==1);
for(i=1;i<=4;i++)
{
cout<<v[i].pct<<endl;
}
return 0;
}
You need to modify your sort part like this. Assuming you are sorting in desc order.
do
{
sw=0;
for(i=1;i<4;i++) //< not <= ,because in case of the last element you wont have any other element after it to compare
{
if(v[i].pct<v[i+1].pct) // use curly brace as you want all 4 following lines to be executed when its true
{
aux=v[i]; //swap entire struct not just one variable
v[i]=v[i+1];
v[i+1]=aux;
sw=1;
}
}
}while(sw==1);
Also you might need to edit variable type of team name as it can be string.
As you are using C++ there is a one liner function that you can use to sort
//#include<algorithm>
//define comparator function
bool cmp(team a, team b)
{
return a.pct < b.pct;
}
sort(v+1,v+4+1,cmp);
Also you can simply write the comparator inside the struct and then use the sort function:
struct team
{
char name;
int pct;
bool operator<(team other) const
{
return pct > other.pct;
}
}v[20];
sort(v+1,v+4+1);
I'm curious about why you include algorithm but do not use any of it. Do you know STL? Since you include algorithm, I guess you may know some simple functions such as swap, sort and copy. They are easy to use, you just need to type one line instead of writing a bubble sort by yourself. Before you use the sort function, you should define which order can take effect on these teams. Just like this:
bool compareTeams(const Team &t1, const Team &t2) {
if (t1.getScore() == t2.getScore()) {
return t1.getName() < t2.getName();
}
else {
return t1.getScore() < t2.getScore();
}
}
The code above defines a direction we consider to sort the teams, first we sort them by scores, if both of the scores are equal, then we sort them by their names.
Finally We can use the sort function which is ready-made in STL. Now the order we defined can be used. (I guess the array v[] stands for the teams. Am I right?)
std::sort(v, v + 20, compareTeams);
[UPDATE: My problem is solved! Lots of thanks to Mike Seymour and Niall and all you guys!]
My code has errors in the for loop and I do not know how to fix it :(
MyClass::ITECH7603Class(set<Student>* students) {
/* Initialize dynamically the group field */
group = new map<string, Student>();
for (set<Student>::iterator it = students->begin(); it != students->end(); it++) {
addStudent(it);
}
}
void MyClass::addStudent(Student* studentPtr) {
string fullName = studentPtr->getName() + " " + studentPtr->getSurname();
group->insert(pair<string, Student>(fullName, *studentPtr));
}
So the main idea is to loop through all students in the set, and add each student into a map group. Any help? Thank you very much!
for (set<Student>::iterator it = students->begin; it != students->end; it++) {
addStudent(it);
}
should be:
for (set<Student>::iterator it = students->begin(); it != students->end(); it++) {
//^^ //^^
addStudent(it);
}
addStudent takes a pointer, while it is an iterator, so can't be passed directly.
You should change addStudent to take either a value or a pointer/reference to const:
// option 1
void addStudent(Student);
addStudent(*it);
// option 2
void addStudent(Student const &);
addStudent(*it);
// option 3
void addStudent(Student const *);
addStudent(&*it);
If, as you say in a comment, you must leave it taking a mutable pointer, then you'll need some grotesquery to deal with the fact that elements of the set are immutable:
// nasty option
addStudent(const_cast<Student*>(&*it));
// slightly less nasty option
Student copy = *it;
addStudent(©);
Beware that the first option will give undefined behaviour if the function uses the dodgy pointer to make any modification to the Student object stored in the set. The second makes a temporary copy, which can be modified without breaking the set. This is fine as long as addStudent only stores a copy of the object passed to it, not the pointer itself, which will become invalid when copy is destroyed.
In c++11 you can use range for sytax:
for (const auto &student : *students)
{
addStudent(it);
}
Then change addStudent function signature to accept reference:
void MyClass::addStudent(const Student &student) {
While you've gotten answers that "fix" your code to the extent of compiling and producing results that you apparently find acceptable, I don't find them very satisfying in terms of code style. I would do this job rather differently. In particular, my code to do this wouldn't have a single (explicit) loop. If I needed to do approximately what you're asking for, I'd probably use code something like this:
std::pair<std::string, Student> make_mappable(Student &stud) {
return std::make_pair(stud.getName() + " " + stud.getSurName(), stud);
}
std::map<std::string, Student> gen_map(std::set<Student> const &input) {
std::map<std::string, Student> ret;
std::transform(input.begin(), input.end(),
std::inserter(ret, ret.end()),
make_mappable);
return ret;
}
There definitely would not be any new in sight, nor would there be any passing a pointer to a Student.
OTOH, since the data you're using as the key for your map is data that's already in the items in the set, it may more convenient all around to continue to use a set, and just specify a comparison function based on the student's name:
struct by_given_name {
bool operator()(Student const &a, Student const &b) const {
if (a.getName() < b.getName())
return true;
if (b.getName() < a.getName())
return false;
return a.getSurName() < b.getSurName();
}
};
std::set<Student, by_given_name> xform(std::set<Student> const &in) {
return std::set<Student, by_given_name>{in.begin(), in.end()};
}
For what its worth, a Live Demo of the latter.
Whether the latter is practical will typically depend on one other factor though: your ability to create a Student from only a name/surname. If you can't do that, searching by name will be inconvenient (at best), so you'd want to use a map.
I realize this probably isn't much (if any) help in completely what's apparently home-work for a class--but even if your class prevents you from actually turning in decent code, it seems worthwhile to me to at least try to learn to write decent code in addition to what it requires. If you do pass the class and get a job writing code, you'd probably rather your coworkers didn't want to hurt you.
What is this class for ?
class EqualTo {
private:
int target;
public:
EqualTo(int i) : target(i) {}
bool operator()(const int& i) const {
return i == target;
}
};
I have this class in a homework and they have used it with the find function just like :
it = list.find(EqualTo(3));
thanks
It's a functor; that is, an object that can be called like a function. It's more flexible than a function, since it can also contain state and use that when it's called. Here's an example of how to use it directly:
EqualTo is5(5); // Object to test whether numbers are 5
assert(is5(5)); // Returns true: value is 5
assert(!is5(42)); // Returns false: value is not 5
In this case, it takes a single value and returns a boolean to tell you whether that value satisfies some condition; a functor that does that is known as a predicate.
it = list.find(EqualTo(3));
This example uses a predicate to find an element in a container which matches the condition. In this case, it gives you an iterator pointing to the first element that equals 3.
I'm working on a problem which requires me to use the STL linked list class to represent a polynomials. I've made a good start on getting the class definition, however I'm a little confused as to where to go next (novice programmer - please excuse my potential ignorance).
class Polynomial
{
public:
Polynomial(); //Default constructor
Polynomial(pair<double,int>); //Specified constructor
void add(Polynomial);
Polynomial multiply(Polynomial);
void print();
private:
list<int> order_terms;
list<double> coeffs;
};
I have two questions:
1) It seems more elegant to store the terms and coefficients as a pair - however I'm unsure how to get that working using the STL list.
2) Regarding the add member function, I'm unsure how to implement it such that I can define a Polynomial and then add terms to it like this:
Polynomial test(pair<3.14,0>);
Polynomial test_2(pair<2,1>);
test.add(test_2);
The main thing I'm having issues with understanding how to access the terms stored in the other object and linking it to the first Polynomial.
Any help greatly appreciated.
EDIT: Code for the add() function - currently not working
void Polynomial::add(const Polynomial& rhs)
{
//Perform some sort of sort here to make sure both lists are correctly sorted
//Traverse the list of terms to see if there's an existing nth order
//term in the list on the left-hand-side polynomial.
list<int>::iterator itr;
list<int>::iterator itl;
for(itr=rhs->terms.begin(); itr!=rhs->terms.end(); itr++)
{
bool match=0;
//See if there's an existing terms, if so add to it
for(itl=terms.begin(); itl!=terms.end(); itl++)
{
if(*itl->second)==*itr->second)
{
*itl->first+=*itr->first;
match = 1;
}
}
//If not, this is the first nth order term so just push it onto the list
if(!match){ terms.push_back(*itr); //Perform the sort again }
}
To use a pair in a list you can do:
list<pair<double, int> > - note the space between the >. It's also nice to do something like
typedef pair<double, int> TermCoeff;
list<TermCoeff> equation;
To sort a list:
list<TermCoeff> equation;
// insert items
equation.sort(coeff_compare);
There are pre-defined comparator functions for a pair in the <utility> header. They compare the first elements and then the second ones if first is equal.
For your second question you should remember that an object of a class can access the member variables of an object of the same class, even if they are private. If you don't leave any gaps in your coefficients (in the constructor fill in missing ones with the second value of the pair set to 0) this means your add method can look like:
Polynomial& Polynomial::add(const Polynomial& rhs) {
// constructor should sort all terms and enforce that all terms are present
// lhs = current object (left hand side of operator)
// rhs = other object (right hand side of operator)
// example: lhs.add(rhs)
list<TermCoeff>::const_iterator rhs_iter = rhs.terms.begin();
list<TermCoeff>::iterator lhs_iter = terms.begin();
while(rhs_iter != rhs.terms.end()) {
if (lhs_iter != terms.end()) {
// add because we aren't at the end of current object's terms
lhs_iter->second += rhs_iter->second;
++lhs_iter;
} else {
// insert because we are at the end of current object's terms
terms.push_back(*rhs_iter);
lhs_iter = terms.end(); // keep the iterator at the end
}
++rhs_iter;
}
return *this;
}
int main (int argc, const char * argv[])
{
list<TermCoeff> first, second;
first.push_back(TermCoeff(0, 0.0)); // empty
first.push_back(TermCoeff(1, 5.0));
first.push_back(TermCoeff(2, 5.0));
second.push_back(TermCoeff(0, 6.0));
second.push_back(TermCoeff(1, 0.0)); // empty
second.push_back(TermCoeff(2, 8.0));
second.push_back(TermCoeff(3, 9.0));
Polynomial first_eq(first);
Polynomial second_eq(second);
first_eq.add(second_eq);
first_eq.print();
return 0;
}
Note that I returned a reference to the current object. This is a nice thing to do in an addition method because then you can chain additions:
first.add(second).add(third);
or
first.add(second.add(third));
Others have explained list<pair<double, int> > (and I like shelleybutterfly's suggestion to derive Polynomial from the list, except that I'd make it protected, not public, so that outside code is not too free to mess with the contents of the list).
But the add function is a little tricky, because adding two polynomials doesn't generally mean concatenating them or adding their terms together. The operation is actually more like merging-- and you'll soon see that the lists must be sorted. (In fact, it's more natural to represent polynomials as vectors, but I guess that's not the assignment.)
I suggest you implement Polynomial::add(pair<double, int>), first, then implement the other one (add(Polynomial &)) in terms of that.
I don't want to spell it out too much, since this looks like homework. Is this enough to point you in the right direction?
EDIT:
Your new code looks correct (albeit inefficient) if you fix a couple of bugs:
void Polynomial::add(const Polynomial& rhs)
{
// Don't forget to implement the sorting routine.
// The iterators must be of the correct type. And itr must be const,
// since you have declared rhs to be a const reference. The compiler
// will not allow you to have an iterator with the power to alter
// a const thing.
list<pair<double,int> >::const_iterator itr;
list<pair<double,int> >::iterator itl;
for(itr=rhs->terms.begin(); itr!=rhs->terms.end(); itr++)
{
bool match=false;
for(itl=terms.begin(); itl!=terms.end(); itl++)
{
// You have an extra parenthesis here, and too much dereferencing.
if(itl->second == itr->second)
{
itl->first+=itr->first;
match = true;
}
}
if(!match)
{ terms.push_back(*itr); //Perform the sort again
} // Be careful not to catch the closing brace in a comment
}
}
Once it is working, you can think about ways to make it cleaner and more efficient. For example, if you insert the new term in the right place, the list will always be in the right order and there will be no need for a sort routine.
As for using a pair, why not use a list<pair<double, int>> (list< pair<double, int> > for older compilers)? Or you could even define a separate class to hold your pair like so:
// example is implemented inline, you could always pull it out to
// your source file; although it's even possible that you could
// do everything inline if you want to allow just including a
// header but not having to link a separate file.
class CoeffAndTerm : public pair<double,int>
{
public:
// if you need it you should put extra functions here to
// provide abstractions:
double getTotalValue()
{
return first * second;
}
}
and then use
list<CoeffAndTerm> Polynomial;
as your variable, or even
// same stuff goes for this class RE: the inline function definitions
class Polynomial : public list<CoeffAndTerm>
{
public:
// same goes here for the abstraction stuff maybe things
// like in your current Polynomial class; assuming some
// functions here ...
Polynomial Multiply(Polynomial other)
{
Polynomial Result = new Polynomial();
for (int i=0; i < size(); ++i)
{
Result.addCoeffAndTerm(
new CoeffAndTerm(
other.first * first,
other.second * second
);
}
return Result;
}
}
so that you've got Polynomial being a derivation of the list itself. Not sure the exact usage of the Polynomial, so it's hard for me to speak to which makes more sense, but I like this way better as a general rule for a type such as this; seems to be that the polynomial "is a" list of coefficient and terms, it doesn't just "have" one. :) I'm sure that's debatable, and again it depends on the actual usage of your code.
for the operations, you could do reference returns, as in one of the other examples, but I have implemented the multiply without modifying the existing value, which you could also do for Add, Subtract, etc. so, assuming First, Second, Third, etc. are other polynomials
Polynomial Result = First.Multiply(Second).Add(Third).Subtract(Fourth);
you could also implement copy constructor, operator =, operator +, operator *, operator / and then do things that look like normal math:
Polynomial Result = First * Second + Third - Fourth;
While it's possible to use std::pair to group the term order and coefficient, I would recomment against it: it's not very readable - it's not clear what 'first' and 'second' means, and C++ will implicitly cast between numeric types - and you get no benefit from the added functionality of pair (ordering).
Instead, create a class like:
class Term {
double coeff_;
int exp_;
public:
Term(double coeff, int exp): coeff_(coeff), exp_(exp) {}
double coefficient() const { return coeff; }
int exponent() const { return exp; }
[...]
};
class Polynomial {
std::list<Term> terms;
[...]
Making fields public (e.g. by using struct or publicly deriving from pair) for performance reasons is not a good idea: inline constructor, getters and setters are just as fast as reading or writing the variable directly, and they have the advantage of encapsulating the implementation.
For that matter, you may want to create separate types to wrap polynomial coefficients and exponents themselves, in order to avoid mixing up numeric types, and performing nonsensical operations e.g.:
class Coefficient {
double val;
public:
explicit Coefficient(double value): val(value) {}
double getValue() { return val; }
double operator*(double rhs) { return val*rhs; }
Coefficient operator+(const Coefficient& rhs) {
return Coefficient(val+rhs.val);
}
[...]
};
etc.
Another possibility: instead of using a class, you could make as struct to represent the term and coefficient; you still can define methods on it just like a class, but the members are public by default which may make sense for efficiency reasons, especially if you're doing a lot of processing with these things. So, maybe:
struct CoeffAndTerm
{
int Term;
double Coeff;
private CoeffAndTerm(int parTerm, double parCoeff)
{
Term = parTerm;
Coeff = parCoeff;
}
public static CoeffAndTerm Make(int parTerm, double parCoeff)
{
return new CoeffAndTerm(parTerm, parCoeff);
}
// etc. and otherwise you can just do things as given in the example
// with the classes deriving from list<pair<int, double>>, e.g.,
// silly example again
public double getTotalValue()
{
return first * second;
}
}
and same applies otherwise as in the first example, again giving more direct access than that example had, but still allowing for the abstraction methods to be placed directly on the object
struct Polynomial : public list<CoeffAndTerm>
{
list<CoeffAndTerm> CoefficientsAndTerms;
Polynomial Multiply(Polynomial other)
{
Polynomial Result = new Polynomial();
for (int i=0; i < size(); ++i)
{
Result.addCoeffAndTerm(
new CoeffAndTerm(
other.first * first,
other.second * second
);
}
return Result;
}
// etc.
}
this is for an assignment so I will be deliberately general. My question is related to implementation decisions I already made--maybe they weren't good ones.
I have a list of pointers to structs, e.g. list<MyStruct*> bob; At one point I've needed to sort these pointers by one of the data members of their targets and I was able to do that easily with
bool sortbyarrival(const MyStruct* a, const MyStruct* b) {
return a->arrival < b->arrival;
}
And then calling bob.sort(sortbyarrival); Works great.
Now somewhere else I need to sort by a different criterion, which involves a counter in the program. I need something like return counter*a->arrival < counter*b->arrival; But the way I just described is the only way I know how to do a sort, I think, and I don't know how to pass my counter as an additional argument. How can I sort this list of pointers?
ETA: The counter is just a variable in main. So ideally I could call something like bob.sort(sortbyratio, counter); or sort(bob.begin(), bob.end(), sortbyratio, counter);
Similar to ltcmelo's example, but if the objects themselves don't contain the counter:
struct sort_with_counter {
sort_with_counter(const double d): counter(d) {}
bool operator()(const MyStruct* a, const MyStruct* b) {
return(counter*a->arrival < counter*b->arrival);
}
const double counter;
};
mylist.sort(sort_with_counter(5.0));
If your counter is an external variable like that though it won't affect the ordering (at least if it's positive - thanks onebyone!) - so this may in fact not be necessary at all (or maybe I misunderstand what you're after?). It's a useful technique in other cases though.
Create a functor, and store the extra value in the functor object:
struct CompareByCounter {
CompareByCounter(int c) : counter(c) {}
bool operator()(const MyStruct *lhs, const MyStruct *rhs) {
return (counter * lhs->arrival) < (counter * rhs->arrival);
}
private:
int counter;
};
// sort ascending
bob.sort(CompareByCounter(1));
// sort descending
bob.sort(CompareByCounter(-1));
Just create an function-object, a class/struct with an overload of operator() that does the right thing for you. In this case, taking into consideration the extra variables. Then, you pass an instance of it to the sort method.
struct my_comparison : binary_function<MyStruct const*, MyStruct const*, bool>
{
bool operator()(MyStruct const* a, MyStruct const* b)
{
return (a->counter * a->arrival) < (b->counter * b->arrival);
}
};
//Use it this way.
my_comparison comp;
//Set the arrival and counter data in instance comp.
/* ... */
//Now, pass it to the list.
bob.sort(comp);
EDIT: I just noticed that you have a list of pointers so I changed a bit the struct.