简体   繁体   English

标准排序似乎永远循环

[英]std sort seems to be looping forever

Right now I have a class to do a binary search. 现在,我有一个可以进行二进制搜索的类。 The class accepts a vector, but then I tell the class to sort. 该类接受一个向量,但随后我告诉该类进行排序。

I need to be able to have it sort by only first name potentially or last name, so I set a character argument as a choice in that class to change how I sort the vector. 我需要能够仅按名字或姓氏对它进行排序,因此我将一个字符参数设置为该类中的选择,以更改对向量进行排序的方式。 I also in that class made an operator() function to use *this, as a class pointer to sort the vector. 我还在该类中使一个operator()函数使用* this作为对向量进行排序的类指针。 But it seems to just be looping forever. 但这似乎永远循环。 Can anyone tell me why? 谁能告诉我为什么? Code Below. 下面的代码。

*note if there's some general practices I'm not following feel free to inform me. *请注意,如果我没有遵循某些常规做法,请随时通知我。 I don't want to start making bad habits now. 我现在不想开始养成不良习惯。

By request: Getname 根据要求:Getname

void personType::getName(string& first, string& last)
{
    // get the name and set it
    first = firstName;
    last = lastName;
}


bool sBinary::operator()(studentType student1, studentType student2){
    string toCheck1, toCheck2, fName1,lName1 ,fName2 , lName2;
    student1.getName(fName1, lName1);
    student2.getName(fName2, lName2);
    toCheck1=checkStr(fName1, lName1);
    toCheck2=checkStr(fName2,lName2);
    return toCheck1<toCheck2;
}

string sBinary::checkStr(string fName, string lName){
    string toCheck;
    switch (choice){
    case 'f':
    case 'F':
        toCheck=fName;
        break;
    case 'l':
    case 'L':
        toCheck=lName;
        break;
    case 'r':
    case 'R':
        toCheck=fName+lName;
        break;
    default:
        toCheck=lName+fName;

    }

    return toCheck;

}


sBinary::sBinary(vector<studentType> _sList, char _choice){
    sList=_sList;
    steps=0;
    choice=_choice;
    sort(sList.begin(),sList.end(), *this);
}

So, it seems not not to loop forever, but executes too long. 因此,似乎不是永远循环,而是执行时间太长。 It's completely different story. 这是完全不同的故事。 You have a couple of pessimisations in your code: The main concern is that you pass *this , to the sorting algorithm: 您的代码中有一些悲观:主要关注点是您将*this传递给排序算法:

sort(sList.begin(),sList.end(), *this);

std::sort takes comparation predicate by value and it copies it many times. std::sort按值获取比较谓词,并将其复制多次。 You can see it, if you define copy constructor: 如果定义复制构造函数,则可以看到它:

sBinary(const sBinary& r):choice(r.choice), sList(r.sList)
{
    std::cout << "copied\n";
}

And your vector gets copied along with the object itself. 您的向量将与对象本身一起复制。

For example, if the array size is 200, std::sort copies object 13646 times . 例如,如果数组大小为200,则std::sort复制对象13646次 It means, that 2700000 student copy operations involved. 这意味着涉及2700000个学生复制操作。

So, you should not pass *this to std::sort . 因此,您不应将*this传递给std::sort You'd better define static function lessThen instead of operator() and pass it to sorting algorithm. 您最好定义静态函数lessThen而不是operator()并将其传递给排序算法。

Further improvements: 进一步改进:

  1. Pass by reference , rather then by value. 通过引用传递 ,而不是通过值传递 For example, in your lessThen function declaration should look like 例如,在您的lessThen函数声明中,

     static bool lessThen(const studentType& student1, const studentType& student2); //^^^^^ ^ //constant reference 
  2. Refactor your studentType class. 重构您的studentType类。

    You'd better have 2 separate functions, returning first and last name (by constant reference). 您最好有2个独立的函数,返回名字和姓氏(通过常量引用)。 In this case you could get rid of copying names to temporary variables. 在这种情况下,您可以摆脱将名称复制到临时变量的麻烦。 Note, that when you have single function, you have to copy both first and last name, even if one name will never be used: 请注意,只有一个功能时,即使永远不会使用一个名称,也必须同时复制名字和姓氏:

     const std::string& first_name() const { return _fname; } const std::string& last_name() const { return _lname; } 

I'm including this only because you should know alternatives to how you're sorting this list. 我之所以将其包括在内,是因为您应该了解如何对列表进行排序。 Lol4t0 has already talked about the hideousness of having a comparator that is expensive to copy (and you would be hard pressed to have one more expensive than your original implementation). Lol4t0已经讨论过拥有比较器(复制比较昂贵)的丑陋性(并且很难拥有比原始实现昂贵的比较器)。

The std::sort algorithms work best when given as simple a comparator as possible, with as much chance for inlining it's implementation as it can get. 当给定尽可能简单的比较器时, std::sort算法最有效,并且有尽可能多的机会内联其实现。 Ideally you implement a comparator operator function like this: 理想情况下,您可以实现这样的比较器运算符功能:

struct cmpObjects
{
    bool operator ()(const Object& left, const Object& right) const
    {
        return (left compared to right somehow);
    }
}

First notice the use of const references. 首先请注意const引用的使用。 The only time you should consider NOT doing this is if your underlying data is an native intrinsic type (such as int , char , etc.). 唯一不考虑这样做的时间是,如果基础数据是本机固有类型(例如intchar等)。 In those cases it is actually faster to pass-by-value. 在这些情况下,按值传递实际上更快 But in this case, your student records are most-assuredly more efficient to access by reference (no copying). 但是在这种情况下,可以肯定的是,您的学生记录可以更高效地通过引用进行访问(无复制)。

Regarding your specific task, yours is a little more complicated based on the fact that you're sorting criteria is choice-based. 关于您的特定任务,由于您的排序标准是基于选择的,因此您的任务要复杂一些。 If you want to maximize sort-speed you ideally have a single, tight, cheaply copyable comparator for each choice case. 如果要最大程度地提高分类速度,则理想情况下,针对每种选择情况,都应使用一个紧凑的可廉价复制的比较器。 Then, use the proper comparator based on that choice, determined before invoking std::sort . 然后,根据该选择使用适当的比较器,该选择器调用std::sort 之前确定。

For example, if you know you're sorting on last name, then: 例如,如果您知道要对姓氏进行排序,则:

// compares last name
struct cmp_LName
{
    bool operator ()(const studentType& left, const studentType& right) const
    {
        return left.lastName < right.lastName;
    }
}

or perhaps first name, last name such as: 或名字,姓氏,例如:

// compares first name, then last name only if first name is identical.
struct cmp_FNameLName
{
    bool operator ()(const studentType& left, const studentType& right) const
    {
        int res = left.firstName.compare(right.firstName);
        return res < 0 || (res == 0 && left.lastName < right.lastName);
    }
}

This makes a partial peek at your sBinary constructor now look like this: 这使您的sBinary构造函数现在看起来像这样:

sBinary(const std::vector<studentType>& sList_, char choice)
    : sList(sList_)
{
    switch (choice)
    {
        case 'L':
        case 'l':
            std::sort(sList.begin(), sList.end(), cmp_LName());
            break;

        case 'R':
        case 'r':
            std::sort(sList.begin(), sList.end(), cmp_FNameLName());
            break;

        ....
    }
}

Notice first we're making the choice for what comparison technique we're choosing prior to actually calling std::sort . 请注意,首先要实际调用std::sort 之前选择要选择的比较技术。 When we do, we have the clear definition of what exactly that criteria is within the custom comparator we're using, and zero overhead it managing it. 当我们这样做时,我们就可以清楚地确定该标准到底在我们所使用的自定义比较器中是什么,并对其进行零开销管理。

So whats the trade off? 那有什么折衷呢? You would need four comparators (cmp_LName, cmp_FName, cmp_FNameLName, and cmp_LNameFName), triggering which to use based on your incoming choice. 您将需要四个比较器(cmp_LName,cmp_FName,cmp_FNameLName和cmp_LNameFName),根据传入选择触发使用哪个比较器。 However, the benefit for doing so cannot be overstated: This will be the fastest way to sort your list based on choice. 但是,这样做的好处不能被夸大: 这将是根据选择对列表进行排序的最快方法。


Addendum: Single Comparator 附录: 单个比较器

If you are absolutely positively married to the idea of using a single comparator, then make it as cheap to copy as possible, and bury the choice made in the sorting condition within it as const to give the compiler the best chance of cleaning up your code. 如果您绝对赞成使用单个比较器的想法,那么请使其尽可能便宜地进行复制,并将在排序条件下所做的选择隐藏为const以使编译器有最大的机会清理代码。 I've included a full expansion of sBinary below to show how this can be done, but I stress, this is not optimal if speed is your primary concern. 我在下面包括了sBinary的完整扩展,以展示如何做到这一点,但我强调,如果速度是您的首要考虑因素,那么这并不是最佳选择。

class sBinary
{
    // compare student based on fixed choice determine at construction.
    struct cmp_student
    {
        const char choice;
        cmp_student(char choice) : choice(choice) {};

        bool operator()(const studentType& left, const studentType& right) const
        {
            switch (choice)
            {
                case 'F':
                case 'f':
                    return left.firstName < right.firstName;

                case 'L':
                case 'l':
                    return left.lastName < right.lastName;

                case 'R':
                case 'r':
                {
                    int res = left.firstName.compare(right.firstName);
                    return res < 0 || (res == 0 &&  left.lastName < right.lastName);
                }

                default:
                {
                    int res = left.lastName.compare(right.lastName);
                    return res < 0 || (res == 0 &&  left.firstName < right.firstName);
                }
            }
        }
    };

public:
    sBinary(const std::vector<studentType>& sList, char choice)
        : sList(sList)
    {
        std::sort(sList.begin(), sList.end(), cmp_student(choice));
    }

    std::vector<studentType> sList;
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM