std::list Strict Weak Ordering - c++

I am having a lot of trouble using the std::list::sort function, it works a majority of the time, however every once in a while it throws an assertion 'invalid operator<'. Looking into this issue I have realized it is because my sort function is not following strict weak ordering, however when I look at my code I do not understand why it is not following strict weak ordering as it seems correct, what is it I am missing here?
The purpose of this function is to sort a list of elements into a formula string based on the hill system, ie. Carbon First, Hydrogen Second, all others alphabetically. The FormulaStruct simply represents a single element and amount in the full formula.
struct FormulaStruct
{
FormulaStruct(const std::string & strSymbol, int nNum, bool bHasCarbon)
:
m_strSymbol(strSymbol),
m_nNum(nNum), m_bHasCarbon(bHasCarbon)
{
}
bool operator < (const FormulaStruct & rhs)
{
//If the symbols are equal
if(m_strSymbol == rhs.m_strSymbol)
return true;
if(m_bHasCarbon)
{
if(m_strSymbol == "C")
return true;
else
if(rhs.m_strSymbol == "H")
return false;
}
return m_strSymbol < rhs.m_strSymbol;
}
bool operator == (const FormulaStruct & rhs)
{
return m_strSymbol == rhs.m_strSymbol;
}
std::string m_strSymbol;
int m_nNum;
bool m_bHasCarbon;
};
list<FormulaStruct> FormulaList; //A list of FormulaStructs, assumed to be filled
FormulaList.sort();
EDIT
bHasCarbon is the condition when there is carbon in the formula, as the hill system requires that if there is carbon in the formula than hydrogen will be next, otherwise everything is alphabetical including hydrogen, this is dictated in another section of my code.

Other answers have already addressed the m_strSymbol == rhs.m_strSymbol issue.
But, based on your description ("C" first, "H" next, everything else in order), it seems like you could want, if you have C++11:
return std::tie(m_strSymbol != "C", m_strSymbol != "H", m_strSymbol)
< std::tie(rhs.m_strSymbol != "C", rhs.m_strSymbol != "H", rhs.m_strSymbol);
This is an easy way to write StrictWeakOrderings (stolen from here)
Or, if you don't have C++11 (or Boost pre-C++11), you can do something like this:
// order of checks here is important, in case both are "C"
if(rhs.m_strSymbol == "C")
return false;
if(m_strSymbol == "C")
return true;
// neither symbol is "C"
if(rhs.m_strSymbol == "H")
return false;
if(m_strSymbol == "H")
return true;
// neither symbol is "C" or "H"
return m_strSymbol < rhs.m_strSymbol;
I'm pretty sure I did that right, but as stated in the article posted above, doing it manually is prone to error and probably should be avoided...also, this could definitely be optimized further to reduce the number of string comparisons, at the risk of introducing bugs and obfuscating the code.
But it's unclear what m_bHasCarbon means and what effect that's supposed to have, so I'm not sure if this is what you need or not.

//If the symbols are equal
if(m_strSymbol == rhs.m_strSymbol)
return true;
Meaning it is true for both a<b and b<a if the symbols are equal.
Perhaps you should return false, since a==b and thus !a<b, in this case.
Also your second set of compares are confusing.. what is m_bHasCarbon.

Related

Qt String Comparison

Suppose I have:
QString x;
Is the following code fragment:
if(x.compare("abcdefg") == 0){
doSomething();
}
else{
doSomethingElse();
}
... functionally equivalent to:
if(x == "abcdefg"){
doSomething();
}
else{
doSomethingElse();
}
I could prove this for myself by writing a fairly trivial program and executing it, but I was surprised I couldn't find the question / answer here, so I thought I'd ask it for the sake of future me / others.
QString::compare will only return zero if the string passed to it and the string it is called on are equal.
Qstring::operator== returns true if the strings are equal otherwise, false.
Since compare only returns zero when the strings are equal then
(qstrign_variable.compare("text") == 0) == (qstrign_variable == "text")
If qstrign_variable contains "text" in the above example. If qstrign_variable contains something else then both cases evaluate to false.
Also note that std::string has the same behavior

Overloaded Operator < to compare strings, sort linked list using string.compare?

If anything in this code looks weird (like the illogical use of pointers) it's because it's for an assignment, so no need to let me know that there's absolutely no reason to use pointers in this situation. Thank you for you help in advance.
The code works for me except one thing, the insertNode function uses an overloaded < from the Pet class. this operator is supposed to compare the strings using the string.compare() function and return true or false based on the resulting value. However no sorting is done at all and the input file is just read in normally from beginning to end.
The problem is in the snippet below
bool Pet::operator <(Pet &right)
{
if (name.compare(right.name) < 0)
return true;
else if (name.compare(right.name) > 0)
return false;
}
It seems to me that there is nothing wrong with this that would cause nothing to be changed. I haven't been able to test if the operators are right (> 0 and < 0) but i'm more concerned that it is doing nothing at all.
Your comparison function doesn't handle the case when the strings are equal.
bool Pet::operator <(Pet &right)
{
if (name.compare(right.name) < 0)
return true;
else if (name.compare(right.name) > 0)
return false;
}
string.compare returns a negative value if it's less then it's argument, 0 when they are equal and a positive value when it's bigger. You don't handle the case when it returns 0 and it therefore falls off the end of the function which is undefined behavior.
Change the else if to:
else if (name.compare(right.name) >= 0)
#Benjamin is right, your whole function could be shortened down to:
bool Pet::operator <(Pet &right)
{
return name.compare(right.name) < 0
}
I somehow always oversee these things when answering questions...

A bool function doesn't work in stable_sort function

The shipwreck;
Input a number, than a vector of type Passenger passengers(number), where Passenger is a struct that consists of string name and string status;
the problem is to sort the passengers who was on the ship by the next priority:
a) first who leaves the ship is rat, than the ship leaves whoman or a child, than the ship leaves a man, the last one who leaves the ship is the captain;
b) it is necessary to write a bool function which we will use in function stable_sort to sort the vector of passengers;
I tried this:
int Priority1(Passenger pas)
{
if(pas.status == "rat")
return 3;
if(pas.status == "woman" || pas.status == "child")
return 2;
if(pas.status == "man")
return 1;
if(pas.status == "captain")
return 0;
}
bool Priority(Passenger pas1, Passenger pas2)
{
return Priority1(pas1) > Priority1(pas2);
}
If your trying to implement an ordering function for one of the
standard library functions, your function is trivially wrong,
because it returns true if both passengers are rats. To
establish a proper ordering function, comparing any two entries
in the same equivalence class must return false.
Further down... what happens if both passengers are men? None
of your if are true, and you fall off the end, resulting in
undefined behavior. (FWIW: it's generally a bad practice to
throw return around right and left in the function. One
single return, as the last line in the function, and outside of
any control structure is a good general rule.)
Anyway, the approach I would take would be to to map both values
to an integral priority, and then return Priority(pas1) < Priority(pas2);. Much simpler, and guaranteed not to miss
any cases.
if(pas1.status == "man" && pas2.status != "woman" && pas2.status == "child" && pas2.status != "rat")
I think the third condition pas2.status == "child", should be pas2.status != "child"
Also, there is no default return statement in the function.

logical comparison operator

Here is my code for my logical comparison operator (==) overloaded. I use this to check if two strings are identical in size and content. It should return false otherwise.
bool MyString::operator==(const MyString& other)const
{
if(other.Size == this->Size)
{
for(int i = 0; i < this->Size+1; i++)
{
if(&other == this)
return true;
}
}
else
return false;
}
When I ran valgrind it told me warning control reaches end of non-void function. Any suggestions on how to fix this issue and what I could do to better the code?
When control reaches the end of your for loop, you immediately get to the end of the function without returning a value.
It looks to me like you have the logic in your for loop munged anyway -- it's comparing the address of the other item to this. While it's sort of okay to do that, you only need to do it once, not in a loop.
In the loop, you undoubtedly want to compare the characters in the string, not the addresses of the objects.
Edit:
A typical implementation would be something on this general order:
class MyString {
char *data;
size_t length;
public:
// ...
bool operator==(MyString const &other) const {
if (length != other.length)
return false;
for (int i=0; i<length; i++)
if (data[i] != other.data[i]) // If we see any inequality
return false; // they're not equal
return true; // all equal, so the strings are equal.
}
};
It's not too clear what determines equality if the sizes are equal, but
the loop suggests that you're looking for something like:
bool
MyString::operator==( MyString const& other ) const
{
return size == other.size && std::equals( ??? );
}
Well, first of all, if you enter the for loop, and the condition &other == this will not be met, you will never return anything. To fix this, you should just remove the else statement. This will cause your function to return false either if the other.Size == this->Size condition is not met, or if you've gone through the whole loop, and have not used return inside of it.
The second problem is the line if(&other == this). I believe that inside of the loop you intend to check all the symbols of the strings. But now you are only checking the pointer to the class itself. To check the characters, you will need to use something like if( other->data == this->data ), provided you have a data member in which you store the...data (sorry for tautology).
Another little flow is in the design. You see, to check that the strings are equal, you need to look through each and every character and check that they match. However, to prove the strings are not equal, you need to find just 1 pair of characters that does not match. After that, it is pointless to continue comparing. So it is better to changee your condition in the cycle to a negative one, in order to stop comparing immediately after you fuond a pair that does not match, and not to do useless comparations of other characters.
Generaly, it is a good practice to return all the errors as fast as it's possible and avoid unneeded cumputation. So if you can check something in the begining of your function with a simple check, better do it.
So, after all, you should have something like this:
bool MyString::operator==(const MyString& other)const
{
if(other.Size != this->Size)
return false;//If the sizes do not match, no need to check anything else. Just return false.
//If we are here, the sizes match. Lets check the characters.
for(int i = 0; i < this->Size+1; i++)
{
//If some pair doesnt match, the strings are not equal, and we exit.
if( other->data[i] != this->data[i])
return false;
}
//If we are here, all the characters did match, so we return true.
return true;
}
Just get rid of the else. This way there is a "default" behaviour returning false if the condition is not met. It's the functionality you intend, and the compiler or syntax checker won't complain.

Is this the right way to use recursion?

Given strings s and t compute recursively, if t is contained in s return true.
Example: bool find("Names Richard", "Richard") == true;
I have written the code below, but I'm not sure if its the right way to use recursion in C++; I just learned recursion today in class.
#include <iostream>
using namespace std;
bool find(string s, string t)
{
if (s.empty() || t.empty())
return false;
int find = static_cast<int>(s.find(t));
if (find > 0)
return true;
}
int main()
{
bool b = find("Mississippi", "sip");
string s;
if (b == 1) s = "true";
else
s = "false";
cout << s;
}
If anyone find an error in my code, please tell me so I can fix it or where I can learn/read more about this topic. I need to get ready for a test on recursion on this Wednesday.
The question has changed since I wrote my answer.
My comments are on the code that looked like this (and could recurse)...
#include <iostream>
using namespace std;
bool find(string s, string t)
{
if (s.empty() || t.empty())
return false;
string start = s.substr(0, 2);
if (start == t && find(s.substr(3), t));
return true;
}
int main()
{
bool b = find("Mississippi", "sip");
string s;
if (b == 1) s = "true";
else
s = "false";
cout << s;
}
Watch out for this:
if (start == t && find(s.substr(3), t));
return true;
This does not do what you think it does.
The ; at the end of the if-statement leaves an empty body. Your find() function will return true regardless of the outcome of that test.
I recommend you turn up the warning levels on your compiler to catch this kind of issue before you have to debug it.
As an aside, I find using braces around every code-block, even one-line blocks, helps me avoid this kind of mistake.
There are other errors in your code, too. Removing the magic numbers 2 and 3 from find() will encourage you to think about what they represent and point you on the right path.
How would you expect start == t && find(s.substr(3), t) to work? If you can express an algorithm in plain English (or your native tongue), you have a much higher chance of being able to express it in C++.
Additionally, I recommend adding test cases that should return false (such as find("satsuma", "onion")) to ensure that your code works as well as calls that should return true.
The last piece of advice is stylistic, laying your code out like this will make the boolean expression that you are testing more obvious without resorting to a temporary and comparing to 1:
int main()
{
std::string s;
if (find("Mississippi", "sip"))
{
s = "true";
}
else
{
s = "false";
}
std::cout << s << std::endl;
}
Good luck with your class!
Your recursive function needs 2 things:
Definite conditions of failure and success (may be more than 1)
a call of itself to process a simpler version of the problem (getting closer to the answer).
Here's a quick analysis:
bool find(string s, string t)
{
if (s.empty() || t.empty()) //definite condition of failure. Good
return false;
string start = s.substr(0, 2);
if (start == t && find(s.substr(3), t)); //mixed up definition of success and recursive call
return true;
}
Try this instead:
bool find(string s, string t)
{
if (s.empty() || t.empty()) //definite condition of failure. Done!
return false;
string start = s.substr(0, 2);
if (start == t) //definite condition of success. Done!
return true;
else
return find(s.substr(3), t) //simply the problem and return whatever it finds
}
You're on the right lines - so long as the function calls itself you can say that it's recursive - but even the most simple testing should tell you that your code doesn't work correctly. Change "sip" to "sipx", for example, and it still outputs true. Have you compiled and run this program? Have you tested it with various different inputs?
You are not using recursion. Using std::string::find in your function feels like cheating (this will most likely not earn points).
The only reasonable interpretation of the task is: Check if t is an infix of s without using loops or string functions.
Let's look at the trivial case: Epsilon (the empty word) is an infix of ever word, so if t.empty() holds, you must return true.
Otherwise you have two choices to make:
t might be a prefix of s which is simple to check using recursion; simply check if the first character of t equals the first character of s and call isPrefix with the remainder of the strings. If this returns true, you return true.
Otherwise you pop the first character of s (and not of t) and proceed recursively (calling find this time).
If you follow this recipe (which btw. is easier to implement with char const* than with std::string if you ask me) you get a recursive function that only uses conditionals and no library support.
Note: this is not at all the most efficient implementation, but you didn't ask for efficiency but for a recursive function.