Home > other >  Could sort() function directly obtain the input role and sort some struct value based on the role?
Could sort() function directly obtain the input role and sort some struct value based on the role?

Time:03-11

here is my code:

struct student {
    int num;
    char name[15];
    int score;
}stu[3] = { 0 };

bool cmp_num(student a, student b) {
    return a.num > b.num;
}
bool cmp_name(student a, student b) {
    return strcmp(a.name, b.name) > 0;
}
bool cmp_score(student a, student b) {
    return a.score < b.score;
}
int main() {
    stu[0] = { 1,"a",3 };
    stu[1] = { 2,"c",2 };
    stu[2] = { 3,"b",1 };
    printf("please input the role of sort:\n");
    char choice[20];
    scanf("%s", &choice);
    if (strcmp(choice,"num") == 0)
    {   
        sort(stu, stu   4, cmp_num);
    }
    else if (strcmp(choice, "name") == 0)
    {
        sort(stu, stu   4, cmp_name);
    }
    else if (strcmp(choice, "score") == 0)
    {
        sort(stu, stu   3, cmp_score);
    }
    else
    {
        printf("wrong!");
    }
    printf(" sort done!\n");
    for (int k = 0; k < 3; k  )
    {
        printf("%d,%s,%d\n", stu[k].num, stu[k].name, stu[k].score);
    }
    return 0;
}

It realize that sort stu[3] based on the role I input. my question:if I just define one function cmp(),could I get the same reault like my code? specifically,could just functinon sort() or its inside function cmp() know the "type" I input and then sort it based on the "type"? or can we through the lambda expression to realize it?

CodePudding user response:

You can have both the reading and the sorting depend on a pointer-to-member.

So instead of

void read_num1(student & s) {
    std::scanf("%d", std::addressof(s.num1));
}

void compare_num1(student lhs, student rhs) {
    return lhs.num1 < rhs.num1;
}

int main() {
    student students[20];
    for (student & s : students) {
        read_num1(s);
    }

    std::sort(std::begin(students), std::end(students), compare_num1);
}

You have functions that have an extra parameter, and close over that where necessary

using member_t = int (student::*);

void read(student & s, member_t member) {
    std::scanf("%d", std::addressof(s.*member));
}

void compare(student lhs, student rhs, member_t member) {
    return (lhs.*member) < (rhs.*member);
}

int main() {
    member_t member = /* some condition */ true ? &student::num1 : &student::num2;

    student students[20];
    for (student & s : students) {
        read(s, member);
    }

    std::sort(std::begin(students), std::end(students), [member](student lhs, student rhs) { return compare(lhs, rhs, member); });
}

CodePudding user response:

I recommend using the ranges interface for sort in the STL. Change your array to use std::array or better std::vector and then you can do sorting as simple as this:

ranges::sort(s, [](int a, int b) { return a > b; });
print("Sort using a lambda expression", s);

Particle particles[] {
    {"Electron", 0.511}, {"Muon", 105.66}, {"Tau", 1776.86},
    {"Positron", 0.511}, {"Proton", 938.27}, {"Neutron", 939.57},
};
ranges::sort(particles, {}, &Particle::name);
print("\nSort by name using a projection", particles, '\n');
ranges::sort(particles, {}, &Particle::mass);
print("Sort by mass using a projection", particles, '\n');

And if you want to sort by multiple fields, e.g last_name, first_name, you can use a std::tuple with references into your struct as projection. std::tuple has lexicographical comparison so you really just have to give the order of fields and the rest happens by magic.

  • Related