This question already has an answer here:
How to sort an array of structs in C?
(1 answer)
Closed 6 years ago.
I have a C struct declared as below (just a simple example):
typedef struct
{
float score;
char* name;
}
person;
So, along the program, I changed the value of the person's score (i gave them an initial value). So, i plan to put all of the scores inside a vector and sort them. Then, I would like to print a list of the name of all the persons, starting from biggest score to smallest score. Any idea on this last part? I'm not sure how to code it. Thanks.
Similar to the C++ approach above, there is a library function in the C standard library called qsort. It is based upon a comparision function, that creates the ordering on the array members.
For your case a minimal example could look like this:
#include <stdlib.h>
#include <stdio.h>
// Definitiion of a person
typedef struct person
{
float score;
char *name;
} person;
// Some small epsilon
#define EPSILON 1.0e-3f
// Comaprision function. Returns <0 for a<b =0 for a=b and >0 for a>b
static int compare_people( const void *a, const void *b )
{
// Cast to the correct pointer type
const person *p1 = (const person*) a;
const person *p2 = (const person*) b;
// There are different ways of comparing floats to each other. In this case we use |a - b| < epsilon for some small epsilon
float difference = p2->score - p1->score;
if( difference <= -EPSILON )
{
return -1;
}
else if( difference >= +EPSILON )
{
return +1;
}
return 0;
}
int main()
{
// Initialize list of people
size_t num_people = 5;
person people[num_people];
people[0].score = 0.3f;
people[0].name = "Homer";
people[1].score = 1.4f;
people[1].name = "Marge";
people[2].score = 0.02f;
people[2].name = "Bart";
people[3].score = 13.2f;
people[3].name = "Lisa";
people[4].score = 1.0f;
people[4].name = "Maggie";
// Print unsorted
printf( "Unsorted:\n" );
for( size_t i = 0; i < num_people; ++i )
{
printf( " %s - %2.2f\n", people[i].name, people[i].score );
}
printf( "\n" );
// Sort
qsort( people, num_people, sizeof(person), &compare_people );
// Print sorted
printf( "Sorted:\n" ) ;
for( size_t i = 0; i < num_people; ++i )
{
printf( " %s - %2.2f\n", people[i].name, people[i].score );
}
printf( "\n" );
// Done
return EXIT_SUCCESS;
}
Note the comment about comparing floating point values.
If you are using Linux, you can investigate system calls and functions of the C standard library by looking up the corresponding man page, e.g.
man qsort
For example you ll have
vector<person> vec;
Now you want sort this using sort from STL
You first must create method for operator " < "
bool operator<(const person& a, const person& b){
return a.score<b.score;
}
I hope this helps you I m sorry for my bad grammar :)
Example of usage
sort(vec.begin(),vec.end(),[](const person& a, const person&b){return b<a});
Now your vector of person will be sorted upside - down order.
Related
I am quite new to c++ and I am building a model studying certain mutations in genes. My "genes" are defined as a function of two doubles, a and b. A single gene is saved in a std::pair format. The whole genome consists of four of these genes collected in a std:array.
I perform some changes on the genes and want to write the information in a text file for analysis. The way I have currently implemented this is tedious. I have separate functions (8 in total) which collect the information like g[i].first, g[i[.second etc. for every i in the array. I feel this could be done much more efficiently.
Relevant code:
Declaration of data type:
using gene = std::pair<double, double>;
using genome = std::array<gene, 4>;
Function in which I create a genome called g:
genome Individual::init_Individual()
{
double a1, a2, a3, a4 = -1.0;
double b1, b2, b3, b4 = 0.0;
gene g1{ a1,b1 };
gene g2{ a2,b2 };
gene g3{ a3,b3 };
gene g4{ a4,b4 };
genome g{g1,g2,g3,g4};
return g;
}
Example of collect function:
double get_Genome_a1() { return g[0].first; };
Function in which I write information to a text file:
void Individual::write_Statistics(unsigned int &counter)
{
//Generate output file stream
std::ofstream ofs;
ofs.open("data.txt", std::ofstream::out | std::ofstream::app);
ofs << counter << std::setw(14) << get_Genome_a1() << std::setw(14)
<< get_Genome_a2() << std::setw(14) << get_Genome_b1() <<
std::setw(14) << get_Genome_b2() << "\n";
}
ofs.close();
}
etc, etc. So the final result of my data file in this example looks like this:
1 a1 a2 b1 b2
2 a1 a2 b1 b2
3 a1 a2 b1 b2
etc, etc.
My question:
I am currently storing the two doubles in a std::pair, which I collect in a std::array. Is this an efficient storage mechanism or can this be improved?
Is there a way to directly reference an individual element from my custom data type "genome" using only one function to write every element away in the exact same manner as I am doing now (with fourteen spaces between every element)? Something in pseudocode like: get_Genome() {return g;};, and when you call it you can specify the element like: get_Genome([0].first) which would be the first value stored in the first pair of the array, for example.
Happy to learn, any insight is appreciated.
Your storage is good. Neither pair nor array requires indirect/dynamic allocation, so this is great for cache locality.
As for referencing elements, no, not exactly like that. You could have an enum with members FIRST, SECOND then pass that as another argument to get_Genome. But, honestly, this doesn't seem to me to be worthwhile.
Overall, your approach looks great to me. My only suggestions would be:
Re-use one ofstream
…rather than opening and closing the file for every sample. You should see substantial speed improvements from that change.
You could make one in your main or whatever, and have write_Statistics take a std::ostream&, which would also be more flexible!
Initialise a bit quicker
All those declarations in init_Individual may get optimised, but why take the risk? The following is pretty expressive:
genome Individual::init_Individual()
{
const double a = -1.0;
const double b = 0.0;
return {{a, b}, {a, b}, {a, b}, {a, b}};
}
It's worth noting here that your double initialisations were wrong: you were only initialising a4 and b4; your compiler ought to have warned you about this. But, as shown, we don't need all of those anyway as they [are intended to] have the same values!
Your array looks good, however using std::pair in this situation might make it a bit more tedious. I would create 2 simple classes or structures one to represent a gene and the other to represent your genome. I'd still use array. The class might look something like this:
#include <array>
const int genesPerGenome = 4; // change this to set how many...
struct Gene {
double a_;
double b_;
Gene() = default;
Gene(double a, double b) : a_(a), b_(b) {}
};
struct Genome {
std::array<Gene, genesPerGenome> genome_;
int geneCount_{0};
Genome() = default;
void addGene(const Gene& gene) {
if ( geneCount_ >= genesPerGenome ) return;
genome_[geneCount_++] = gene; // post increment since we added one
}
};
Then I would have a stand alone function that would generate your genome as such:
void generateGenome( Genome& genome ) {
for (int i = 0; i < 4; i++) {
// When looking at your example; I notices that the genes were all
// initialized with [-1.0,0.0] so I used Gene's constructor to init
// them with those values.
Gene gene(-1.0, 0.0);
genome.addGene(gene);
}
}
Then to couple these together, I'll just print them to the console for demonstration. You can then take this approach and apply it to what ever calculations that will be done and then writing the results to a file.
#include <array>
#include <iostream>
int main() {
Genome genome;
generateGenome( genome );
// printing to console here is where you would do your calculations then write to file
for ( int i = 0; i < 4; i++ ) {
if ( i >= genome.geneCount_ ) break; // prevent accessing beyond array bounds
std::cout << (i+1) << " [" << genome.genome_[i].a_ << "," << genome.genome_[i].b_ << "]\n";
}
return 0;
}
-Output- - No calculations, only the initialized values:
1 [-1,0]
2 [-1,0]
3 [-1,0]
4 [-1,0]
Maybe this will help. From here you can write a operartor<<() function that will take an ostream reference object and a const reference to a Genome and from there you should be able to print the entire Genome to file in a single function call.
-Edit-
User t.niese left a comment with a valid point that I had overlooked. I was using a static variable in the addGene() function. This would work okay as long as you are working only with a single Genome, but if you had more than one Genome object, every time you'd call the addGene() function this value would increase and you wouldn't be able to add more than gene to each genome due to the condition of the if statement in the addGene() function.
I had modified the original code above to fix this limitation. Here I removed the static variable and I introduced two new variables; one is a const int that represents how many genes per genome as it will be used to define the size of your array as well as checking against how many genes to add to that genome. The other variable I added is a member variable to the Genome class itself that keeps track of how many genes there are per each Genome object.
Here is an example of what i meant in my comment by overloading the operator [].
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
class Genome {
public:
typedef std::pair<double, double> gene;
private:
double a1 = -1.0, a2 = -1.0, a3 = -1.0, a4 = -1.0;
double b1 = 0.0, b2 = 0.0, b3 = 0.0, b4 = 0.0;
gene g1{ a1,b1 };
gene g2{ a2,b2 };
gene g3{ a3,b3 };
gene g4{ a4,b4 };
public:
Genome() {}
const double operator[] (std::string l) const {
if (l == "a1") {return g1.first;}
else if (l == "b1") {return g1.second;}
else if (l == "a2") {return g2.first;}
else if (l == "b2") {return g2.second;}
else if (l == "a3") {return g3.first;}
else if (l == "b3") {return g3.second;}
else if (l == "a4") {return g4.first;}
else if (l == "b4") {return g4.second;}
else {
throw std::invalid_argument("not valid label");
}
}
void setvalue(std::string l, double x) {
if (l == "a1") {g1.first = x;}
else if (l == "b1") {g1.second = x;}
else if (l == "a2") {g2.first = x;}
else if (l == "b2") {g2.second = x;}
else if (l == "a3") {g3.first = x;}
else if (l == "b3") {g3.second = x;}
else if (l == "a4") {g4.first = x;}
else if (l == "b4") {g4.second = x;}
else {
throw std::invalid_argument("not valid label");
}
}
void write_Statistics(unsigned int counter) {
std::ofstream ofs;
ofs.open("data.txt", std::ofstream::out | std::ofstream::app);
ofs << counter
<< std::setw(14) << (*this)["a1"] << std::setw(14) << (*this)["a2"]
<< std::setw(14) << (*this)["b1"] << std::setw(14) << (*this)["b2"] << "\n";
ofs.close();
}
}
};
I don't know if you may find useful to access to the individual genes by a label instead of an index, but this is what this overload do.
int main(int argc, char **argv) {
Genome a = Genome();
std::cout << a["b1"] << std::endl; #this prints 0
a.setvalue("b2", 3.0);
std::cout << a["b2"] << std::endl; #this prints 3
a.write_Statistics(0);
return 0;
}
The following C++ code sorts an array in descending order using qsort:
#include<iostream>
#include<cstdio>
#include <stdlib.h>
using namespace std;
struct player {
double data;
int index;
};
struct player A[] = {{0.690277,0}, {0.517857,1}, {0.780762,2}, {0.0416667,3}, {0.0416667,4}};
int compare (const void * a, const void * b)
{
return ( ((struct player*)b)->data - ((struct player*)a)->data );
}
int main ()
{
int n;
qsort (A, 5, sizeof(struct player), compare);
for (n=0; n<5; n++)
printf ("data=%lf, index=%d\n", A[n].data, A[n].index);
return 0;
}
But I am getting output like this:
data=0.517857, index=1
data=0.780762, index=2
data=0.041667, index=3
data=0.041667, index=4
data=0.690277, index=0
Is there anything wrong in the code?
In compare, you are subtracting two sub-1 doubles and casting them to an int, the result will in most cases be 0. Instead of subtracting you should compare them and return -1/1.
Consider using this compare instead:
int compare (const void * a, const void * b)
{
auto x = reinterpret_cast<const player*>(a);
auto y = reinterpret_cast<const player*>(b);
if(x->data < y->data)
return -1;
if(x->data > y->data)
return 1;
return 0;
}
That said, this style of coding is ancient/deprecated/bad practice.
Consider writing similar to this instead:
#include<iostream>
#include <algorithm>
struct player {
double data;
int index;
bool operator < (const player& p) const
{
return data < p.data;
}
};
auto A = std::vector<player>{
{0.690277,0}, {0.517857,1},
{0.780762,2}, {0.0416667,3}, {0.0416667,4}
};
int main ()
{
std::sort(std::begin(A), std::end(A));
for(const auto& x: A)
std::cout << "data=" << x.data << ", "
<< "index=" << x.index << "\n";
}
Suggested changes:
don't import std names globally
don't mix cstdio and iostreams (only include one of them)
use std::vector or std::array instead of native array
define the sorting order in the interface of the class (bool operator <). (this should also imply that you define the other arithmetic operators - it is good practice and avoids subtle bugs later, but it is not required for this particular implementation to compile and work)
use std::sort instead of qsort
don't use raw pointers (using them like this is a source for bugs)
This question already has answers here:
C++ fixed length string class?
(11 answers)
Closed 8 years ago.
Let's say I have a class that contains a pointer to string:
string *name;
Inside the constructor I would allocate memory for it:
*name = new string[256];
This way the size limit is set by the constructor, however is there a way to limit the size in the class itself at the pointer declaration?
Above is wrong!
Should be (probably)
*name = new char[256];
It's generally not a good idea to do this. If you are trying to limit the size provided by an end user, then you should apply a limit check when receiving the input. Internally, however, you should just use std::string in your program with no enforcement on the size. For example:
namespace {
const int kMaxInputSize = 256;
bool IsInputValid(const string& user_input) {
return user_input.size() < kMaxInputSize;
}
// ...
}
// ...
Now, technically, you could use char name[256] to hard-code a string that can hold up to 256 characters (including the null terminator character), but that is incredibly error prone and ill advised.
If your compiler supports features of the C++ 2011 Standard then you can define the class the following way
#include <iostream>
#include <string>
class A
{
private:
static const size_t N = 26;
std::string *name = new std::string[N];
public:
typedef size_t size_type;
~A() { delete [] name; }
std::string & operator []( size_type i )
{
return name[i];
}
const std::string & operator []( size_type i ) const
{
return name[i];
}
size_type size() const { return N; }
// Definitions of copy/move constructors and copy/move assignment operators
};
int main()
{
A a;
for ( A::size_type i = 0; i < a.size(); i++ )
{
a[i] += char( 'A' + i % 26 );
}
for ( A::size_type i = 0; i < a.size(); i++ )
{
std::cout << a[i] << ' ';
}
std::cout << std::endl;
return 0;
}
The output is
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Do not forget to define destructor, copy/move constructors and copy/move assignment operators.
Also you could use smart pointer std::unique_ptr instead of the raw pointer.
I have defined 2 functions in my class Matrix as follows (in Matrix.hpp)
static Matrix MCopy( Matrix &a );
static Matrix MatInvert( Matrix &x )
static double MatDet( Matrix &x ); // Matdef function
In my Matrix.Cpp file, I defined these functions as follows:
Matrix Matrix::MatCopy( Matrix &a )
{
Matrix P( a.getRow() , a.getCol() , Empty );
int j=0;
while( j != P.getRow() ){
int i=0;
while( i != P.getCol() ){
P(j,i)=a(j,i);
++i;
}
++j;
}
return P;
}
Matrix Matrix::MatInvert( Matrix &x )
{
Matrix aa = Matrix::MatCopy(x); // i got error message here
int n = aa.getCol();
Matrix ab(n,1,Empty);
Matrix ac(n,n,Empty);
Matrix ad(n,1,Empty);
if(MatLu(aa,ad)==-1){
assert( "singular Matrix" );
exit(1);
}
int i=0;
while( i != n ){
ab.fill(Zero);
ab (i,0)=1.0;
MatRuecksub(aa, ab,ac,ad,i);
++i;
}
return ac;
}
ok this is the my MatDef function
double Matrix::MatDet( Matrix &x )
{
double result;
double vorz[2] = {1.0, -1.0};
int n = x.getRow();
Matrix a = Matrix::MatCopy(x);
Matrix p( n, 1, Empty);
int i = MatLu(a, p);
if(i==-1){
result = 0.0;
}
else {
result = 1.0;
int j=0;
while(j != n){
result *= a( static_cast<int>(p(j,0)) ,j);
++j;
}
result *= vorz[i%2];
}
return result;
}
but when I compile this, I get an error telling me that:
line 306:no matching function for call to ‘Matrix::Matrix[Matrix]’:
note: candidates are: Matrix::Matrix[Matrix&]
note:in static member function ‘static double Matrix ::MatDet[Matrix&]’:
I can not understand what the problem is since I am new to C++ programming, so please help me in fixing this error.
Where I have used
Matrix aa = Matrix::MatCopy(x);
It shows same error message like line 306 but with different notes so I think MatDef is not a problem.
Please give your comments to solve this. Thanks!
If you have a class called A and it has a static function foo you would call it this way
A::foo();
Reading material
It seems that you are trying to access a variable of the class from a static member function of that class. Static member functions cannot access regular variables of the class.
You need to check the link that #Woot4Moo suggested:
non-static vs. static function and variable
I wrote a program which shows the data about 5 students I want to know how to show the student name registration number and marks who obtained highest marks in the array.
The program is like that:
#include "stdio.h"
#include "conio.h"
struct student
{
char name[30];
char reg[10];
short int marks_dbms;
short int marks_prog;
};
void main()
{
short int i;
student a[5]={
{"salman","B-1499",92,98},
{"Haider","B-1489",34,87},
{"zeeshan","B-1897",87,90},
{"faizan","B-1237",56,66},
{"Asif","B-1233",88,83}
};
for(i=0; i<5; i++)
{
printf("%s\t",a[i].name);
printf("%s\t",a[i].reg);
printf("%d\t",a[i].marks_dbms);
printf("%d\t\n",a[i].marks_prog);
}
getch();
}
Thats a simple one.
int maxDbmsMarks = 0;
int maxMarksIndex = 0;
for(int i = 0; i < 5; i++)
{
if(a[i].marks_dbms > maxDbmsMarks )
{
maxDbmsMarks = a[i].marks_dbms;
maxMarksIndex = i;
}
}
printf("Student with the maximum dbms marks :\n");
printf("Name : %s, Reg : %s, dbmsMarks: %d, progMarks : %d\n", a[maxMarksIndex].name, a[maxMarksIndex].reg, a[maxMarksIndex].makrs_dbms, a[maxMarksIndex].marks_prog);
It is the same as finding the maximum number in an array. The above logic is for 'highest dbms marks', you can modify it to find the highest prog marks or highest combined marks.
First, write a function to compare students by their marks. It should accept two student parameters, and return true if the first student should be considered less than the second.
bool compareStudentsByMarks(const student & lhs, const student & rhs)
{
// This only compares by marks_dbms, I'm not sure how you want to do it.
return lhs.marks_dbms < rhs.marks_dbms;
}
Then, after including the <algorithm> header, use the function std::max_element, passing your function as the last argument.
const int SIZE_OF_STUDENT_ARRAY = 5;
student * pmax = std::max_element(a, a+SIZE_OF_STUDENT_ARRAY, compareStudentsByMarks);
// now pmax is a pointer to the student with the highest marks,
// you already know how to display their info
If you have access to C++0x, this is all a lot easier:
auto pmax = std::max_element(a, a+SIZE_OF_STUDENT_ARRAY,
[](const student & lhs, const student & rhs)
{
return lhs.marks_dbms < rhs.marks_dbms;
});