I have a question:
Let's say there are two std::strings and I want to compare them, there is the option of using the compare() function of the string class but I also noticed that it is possible using simple < > != operators (both of the cases are possible even if I don't include the <string> library).
Can someone explain why the compare() function exists if a comparison can be made using simple operators?
btw I use Code::Blocks 13.12
here is an example of my code:
#include <iostream>
#include <string>
using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::getline;
int main()
{
string temp1, temp2;
cout << "Enter first word: ";
getline (cin,temp1);
cout << "Enter second word: ";
getline (cin,temp2);
cout << "First word: " << temp1 << endl << "Second word: " << temp2 << endl;
if (temp1 > temp2)
{
cout << "One" << endl;
}
if (temp1.compare(temp2) < 0)
{
cout << "Two" << endl;
}
return 0;
}
.compare() returns an integer, which is a measure of the difference between the two strings.
A return value of 0 indicates that the two strings compare as equal.
A positive value means that the compared string is longer, or the first non-matching character is greater.
A negative value means that the compared string is shorter, or the first non-matching character is lower.
operator== simply returns a boolean, indicating whether the strings are equal or not.
If you don't need the extra detail, you may as well just use ==.
string cat = "cat";
string human = "human";
cout << cat.compare(human) << endl;
This code will give -1 as a result. This is due to the first non-matching character of the compared string 'h' is lower or appears after 'c' in alphabetical order, even though the compared string, 'human' is longer than 'cat'.
I find the return value described in cplusplus.com is more accurate which are-:
0 : They compare equal
<0 : Either the value of the first character that does not match is lower in the compared string, or all compared characters match but the compared string is shorter.
more than 0 : Either the value of the first character that does not match is greater in the compared string, or all compared characters match but the compared string is longer.
Moreover, IMO cppreference.com's description is simpler and so far best describe to my own experience.
negative value if *this appears before the character sequence specified by the arguments, in lexicographical order
zero if both character sequences compare equivalent
positive value if *this appears after the character sequence specified by the arguments, in lexicographical order
Regarding the question,
” can someone explain why the compare() function exists if a comparison can be made using simple operands?
Relative to < and ==, the compare function is conceptually simpler and in practice it can be more efficient since it avoids two comparisons per item for ordinary ordering of items.
As an example of simplicity, for small integer values you can write a compare function like this:
auto compare( int a, int b ) -> int { return a - b; }
which is highly efficient.
Now for a structure
struct Foo
{
int a;
int b;
int c;
};
auto compare( Foo const& x, Foo const& y )
-> int
{
if( int const r = compare( x.a, y.a ) ) { return r; }
if( int const r = compare( x.b, y.b ) ) { return r; }
return compare( x.c, y.c );
}
Trying to express this lexicographic compare directly in terms of < you wind up with horrendous complexity and inefficiency, relatively speaking.
With C++11, for the simplicity alone ordinary less-than comparison based lexicographic compare can be very simply implemented in terms of tuple comparison.
Related
I am getting few problems when reading code. This is the text file.
2X^6+3X^3+4X^0=0
5X^6+X^2+X^1-4X^0=0
I am getting a proper input for the first line but in second line first I need to ignore. I searched here and found how to use it and it's work to get to next line ignoring all the left over characters of first line.
You can see in second line with X there is no integer, now problem is the second while loop is running continuously. If I add 1 in text file with the X the file reads perfectly. Also how can I put a condition to satisfy this that when there is directly X or -X, it should store 1 or -1 and goes to next character? Also you can see ^ I have to store this in a variable whereas I should ignore it but didn't how to ignore it?
Thanks in advance
int main()
{
int in;
int power;
char x;
char f;
fstream fin;
fin.open("input1.txt");
list l1,l2;
while(fin.peek() != 61)
{
fin>>in;
fin>>x;
fin>>f;
fin>>power;
cout<<in<<endl<<x<<endl<<f<<endl<<power<<endl;
l1.addtoend(in,power,x);
cout<<endl;
}
fin.ignore(2,'\n');
while(fin.peek() != 61)
{
fin>>in;
fin>>x;
fin>>f;
fin>>power;
cout<<in<<endl<<x<<endl<<f<<endl<<power<<endl;
l2.addtoend(in,power,x);
cout<<endl;
}
l1.display();
l2.display();
}
Unfortunately, this is not so simple as expected.
We need to split up the task into smaller parts.
What you want to do, is splitting your equation in terms and extract from this the coefficients and exponents.
Splitting up something in similar parts is also called tokenizing.
So, your equation consists of terms, which all follow the same pattern. First an optional sign, followed by the coefficients, then a “X^”, and, at the end the exponent (which may or may not have a sign).
And since all terms have the same pattern, we can find them with a so-called regex. C++ supports this functionality. Also for splitting up a text in smaller tokens/terms/pattern-matches, we have a special iterator std::sregex_token_iterator. Like any other iterator in C++, it iterates over the source string and extracts (and copies) all matched patterns.
OK, then we found already a solution for the first sub task. Extract all terms and put them into a std::vector. We will use the std::vectors range constructor, to do this, while defining the variable.
The next step is to get the coefficient. Here we need some special handling, because the coefficient can be omitted with an assumed 1. Using this assumption, we will read the term and convert the coefficient to an integer. And because we want to do that in one statement, we use std::transform from the STL’s algorithm library.
Getting the exponents is easier. We simply convert anything in a term following the ‘^’-sign to an integer. We again use std::transform to work on all terms in one statement.
Last but not least, we will get the right-hand-side of the equation and convert it also to an integer.
Please note:
All this can be done also with float type values
We could also allow spaces in the equation
For that, we would simple modify the std::regex-string.
Please see the complete example below:
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <regex>
#include <algorithm>
#include <iomanip>
int main() {
std::string equation{ "5X^6+X^2+X^1-4X^0=0" };
const std::regex re(R"(([+-]?\d?X\^[+-]?\d+))");
std::vector<std::string> terms{ std::sregex_token_iterator(equation.begin(), equation.end(), re,1),std::sregex_token_iterator() };
std::vector<int> coefficients(terms.size());
std::vector<int> exponents(terms.size());
int rightHandSite{ 0 };
// Everything in front of X is the coefficient. Handle special case, when no digit is given
std::transform(terms.begin(), terms.end(), coefficients.begin(), [](const std::string& s) {
std::string temp = s.substr(0U, s.find('X'));
if (1 == temp.size() && !std::isdigit(temp[0])) temp += '1';
return std::stoi(temp); });
// Get all exponents
std::transform(terms.begin(), terms.end(), exponents.begin(), [](const std::string & s) {
return std::stoi(s.substr(s.find('^') + 1)); });
// Get right Hand site of equation
rightHandSite = std::stoi(equation.substr(equation.find('=') + 1));
// Show result
std::cout << "\nEquation: " << equation << "\n\nFound '" << terms.size() << "' terms.\n\nCoeffient Exponent\n";
for (size_t i = 0U; i < terms.size(); ++i)
std::cout << std::right << std::setw(9) << coefficients[i] << std::setw(10) << exponents[i] << "\n";
std::cout << "\n --> " << rightHandSite << "\n";
return 0;
}
There are many other possible solutions. But maybe it will give you some idea on what you could do.
How to perform range checking using regular expressions?
Take a 4-bit number (i.e. "dddd") as an example, how can I check whether it is within given range, say [1256-4350] or not?
To check whether the input is a 4 digit number use regex_match, and then convert the string to an integer using std::stoi to check the range.
std::regex expr(R"(\d{4})");
if(std::regex_match(input, expr)) {
int num = std::stoi(input);
if(num >= 1256 && num <= 4350) {
// input is within range
}
}
As Jarod42 mentions in the comments, since you've already validated the input is a 4 digit number, it's not necessary to convert it to an integer. Assuming input is an std::string, this would work too
if(input >= "1256" && input <= "4350") {
// input is within range
}
Using this website, the regex you are after should look like this: ^(125[6-9]|12[6-9][0-9]|1[3-9][0-9]{2}|[23][0-9]{3}|4[0-2][0-9]{2}|43[0-4][0-9]|4350)$.
That being said, I think it is far more readable and maintainable do break it into two steps, first validate the data type and then the range. (What happens when the range shifts? Your entire regex will most likely be made useless).
Here is a great site that will give you the answer.
For your example:
(\b(125[6-9]|12[6-9][0-9]|1[3-9][0-9]{2}|[23][0-9]{3}|4[0-2][0-9]{2}|43[0-4][0-9]|4350)\b
Yeah, I know this will work. I just want to check if we can verify it's indeed a number and at the same time check its range using
Okay... But don't use regex for this task. It's a terrible choice.
#include <iostream>
#include <sstream>
using namespace std;
bool is_number_in_range(string s) {
istringstream str(s);
int i; char c;
str >> i;
if( str.fail() ) return false;
return i>=1256 && i<=4350 && str.eof();
}
int main() {
cout<< is_number_in_range("0") << '\n'<<
is_number_in_range("1234") << '\n'<<
is_number_in_range("1256") << '\n'<<
is_number_in_range("2000") << '\n'<<
is_number_in_range("4350") << '\n'<<
is_number_in_range("5000") << '\n'<<
is_number_in_range("abcd") << '\n'<<
is_number_in_range("1234.0") << '\n';
return 0;
}
see it live
I have a string as "1.0.0" and I want to extract the "1", "0", and "0". If the last zero is not present, the string must store 0 by default:
verstr.substr(0,verstr.find(".");
The above statement can find the first digit that is "1", however, I am not able to think of a solution for extracting the remainder of the string.
After this i convert it to a long as:
va = atol(verstr.substr(0,verstr.find(".")).c_str());
so i want the "1" in va , 0 in "vb" and so on
Thanks.
C++11 solution:
#include <iostream>
#include <string>
#include <regex>
using namespace std;
int main(int, char **) {
string version("1.2.3");
match_results<string::const_iterator> m;
regex re("([0-9]+)\\.([0-9]+)(\\.([0-9]+))?");
if (regex_match(version, m, re)) {
int major = stoi(m[1].str()),
minor = stoi(m[2].str()),
rev = stoi(m[4].str().length() == 0 ? 0 : m[4].str());
cout << "major: " << major << endl;
cout << "minor: " << minor << endl;
cout << "rev: " << rev << endl;
} else {
cout << "no match\n";
}
}
The regular expression used is ([0-9]+)\.([0-9]+)(\.([0-9]+))? and breaks down as follows:
[0-9]+ matches one or more digits
\. matches a literal dot.
? following the last expression indicates that it is optional
Expressions wrapped in ( and ) are capture groups. There are five capture groups in this expression:
0 - always matches the entire string - we don't use this.
1 - matches the major version number.
2 - matches the minor version number.
3 - matches a dot followed by the revision number - we don't use this but it is necessary because we use the parentheses followed by a ? to make this whole group optional.
4 - matches the revision number.
Not sure if I understand what you need, if you want to retrieve the digits as strings, with a minimum of x digits, you can do something like this.
vector<string> GetVersion(const string &strInput, int iMinSize)
{
vector<string> vRetValue;
std::stringstream ss(strInput);
string strItem;
while(std::getline(ss, strItem, '.'))
vRetValue.push_back(strItem);
while(vRetValue.size() < iMinSize)
vRetValue.push_back("0");
return vRetValue;
}
int _tmain(int argc, _TCHAR* argv[])
{
vector<string> vRetValue = GetVersion("1.0", 3);
return 0;
}
A possibility would to use std::sscanf(). It is simple to use and provides a level of error checking with relatively few lines of code:
#include <iostream>
#include <string>
#include <cstdio>
int main()
{
std::string input[] = { "1.0.7", "1.0.", "1.0", "1.", "1" };
for (size_t i = 0; i < sizeof(input)/sizeof(input[0]); i++)
{
std::cout << input[i] << ": ";
// Init to zero.
int parts[3] = { 0 };
// sscanf() returns number of assignments made.
if (std::sscanf(input[i].c_str(),
"%d.%d.%d",
&parts[0],
&parts[1],
&parts[2]) >= 2)
{
// OK, the string contained at least two digits.
std::cout << parts[0]
<< ","
<< parts[1]
<< ","
<< parts[2]
<< "\n";
}
else
{
std::cout << "bad format\n";
}
}
return 0;
}
Output:
1.0.7: 1,0,7
1.0.: 1,0,0
1.0: 1,0,0
1.: bad format
1: bad format
See online demo: http://ideone.com/0Ox9b .
find and substr are two really nice family of function overloads that are pretty well suited to many simple parsing problems, especially when your syntax checking only needs to be loose.
To extract multiple scalars out of your version vector, store the found index somewhere:
const auto a = verstr.find('.');
const std::string major = verstr.substr(0, a);
Then re-use it with one of the overloads of string::find, saying start searching at one after a:
const auto b = verstr.find ('.', a+1);
const std::string minor = verstr.substr(a+1, b);
And so forth.
If you need a syntax check, compare the returned indices against string::npos:
const auto a = verstr.find('.');
if (std::string::npos == a)
.... bad syntax ....
Pastebin style version of this answer:
#include <string>
#include <stdexcept>
#include <iostream>
struct Version
{
std::string Major, Minor, Patch;
Version(std::string const &Major)
: Major(Major), Minor("0"), Patch("0")
{}
Version(std::string const &Major, std::string const &Minor)
: Major(Major), Minor(Minor), Patch("0")
{}
Version(std::string const &Major, std::string const &Minor, std::string const &Patch)
: Major(Major), Minor(Minor), Patch(Patch)
{}
};
std::ostream& operator<< (std::ostream &os, Version const &v)
{
return os << v.Major << '.' << v.Minor << '.' << v.Patch;
}
Version parse (std::string const &verstr) {
if (verstr.empty()) throw std::invalid_argument("bad syntax");
const auto first_dot = verstr.find('.');
if (first_dot == std::string::npos)
return Version(verstr);
const auto second_dot = verstr.find('.', first_dot+1);
if (second_dot == std::string::npos)
return Version(verstr.substr(0, first_dot),
verstr.substr(first_dot+1, second_dot));
return Version(verstr.substr(0, first_dot),
verstr.substr(first_dot+1, second_dot),
verstr.substr(second_dot+1));
}
and then
int main () {
std::cout << parse("1.0") << '\n'
<< parse("1.0.4+Patches(55,322)") << '\n'
<< parse("1") << '\n';
parse(""); // expected to throw
}
try something like this instead of solution below the line
string s = "1.0.0";
string delimiters = ".";
size_t current;
size_t next = -1;
do
{
current = next + 1;
next = s.find_first_of( delimiters, current );
string current_substring = s.substr( current, next - current ); // here you have the substring
}
while (next != string::npos);
Ok, please don't use this solution below, if you really don't know what you're doing, according to discussion below this answer with #DavidSchwartz
Take a look at function strtok http://www.cplusplus.com/reference/clibrary/cstring/strtok/
char str[] = "1.0.0";
char * pch;
pch = strtok (str,".");
while (pch != NULL)
{
printf ("%s\n",pch);
pch = strtok (NULL, ".");
}
Take a look at Boost libraries, specifically String Algo.
Standard library support for string manipulation is somewhat limited in C++. And reinventing the wheel is just plain bad.
Update:
I was asked in comments why I consider all find/substr based solutions bad style.
I'll try my best.
As questions does not states otherwise, performance is not a question here. Maintainability and readability are much more important. All solutions proposed here tightly tie split algorithm semantics with a specific version parsing algorithm semantics. This hurts both.
This hurts maintainability, because when you will need to change version format, it will involve changing the very same block of code that implements splitting, making it more error-prone. Same applies to unit-tests.
This hurts readability, because due to mixed semantics I can't at once guess an intent behind this block of code. For example, when I am looking up parse algorithm to check how missing 3d version argument is handled, I'd better not waste my time digging through split implementation details.
If parsing pattern would have been slightly more difficult, I'd have advised regular expressions. But in this case splitting string by a delimiter is an action generic and often used enough to justify having it as a separate function.
if it's only simple char comparison in a small string...
char[] should not be so bad... and c functions should work... (EDIT: for some, its a blasphemy... a lot of C++ method use char* whether it's const or not).
why use an object if it's to has the same functionality with more memory to be used, and more time for the process to spend?
EDIT:
I saw that some answer suppose to create a lot of string object... i don't khnow if it's really the best way...
a little 2 line recursive C like function can do that without gasping a lot.
In c++ code I probably would do that with string object, as it's negligible gasp... but just to say it so.
In string object i would use the length property to get the last char first (with [] operator, or appropriate method).
then just need to get the two elements (in a loop, or with 2 back reference in an object accepting regex (which is less efficient))
I know there's an "isspace" function that checks for spaces, but that would require me to iterate through every character in the string, which can be bad on performance since this would be called a lot. Is there a fast way to check if a std::string contains only spaces?
ex:
function(" ") // returns true
function(" 4 ") // returns false
One solution I've thought of is to use regex, then i'll know that it only contains whitespace if it's false... but i'm not sure if this would be more efficient than the isspace function.
regex: [\w\W] //checks for any word character(a,b,c..) and non-word character([,],..)
thanks in advance!
With a regular string, the best you can do will be of the form:
return string::find_first_not_of("\t\n ") == string::npos;
This will be O(n) in the worst case, but without knowing else about the string, this will be the best you can do.
Any method would, of necessity, need to look at each character of the string. A loop that calls isspace() on each character is pretty efficient. If isspace() is inlined by the compiler, then this would be darn near optimal.
The loop should, of course, abort as soon as a non-space character is seen.
You are making the assumption regex doesnt iterate over the string. Regex is probably much heavier than a linear search since it might build a FSM and traverse based on that.
The only way you could speed it up further and make it a near-constant time operation is to amortize the cost by iterating on every update to the string and caching a bool/bit that tracks if there is a space-like character, returning that value if no changes have been made since, and updating that bit whenever you do a write operation to that string. However, this sacrifices/slows that speed of modifying operations in order to increase the speed of your custom has_space().
For what it's worth, a locale has a function (scan_is) to do things like this:
#include <locale>
#include <iostream>
#include <iomanip>
int main() {
std::string inputs[] = {
"all lower",
"including a space"
};
std::locale loc(std::locale::classic());
std::ctype_base::mask m = std::ctype_base::space;
for (int i=0; i<2; i++) {
char const *pos;
char const *b = &*inputs[i].begin();
char const *e = &*inputs[i].end();
std::cout << "Input: " << std::setw(20) << inputs[i] << ":\t";
if ((pos=std::use_facet<std::ctype<char> >(loc).scan_is(m, b, e)) == e)
std::cout << "No space character\n";
else
std::cout << "First space character at position " << pos - b << "\n";
}
return 0;
}
It's probably open to (a lot of) question whether this gives much (if any) real advantage over using isspace in a loop (or using std::find_if).
You can also use find_first_not_of if you all the characters to be in a given list.
Then you can avoid loops.
Here is an example
#include <string>
#include <algorithm>
using namespace std;
int main()
{
string str1=" ";
string str2=" u ";
bool ContainsNotBlank1=(str1.find_first_not_of("\t\n ")==string::npos);
bool ContainsNotBlank2=(str2.find_first_not_of("\t\n ")==string::npos);
bool ContainsNotBlank3=(str2.find_first_not_of("\t\n u")==string::npos);
cout << ContainsNotBlank1 <<endl;
cout << ContainsNotBlank2 <<endl;
cout << ContainsNotBlank3 <<endl;
return 0;
}
Output:
1: because only blanks and a tab
0: because u is not into the list "\t\n "
1: because str2 contains blanks, tabs and a u.
Hope it helps
Tell me if you have any questions
I'm working on a program that takes elements from a user and sorts them. For this program, I have to use a vector as the size of the element list is unknown prior to user input. Our instructions were:
Write a program in C++ to implement sorting of a list of elements.
Elements can be of any type but will all be of same type, like all
integers or all floats or all chars or all strings (strings shall be
sorted like in a dictionary). You can implement any sorting algorithm
of your choice.
Ask the user how many elements will be there
Ask the user to input elements
Ask the user to choose the sorting order: ascending or descending or both
Print both input and output lists
User will not provide any information regarding the type of elements
I'm not very familiar with vectors (teacher basically skimmed topic in class) and my book isn't giving me a whole lot of information on the subject. The problem I'm running into is that I don't know the type of the element list until the user begins input. So far, I have tried:
creating a void type vector (obviously not allowed now that I've researched it, oops)
overloading a function called insertInVector by sending the first element to the function and letting the function determine which vector type to create based on the type of the first element (which seemed like my best option when I thought of it, except I need access to the vector after the function terminates, so that ended up being a no go, too)
#include <typeinfo> in program, finding the type of the first element, and then
creating a vector using vector<typeid(firstElement).name()> and honestly I'm not
sure why that didn't work, but it didn't.
Like I said I have EXTREMELY limited experience with vectors as this is my first time using them. I'm also a fairly new programmer so a lot of the research I've done on this has gone over my head. Any help that could be given in this would be GREATLY appreciated!
C++ is a language statically typed. It means that all the types should be determined during compilation: you cannot introduce new types when running the program.
creating a void type vector (obviously not allowed now that I've researched it, oops)
void is actually quite a strange type, mostly a placeholder for when you would expect a type (like a function return type) and have none to provide. void* is used as a pointer to an unknown type (mostly in C) but this is quite a hack, because the information about the original is discarded (as far as the language is concerned) so this causes issues to actually do things with the value so obtained.
overloading a function called insertInVector by sending the first element to the function and letting the function determine which vector type to create based on the type of the first element
#include <typeinfo> in program, finding the type of the first element, and then creating a vector using vector<typeid(firstElement).name()> and honestly I'm not sure why that didn't work, but it didn't.
Unfortunately neither is possible: since you cannot declare a variable without a type, what would be the type of firstElement to begin with ?
The problem you are describing is difficult in general. Basically it means that you will have to accept a string of characters, and then code a set of rules to determine how to interpret those characters. This is done generically by using a grammar to encode those rules; but grammars might way complicated for what is probably a simple task.
Let me put together a small example:
class Input {
public:
enum Type {
Int,
Double,
String
};
static Input Parse(std::string const& s);
Input(): _type(Int), _int(0), _double(0.0) {} // need to define a default...
Type type() const { return _type; }
int asInt() const {
assert(_type == Int && "not an int");
return _int;
}
double asDouble() const {
assert(_type == Double && "not a double");
return _double;
}
std::string const& asString() const {
assert(_type == String && "not a string");
return _string;
}
private:
Type _type;
int _int;
double _double;
std::string _string;
};
Obviously, the real challenge is to correctly Parse the input.
The idea is to use a set of rules, for example:
an int is composed exclusively of digits, optionally prefixed by -
a double is composed exclusively of digits, with at most one . and optionally prefixed by -
a string can be anything, therefore is our catch-all
Then we can write the recognition part of the Parse method:
static bool isInt(std::string const& s) {
if (s.empty()) { return false; }
// The first character may be among digits and '-'
char const first = s.at(0);
if (not isdigit(first) and first != '-') { return false; }
// Subsequent characters may only be digits
for (char c: s.substr(1)) {
if (not isdigit(c)) { return false; }
}
// Looks like it is an int :)
return true;
} // isInt
// Note: any int could be interpreted as a double too
static bool maybeDouble(std::string const& s) {
if (s.empty()) { return false; }
// The first character may be among digits, '.' and '-'
char const first = s.at(0);
if (not isdigit(first) and first != '.' and first != '-') { return false; }
// There may only be one dot
bool hasSeenDot = s.at(0) == '.';
// Subsequent characters may only be digits and a dot now
for (char c: s.substr(1)) {
if (not isdigit(c) and c != '.') { return false; }
if (c == '.') {
if (hasSeenDot) { return false; } // no second dot allowed
hasSeenDot = true;
}
}
// Looks like it could be a double
return true;
} // maybeDouble
static Input::Type guessType(std::string const& s) {
if (isInt(s)) { return Input::Int; }
// Test double after we ensured it was not an int
if (maybeDouble(s)) { return Input::Double; }
return Input::String;
} // guessType
And with the guessing logic together, finally the parse comes:
Input Input::Parse(std::string const& s) {
Input result;
result._type = guessType(s);
switch(result._type) {
case Input::Int: {
std::istringstream stream(s);
s >> result._int;
return result;
}
case Input::Double: {
std::istringstream stream(s);
s >> result._double;
return result;
}
case Input::String:
result._string = s;
return result;
}
// Unreachable (normally)
abort();
} // Input::Parse
Phew!
So ? Almost there. Now we need to determine how to compare two inputs. It's easy if they all have the same type, if not you will need to determine an arbitrary logic. You can transform an input Int in an input Double easily enough, but for string it's a bit weirder.
// define < for comparing two instance of "Input",
// assuming they both have the same type
bool operator<(Input const& left, Input const& right) {
assert(left.type() == right.type() && "Different Types!");
switch(left.type()) {
case Input::Int: return left.asInt() < right.asInt();
case Input::Double: return left.asDouble() < right.asDouble();
case Input::String: return left.asString() < right.asString();
}
} // operator<
And finally, the program:
int main(int argc, char* argv[]) {
// parse command line
std::vector<Input> inputs;
// by convention argv[0] is the program name, it does not count!
for (int i = 1; i != argc; ++i) {
inputs.push_back(Input::Parse(argv[i]));
// Detect that the type is the same as the first input
if (inputs.size() >= 2) {
if (inputs.back().type() != inputs.front().type()) {
std::cerr << "Please only use one type among Int, Double and String\n";
return 1; // non-0 is an error
}
}
}
// sort
std::sort(inputs.begin(), inputs.end());
// echo back to the user
for (Input const& i: inputs) {
switch(i.type()) {
case Input::Int: std::cout << i.asInt() << "\n"; break;
case Input::Double: std::cout << i.asDouble() << "\n"; break;
case Input::String: std::cout << i.asString() << "\n"; break;
}
}
// End of the program
return 0;
}
Of course as I don't know the types you wish to deal with.. I've decided an arbitrary set ;) However this should give you a skeleton to base yourself on.
Looking at the actual requirements of the problem as stated in the comments, I suggest you store all the inputs in an std::vector<std::string> and sort the vector using std::sort. So, instead of worrying about different types, you can specify the sorting logic depending on what you interpret the strings in your vector to represent. So
Implement sorting functions for strings depending on what the strings represent (more later)
store inputs as strings in a vector.
Determine which type the strings represent
select the sorting function based on this type
Sort the vector using std::sort and the appropriate sort function.
Concerning the sorting function, std::sort accepts a binary functor or function that applies a "less-than" comparison to two elelemts, so your functors or functions should look something like
bool foo(const std::string& rhs, const std::string& lhs) {
// implement the logic
}
Edit: Looking at more recent comments, it seems that the main purpose if the exercise might have been to implement sorting algorithms for different types. In that case, I would suggest following the approach taken by the C++ standard library, that is, to implement sorting in terms or a less-than comparison between two types, thereby decoupling the sorting logic from the types to be sorted. So you would want a template sorting function, templated on iterator type and comparison function/functor.
If you know what are the types the user may input, you can use templates and inheritance:
class Generic {
public:
virtual void process_input() = 0; // Handles the next input from user
virtual void process_output() = 0; // Processes the data inserted
};
template <typename T>
class HandleInput : public Generic {
private:
std::vector<T> storage;
public:
HandleInput(T first)
{
storage.push_back(first);
}
void process_input()
{
// do whatever you want
}
void process_output()
{
// do whatever you want
}
};
int main(int argc, char **argv)
{
// Get first input
Input i = input();
Generic *g;
// Instantiate the "right" generic with a switch
switch (i.type) {
case T:
g = new HandleInput<T>(i.value);
}
// Use Generic from here onwards
}
That's just an idea (Input cannot be implemented like that, you need to change that part with the logic that gets something from the user and determines its type), but it has the benefit of masking the type into a generic class, so you can factor your code around the interface provided by Generic.
Another idea (easier, probably) is using a std::vector<void*> and an enum that tells you what the type of the data stored in the vector is. When you need to process that data somewhere in the future, you can switch on the enum to appropriately cast the vector elements to the correct type and dispatch them to the appropriate code.
EDIT: another idea is to define a templatized function that takes the input and sorts the array using standard comparators:
#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/lexical_cast.hpp>
template <typename T>
void print_v(std::vector<T> &v)
{
typename std::vector<T>::iterator it;
for (it = v.begin(); it != v.end(); it++)
std::cout << *it << " ";
std::cout << std::endl;
}
template <typename T>
void sort_and_print(T first, size_t n, bool asc)
{
std::vector<T> v;
v.push_back(first);
for (size_t i = 0; i < n; i++) {
std::string s;
std::cin >> s;
T e = boost::lexical_cast<T>(s);
v.push_back(e);
}
print_v(v);
if (asc)
std::sort(v.begin(), v.end(), std::greater<T>());
else
std::sort(v.begin(), v.end());
print_v(v);
}
int main(int argc, char **argv)
{
std::string s = "test";
sort_and_print(s, 2, true);
unsigned int j = 3;
sort_and_print(j, 2, true);
return 0;
}
The logic to determine the type of the first input is up to you (maybe you can open another question) ;)
There are two aspects to this question: parsing & sorting.
You can use regular expressions to check the user-input data-types.
You can use cin to parse the data.
First: realise that you cannot necessarily know the type of your
users input until you have received all of it
~eg: consider a list of user names :
728278243
390349346
495045594
elizabeth
Hence, best not to assume to know best about the incoming data (can lead to a frustrating user-experience) but instead, prefer to treat everything as potentially a string. Store all raw input as strings so you can output in same format as input.
you can use say, an enumerated type to switch inside a sort comparator
or consider using a mutliset/multimap. here you will be
building an ordered set. so there is no need to sort.
NB: the complexity for constructing an ordered set of N elements or, for a single sort on N unsorted list elements, is roughly equivalent ~> NlogN
For your task-in-hand, it hardly matters but in reality depending upon upon how the list is used, one or other approach will be far more appropriate in performance terms.
If you have already used the likes of std::vector then std::multimap shouldn't be too scary. Loosely, it is an associated array of key-value pairs. the multi here meaning it can store multiple elements with the same key (which here, you want).
In this example i am using the boost regex library in order to determine some funky input data-types.
(eg: sudo apt-get install libboost-regex1.46-dev)
This regex might seem arcane but there are many examples on the i/web for practically every conceivable pattern.
[NB: C++11 regex is pretty-much a drop-in replacement for boost regex. ie: boost regex should be forward-compatible with the emerging C++11 standard]
blah.cpp:
#include <iostream>
#include <sstream>
#include <string>
#include <list>
#include <map>
#include <set>
#include <boost/regex.hpp>
//NB: GNU gcc added *experimental support for regular expressions in TR1 v 4.3.0.
// compile with: -std=c++0x
using namespace std;
using namespace boost;
//some example input data-types (perhaps notably missing a date!)
const regex re_char("[^0-9]", regex_constants::extended); //non numeric chars
const regex re_digit("[[:digit:]]+", regex_constants::extended); //a string of only digits in range [0..9] ~ie: Z+
const regex re_xdigit("0[xX][[:xdigit:]]+", regex_constants::extended); //support hex iff starts with '0x' or '0X'
const regex re_float("[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?", regex_constants::extended); //all kinds of numbers
int main(int argc, char** argv)
{
int i, countc=0;
double d;
string str;
int element_count;
do
{
cout << "how many elements will there be? ";
if (cin >> element_count) break;
cin.clear();
cin >> str;
cout << "\033[A\033[2K" << flush;
}
while(13);
cin.ignore(128,'\n');
multimap<double, string> list_num;
multimap<double, string> list_fp;
//NB: below, by way of example, construction using the 'greater<int>' comparison class achieves _descending_ order
multimap<int, string, greater<int> > list_int;
list<string> list_str;
for (int next=0; next < element_count; next++)
{
cout << "\033[A\033[2K" << flush;
cout << "enter next element in list ["<< next+1 << "/" << element_count << "] : ";
getline (cin,str);
if (regex_match(str, re_xdigit))
{
//see all about manipulators here:
//http://www.cplusplus.com/reference/iostream/istream/operator%3E%3E/
stringstream(str) >> hex >> i;
list_int.insert(pair<int, string>(i, str));
list_num.insert(pair<double, string>(i, str));
}
else if (regex_match(str, re_digit))
{
stringstream(str) >> i;
list_int.insert(pair<int, string>(i, str));
list_num.insert(pair<double, string>(i, str));
}
else if (regex_match(str, re_float))
{
stringstream(str) >> d;
list_fp.insert(pair<double, string>(d, str));
list_num.insert(pair<double, string>(d, str));
}
if (regex_match(str, re_char)) countc++;
list_str.push_back(str);
}
cout << "\033[A\033[2K" << flush;
cout << "input: unsorted list:" << endl;
for (list<string>::iterator it=list_str.begin(); it!=list_str.end(); it++)
cout << *it << endl;
if (list_int.size() == element_count)
{
cout << endl << "output: sorted list of Z+ types:" << endl;
for (multimap<int, string>::iterator it=list_int.begin() ; it != list_int.end(); it++ )
cout << (*it).second << endl;
}
else if (list_fp.size() == element_count)
{
cout << endl << "output: sorted list of fp types:" << endl;
for (multimap<double, string>::iterator it=list_fp.begin() ; it != list_fp.end(); it++ )
cout << (*it).second << endl;
}
else if (list_num.size() == element_count)
{
cout << endl << "output: sorted list of numeric types:" << endl;
for (multimap<double, string>::iterator it=list_num.begin() ; it != list_num.end(); it++ )
cout << (*it).second << endl;
}
else //output as sorted strings ~but in _descending_ order, using reverse iterator, by way of example
{
list_str.sort(); //but best to use list_str.sort(greater<string>()); with forward iterators
cout << endl << "output: sorted list of " << (countc == element_count ? "non numeric char" : "string") << " types:" << endl;
for (list<string>::reverse_iterator it=list_str.rbegin(); it!=list_str.rend(); ++it)
cout << *it << endl;
}
return 0;
}
Example was compiled & run on Ubuntu. Commandline stuff:
$
$ lsb_release -d
Description: Ubuntu 11.10
$ g++ --version
g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1
$ g++ --pedantic -oblah blah.cpp -lboost_regex
$ ./blah
input: unsorted list:
4.77
2.0e+2
-.3
11
0x10
output: sorted list of numeric types:
-.3
4.77
11
0x10
2.0e+2
$
NB: This is example code:
There are many optimisations that can be made here. You clearly
don't need so many stl containers as i am using.
I do not strictly deal with the direction of the sort (but show a couple of ways it may be achieved).
It might also be nice to encapsulate the type-specific functionality
in C++ objects; have a base class & then derived classes for each type you wish
to support ~but this homework right? -so probably not worth going over-board then
;)