I am trying the lower_bound function in C++.
Used it multiple times for 1 d datatypes.
Now, I am trying it on a sorted array dict[5000][20] to find strings of size <=20.
The string to be matched is in str.
bool recurseSerialNum(char *name,int s,int l,char (*keypad)[3],string str,char (*dict)[20],int
dictlen)
{
char (*idx)[20]= lower_bound(&dict[0],&dict[0]+dictlen,str.c_str());
int tmp=idx-dict;
if(tmp!=dictlen)
printf("%s\n",*idx);
}
As per http://www.cplusplus.com/reference/algorithm/lower_bound/?kw=lower_bound , this function is supposed to return the index of 'last'(beyond end) in case no match is found i.e. tmp should be equal dictlen.
In my case, it always returns the beginning index i.e. I get tmp equal to 0 both 1. When passed a string that is there in the dict and 2. When passed a string that is not there in the dict.
I think the issue is in handling and passing of the pointer. The default comparator should be available for this case as is available in case of vector. I also tried passing an explicit one, to no avail.
I tried this comparator -
bool compStr(const char *a, const char *b){
return strcmp(a,b)<0;
}
I know the ALTERNATE is to used vector ,etc, but I would like to know the issue in this one.
Searched on this over google as well as SO, but did not find anything similar.
There are two misunderstandings here, I believe.
std::lower_bound does not check if an element is part of a sorted range. Instead it finds the leftmost place where an element could be inserted without breaking the ordering.
You're not comparing the contents of the strings but their memory addresses.
It is true that dict in your case is a sorted range in that the sense that the memory addresses of the inner arrays are ascending. Where in relation to this str.c_str() lies is, of course, undefined. In practice, dict is a stack object, you will often find that the memory range for the heap (where str.c_str() invariably lies) is below that of the stack, in which case lower_bound quite correctly tells you that if you wanted to insert this address into the sorted range of addresses as which you interpret dict, you'd have to do so at the beginning.
For a solution, since there is an operator<(char const *, std::string const &), you could simply write
char (*idx)[20] = lower_bound(&dict[0], &dict[0] + dictlen, str);
...but are you perhaps really looking for std::find?
Related
I've been working on a program where I need to be able to sum rows in a two-dimensional array whose number of columns are variables. I should also add that the rows are "split" into two parts (part A, and part B) whose sizes depend on user input.
I can obviously sum a row just using a for loop, but I wanted a more elegant solution that would also be easier to set up across the whole program. I stumbled across the accumulate function out of the numeric library, but all examples that I was able to find were exclusively for one-dimensional arrays.
Here's a sample of my problem code:
total = partNum[PART_A] + partNum[PART_B];
partStart[PART_A] = 0;
partEnd[FUNC_A] = partNum[PART_A];
partStart[PART_B] = partNum[PART_A];
partEnd[FUNC_B] = total;
double stat[5][total];
double mass_sum = 0.0
func = PART_A;
accumulate(stat[MASS][partStart[func]], stat[MASS][partStart[func]], mass_sum);
However, I get a buildtime error which states that:
Indirection requires pointer operand ('double' invalid')
I assume this is a syntax error, but changing how I defined the array's start and end did nothing to fix the error.
The two first argument of accumulate are iterators that the function will use to iterate over the range, but you are passing actual element of the array
Iterator in C++ is a concept that requires certain operations to be valid on your object, as defined per the standard. For instance, pointer types usually match the LegacyRandomAccessIterator, meaning that you can basically use them to as array-like object (you can increment them with ++, you can indirect them with *, you can access an element at position i with [], etc.). I won't go into full details about what are iterators in C++ because it's a whole topic and you can find plenty of references, in particular on the two links I provided.
Back to your problem, what you want is to give accumulate iterator to the beginning and the end of your row, or the beginning and the end of your subranges. There are two ways to do this:
Take the address of the element stat[MASS][partStart[func]], i.e., &stat[MASS][partStart[func]], which will give you the beginning of the range, and then &stat[MASS][partEnd[func]] will give you the end of the range. This works because stat is as double stat[5][total] and the access operator ([]) gives you a reference (a double&), that you can take the address of, and the element on the row are contiguous in memory (that would not work for a column).
Use stat[MASS] + partStart[func] and stat[MASS] + partEnd[func]. In this case, you take the beginning of the row (stat[MASS]), which is (or is implicitly convertible to) a pointer to double (double*) and you increment that pointer by partStart[func] or partEnd[func], giving you the addresses of the elements you want in the row.
So basically:
std::accumulate(&stat[MASS][partStart[func]], &stat[MASS][partEndfunc]], mass_sum);
// or
std::accumulate(stat[MASS] + partStart[func], stat[MASS] + partEnd[func], mass_sum);
I am a C++ beginner. I want to understand how is this function operating on a string to sort it?
int SA_cmp(const void *a, const void *b) { return strcmp(S + *(int*)a, S + *(int*)b); }
any pointer will help?
That's a C comparison function, not a (C++) sorting function. A sort function (probably qsort, given the const void* arguments) would call it a number of times (passing it pointers to what are apparently indices into a buffer containing a number of null-terminated strings) in order to sort those indices (and, indirectly, the substrings they indicate).
This will not sort a string.
This is a method that helps comparing strings and is used by sorthing methods as a helper method.
It will return a negative number if string a is lexicographically smaller than string b, 0 if they are equal, or a positive number if b is greater than a.
Is there a way to find and replace subset of a char*/string in a set?
Example:
std::set<char*> myset;
myset.insert("catt");
myset.insert("world");
myset.insert("hello");
it = myset.subsetfind("tt");
myset.replace(it, "t");
There are at least three reasons why this won't work.
std::set provides only the means to search the set for a value that compares equally to the value being searched for, and not to a value that matches some arbitrary portion of the value.
The shown program is undefined behavior. A string literal, such as "hello" is a const char *, and not a char *. No self-respecting C++ compiler will allow you to insert a const char * into a container of char *s. And you can't modify const values, by definition, anyway.
Values in std::set cannot be modified. To effect the modification of an existing value in a set, it must be erase()d, then the new value insert()ed.
std::set is simply not the right container for the goals you're trying to accomplish.
No, you can't (or at least shouldn't) modify the key while it's in the set. Doing so could change the relative order of the elements, in which case the modification would render the set invalid.
You need to start with a set of things you can modify. Then you need to search for the item, remove it from the set, modify it, then re-insert the result back into the set.
std::set<std::string> myset {"catt", "world", "hello"};
auto pos = std::find_if(myset.begin(), myset.end(), [](auto const &s) { return s.find("tt");};
if (pos != myset.end()) {
auto temp = *pos;
myset.remove(pos);
auto p= temp.find("tt");
temp.replace(p, 2, "t");
myset.insert(temp);
}
You cannot modify elements within a set.
You can find strings that contain the substring using std::find_if. Once you find matching elements, you can remove each from the set and add a modified copy of the string, with the substring replaced with something else.
PS. Remember that you cannot modify string literals. You will need to allocate some memory for the strings.
PPS. Implicit conversion of string literal to char* has been deprecated since C++ was standardized, and since C++11 such conversion is ill-formed.
PPPS. The default comparator will not be correct when you use pointers as the element type. I recommend you to use std::string instead. (A strcmp based comparator approach would also be possible, although much more prone to memory bugs).
You could use std::find_if with a predicate function/functor/lambda that searches for the substring you want.
Is there any issue with using a C string as a map key?
std::map<const char*, int> imap;
The order of the elements in the map doesn't matter, so it's okay if they are ordered using std::less<const char*>.
I'm using Visual Studio and according to MSDN (Microsoft specific):
In some cases, identical string literals can be "pooled" to save space in the executable file. In string-literal pooling, the compiler causes all references to a particular string literal to point to the same location in memory, instead of having each reference point to a separate instance of the string literal.
It says that they are only pooled in some cases, so it seems like accessing the map elements using a string literal would be a bad idea:
//Could these be referring to different map elements?
int i = imap["hello"];
int j = imap["hello"];
Is it possible to overload operator== for const char* so that the actual C string and not the pointer values would be used to determine if the map elements are equal:
bool operator==(const char* a, const char* b)
{
return strcmp(a, b) == 0 ? true : false;
}
Is it ever a good idea to use a C string as a map key?
Is it possible to overload operator== for const char* so that the actual C string and not the pointer values would be used to determine if the map elements are equal
No it's not, and yes, it's not a good idea for exactly the reason pointed out in the question and because you don't need char*, you can use a std::string instead. (you can provide a custom compare function - as pointed out by simonc, but I'd advise against it)
//Could these be referring to different map elements?
int i = imap["hello"];
int j = imap["hello"];
Yes, and they can even refer to elements that don't exist yet, but they'll be created by operator[] and be value initialized. The same issue exists with assignment:
imap["hello"] = 0;
imap["hello"] = 1;
The map could now have 1 or 2 elements.
You can provide a map with a custom comparitor which compares the C strings
std::map<const char*,YourType,CstrCmp>;
bool CstrCmp::operator()(const char* a, const char* b) const
{
return strcmp(a, b) < 0;
}
First, in order to introduce ordering over map keys you need to define a "less-than" comparison. A map says that two elements are "equivalent" if neither is less than the other. It's a bad idea to use char* for map keys because you will need to do memory management somewhere outside the map.
In most realistic scenarios when you query a map your keys will not be literals.
On the other hand, if you maintain a pool of string literals yourself and assign an ID to every literal you could use those IDs for map keys.
To summarize, I wouldn't rely on Microsoft saying "In some cases literals may be pooled". If you fill the map with literals and if you query the map with literals as keys you might as well use enum for keys.
I'm working on a project for school and I am running into a bit of a problem (error is in the title).
Here is the line of code that runs into the error:
kruskalS[n].nodeList[m].push_back(tempFirstCity);
kruskalS is a struct and nodeList is a vector of type string within the struct and I'm trying to insert tempFirstCity (also a string) into that array.
I could easily be making a basic mistake since I haven't done any programming since April. Any kind of help would be appreciated and I'm willing to post a bit more information from the program if needed.
A std::string is (sort of) a container of chars. A push_back function is used to add one element to the end of a container. So when you call kruskalS[n].nodeList[m].push_back(tempFirstCity);, you say you are trying to add one element to the end of the string called kruskalS[n].nodeList[m]. So the compiler expects that one element to be a char.
If you know that tempFirstCity is not empty and you want to add the first char from tempFirstCity to the end of kruskalS[n].nodeList[m] (including the case where you know tempFirstCity.size() == 1), you can do
kruskalS[n].nodeList[m].push_back(tempFirstCity[0]);
If you want to add the entire string after any current contents, you can do
kruskalS[n].nodeList[m] += tempFirstCity;
If you expect there are no current contents and/or you want to just replace anything already there with the tempFirstCity string, you can do
kruskalS[n].nodeList[m] = tempFirstCity;
You can use:
std::string::c_str()
It returns a const char *.
You say nodeList is an array of type string. i.e. std::string nodeList[x] where x is a constant.
Then assigning a new element to that array where m < x is as follows:
kruskalS[n].nodeList[m] = tempFirstCity;
Based on comments:
For appending to end of vector you don't need the index m:
kruskalS[n].nodeList.push_back(tempFirstCity);
For inserting at index m:
vector<string>::iterator itr = nodeList.begin();
for (int i = 0; i < m; i++)
itr++;
nodeList.insert(itr, tempFirstCity);
In C++, you can use string::c_str() to convert a string to C programming char array..