I have got a small problem. I want to capitalize on doubled letters in a string. I managed to compile a program, but not successfully.
#include <iostream>
#include <cctype>
#include <string>
std::string::iterator function(
std::string::const_iterator a,
std::string::const_iterator b,
std::string::const_iterator e)
{
for (; a < b; a++)
{
if (*a == *(a + 1))
{
toupper(*a);
toupper(*(a + 1));
}
}
}
int main()
{
std::string in = "peppermint 1001 bubbles balloon gum", out(100, '*');
auto e = function(in.cbegin(), in.cend(), out.begin());
int n = e - out.begin();
std::string s = out.substr(0, n);
bool b = (s == "pePPermint 1001 buBBles baLLOOn gum");
std::cout << std::boolalpha << b << std::endl;
}
What do I do wrong?
You have a couple of issues there.
Firstly, your function promised to return std::string::iterator
std::string::iterator function(....)
{
//... return statement is missing here!
}
and you are not keeping the promise. This will leads to undefined behaviour. For example, in your case, it just compiles and does not give the output.
In order to get a defined behaviour, you should return from the function
std::string::iterator function(...)
{
// ... code
return {}; // return appropriately iterator of std::string
}
Secondly, you want to modify the characters of the string, which requires a modifiable iterator rather than std::string::const_iterator.
Then in the loop, you need to alter the capitalized charector by reassigning to it. For example:
*a = toupper(*a);
Thirdly, you should be careful about doing this in the for-loop of your function
for(; a < b; a++)
{
if(*a == *(a + 1)) // --->here
// ... code
}
What would happen for the case when a== str.end()-1, you still would do the increment(i.e. *(a + 1)), right?. Incrementing the end iterator again leads you Undefined behaviour.
In that case, you could use std::next from <iterator> header to safely check this.
Following is the demonstrative code which clears the issues mentioned above:
#include <iostream>
#include <string>
#include <iterator> // std::next
std::string::iterator function(
std::string::iterator a,
std::string::iterator b,
std::string::iterator e)
{
auto beg = a;
for (; a < b; a++)
{
if (std::next(a) != b && *a == *std::next(a)) {
*a = toupper(*a);
*std::next(a) = toupper(*std::next(a));
}
}
std::cout << std::string{ beg, b };
return {}; // return appropriately iterator of std::string
}
prints for now: https://godbolt.org/z/ZsLHxw
pePPermint 1001 buBBles baLLOOn gum
I assume that you want somehow to get the output to the third function parameter std::string::iterator e. I will let that part for you to figure it out. Meanwhile, have a look at the standard algorithm function std::transform, which might be handy to do such kind of transformation.
Answers are already given. I want additionally to show an answer based on existing C++ functionality. For your given task there is a function existing in the C++ standard algorithm library. It is called std::adjacent_find. Please see here.
With that you could rewrite your code simply to:
#include <iostream>
#include <string>
#include <algorithm>
#include <cctype>
int main() {
std::string test{ "peppermint 1001 bubbles balloon gum" };
// Find all duplicates
for (auto il = std::adjacent_find(test.begin(), test.end()); il != test.end(); il = std::adjacent_find(il+1, test.end())) {
// If duplicate found, then convert both to uppercase
*il = std::toupper(*il);
*(il + 1) = std::toupper(*(il+1));
}
std::cout << test << '\n';
return 0;
}
We call this function in a simple for loop, until no more duplicates could be found.
Maybe it can give you an idea for a more simple implementation.
Your function exhibits undefined behavior because it never returns a value. Please compile with -Wall -Wextra to enable all compiler warnings to avoid such unnecessary bugs.
Related
Say I have
vector<shared_ptr<string>> enemy;
how do I remove elements from the enemy vector?
Thanks for your help in advance
**Edit (code in context)
void RemoveEnemy( vector<shared_ptr<Enemy>> & chart, string id )
{
int i = 0;
bool found = FALSE;
for(auto it = chart.begin(); it != chart.end(); i++)
{
if(id == chart[i]->GetEnemyID() )
{
found = TRUE;
chart.erase(it);
}
}
the code above segfaults me
You remove elements the same way you remove any elements from any std::vector - via the std::vector::erase() method, for instance. All you need for that is an iterator to the desired element to remove.
In your case, since you are storing std::shared_ptr<std::string> objects rather than storing actual std::string objects, you may need to use something like std::find_if() to find the vector element containing the desired string value, eg:
void removeEnemy(string name)
{
auto iter = std::find_if(enemy.begin(), enemy.end(),
[&](auto &s){ return (*s == name); }
);
if (iter != enemy.end())
enemy.erase(iter);
}
UPDATE: in the new code you have added, you are incorrectly mixing indexes and iterators together. You are creating an infinite loop if the vector is not empty, as you never increment the it iterator that controls your loop, you are incrementing your index i variable instead (see what happens when you don't give your variables unique and meaningful names?). So you end up going out of bounds of the vector into surrounding memory. That is why you get the segfault error.
Even though you are (trying to) use an iterator to loop through the vector, you are using indexes to access the elements, instead of dereferencing the iterator to access the elements. You don't need to use indexes at all in this situation, the iterator alone will suffice.
Try this instead:
void RemoveEnemy( vector<shared_ptr<Enemy>> & chart, string id )
{
for(auto it = chart.begin(); it != chart.end(); ++it)
{
if (id == it->GetEnemyID() )
{
chart.erase(it);
return;
}
}
Or, using the kind of code I suggested earlier:
void RemoveEnemy( vector<shared_ptr<Enemy>> & chart, string id )
{
auto iter = std::find_if(chart.begin(), chart.end(),
[&](auto &enemy){ return (enemy->GetEnemyID() == id); }
);
if (iter != chart.end())
chart.erase(iter);
}
The problem with your code is that erase() invalidates the iterator. You must use it = chart.erase(it).
I like mine which will remove aliens at high speed and without any care for the ordering of the other items. Removal with prejudice!
Note: remove_if is most often used with erase and it will preserve the order of the remaining elements. However, partition does not care about the ordering of elements and is much faster.
partition-test.cpp:
make partition-test && echo 1 alien 9 alien 2 8 alien 4 7 alien 5 3 | ./partition-test
#include <algorithm>
#include <iostream>
#include <iterator>
#include <memory>
#include <string>
#include <vector>
using namespace std;
template <typename T>
ostream &operator<<(ostream &os, const vector<T> &container) {
bool comma = false;
for (const auto &x : container) {
if (comma)
os << ", ";
os << *x;
comma = true;
}
return os;
}
int main() {
vector<shared_ptr<string>> iv;
auto x = make_shared<string>();
while (cin >> *x) {
iv.push_back(x);
x = make_shared<string>();
}
cout << iv << '\n';
iv.erase(partition(begin(iv), end(iv),
[](const auto &x) { return *x != "alien"s; }),
end(iv));
cout << iv << '\n';
return 0;
}
Code:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
template<typename T, typename C> T pfind(T f, T e, C c){
do{
for (int i = 1; f + i != e; i++)
if (c(*f, *f + i))
return (f + i);
} while (++f != e);
}
int main()
{
vector<int>vstr = { 1, 2, 3, 4,4,5,5,5 };
vstr.erase(pfind(vstr.begin(), vstr.end(),equal_to<int>()));
for (auto&itr : vstr){
cout << itr << " ";
}
getchar();
}
This code crashes...
But when i tried to use less instead of equal_to its working....
Could anyone please explain me why?
If you'd paid attention to the compiler warnings, you'd have had a clue why your function fails. When you pass std::equal_to as the comparison predicate, it's not finding a match (due to an error in your code).
In the case where a match isn't found, your function ends without returning anything, which is undefined behavior. The subsequent use of this non-existent return value in the call to vector::erase results in the crash.
Your code fails to find a match because of this condition:
if (c(*f, *f + i))
You're first dereferencing the iterator and then adding i to that result. What you actually want to do is
if (c(*f, *(f + i)))
And then add a return statement at the end of the function for the case when a match is not found.
return e; // return the end iterator
Finally, your entire function can be replaced by std::adjacent_find which searches a range for two adjacent elements that meet a specified criterion. The two argument version uses operator== to perform the comparison, while you can supply a binary predicate using the three argument version. Using this, your example reduces to
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<int>vstr = { 1, 2, 3, 4,4,5,5,5 };
vstr.erase(std::adjacent_find(vstr.begin(), vstr.end()));
for (auto&itr : vstr) {
cout << itr << " ";
}
}
Output:
1 2 3 4 5 5 5
Live demo
So, in the comments Alessandro Teruzzi has the key point.
are you sure you meant c(*f,*f+i) instead of c(*f,*(f+i))?
But that being said, this could be written more clearly as two nested for loops, and with longer variable names.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
template<typename T, typename C> T pfind(T begin, T end, C comparator){
for (int i = 0; begin + i != end; ++i)
for (int j = i+1; begin + j != end; j++)
if (comparator(begin[i], begin[j]))
return (begin + i);
return end;
}
int main()
{
vector<int>vstr = { 1, 2, 3, 4,4,5,5,5 };
vstr.erase(pfind(vstr.begin(), vstr.end(),equal_to<int>()));
for (auto&itr : vstr){
cout << itr << " ";
}
getchar();
}
We could also write the pfind() function directly using iterators:
template<typename T, typename C> T pfind(T begin, T end, C comparator){
for (; begin != end; begin++)
for (T iterator = std::next(begin); iterator != end; iterator++)
if (comparator(*begin, *iterator))
return begin;
return end;
}
Additionally, in both cases there's a chance that we will return the end pointer. If we do, then we will call vstr.erase(vstr.end()), which is undefined behavior. So you probably want to check for that.
Let's talk about what a particular warning means:
In the comments you said that the compiler reported this warning:
main.cpp:13:1: warning: control may reach end of non-void function [-Wreturn-type]
It means that the compiler was not able to ensure that all paths through the code would end with a statement return ...; In your case, in the simplest case, if begin == end, then the first loop will not be entered, and we will hit the end of the function without reaching a return ...; statement.
In the code I've posted with fixes, you can see that if we never find a match, we return the end pointer, which is a common way of reporting no match.
I am playing with boost::range and boost::lambda with following example to compare two numbers and get the element out which has same number.
#include <iostream>
#include <boost/optional.hpp>
#include <boost/range/algorithm/find_if.hpp>
#include <boost/typeof/typeof.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/utility/compare_pointees.hpp>
template <class Range, class Predicate>
boost::optional<typename boost::range_value<Range>::type>
search_for(const Range& r, Predicate pred)
{
BOOST_AUTO (it, boost::find_if(r, pred));
if (it == boost::end(r))
return boost::none;
return *it;
}
int main()
{
int a = 1;
int b = 2;
int c = 3;
int d = 3;
std::vector<int*> m = {&a, &b, &c};
if (boost::optional<int*> number =
search_for(m, boost::equal_pointees(???, &d))) {
std::cout << "found:" << (*number.get()) << std::endl;
}
else {
std::cout << "not found" << std::endl;
}
}
What should I use for ??? above in search_for function?
I believe it could be very simple but don't know how to do it. I can use the boost::bind or std::bind2d, etc to compare but was thinking if there is any elegant way to do it. Also, this code sample could be restructured to much simpler one but I am just learning.
With boost::lambda, it looks like this:
namespace ll = boost::lambda;
search_for(m, *ll::_1 == d)
Which is far less complicated than taking a pointer to d just so you can use equal_pointees.
Is it possible to do something like:
string word = "Hello";
word[3] = null;
if(word[3] == null){/.../}
in C++, basically making an array element empty. For example if I wanted to remove the duplicate characters from the array I'd set them to null first and then shifted the array to the left every time I found an array index that contained null.
If this is not possible what's a good way of doing something like this in C++ ?
If you want to remove adjacent duplicate characters, you can do this:
std::string::iterator new_end = std::unique(word.begin(), word.end());
word.erase(new_end, word.end());
If you want to mark arbitrary characters for removal, you can skip the marking and just provide the appropriate predicate to std::remove_if:
new_end = std::remove_if(word.begin(), word.end(), IsDuplicate);
word.erase(new_end, word.end());
However, I can't think of an appropriate predicate to use here that doesn't exhibit undefined behavior. I would just write my own algorithm:
template<typename IteratorT>
IteratorT RemoveDuplicates(IteratorT first, IteratorT last)
{
typedef typename std::iterator_traits<IteratorT>::value_type
ValueT;
std::map<ValueT, int> counts;
for (auto scan=first; scan!=last; ++scan)
{
++counts[*scan];
if(counts[*scan] == 1)
{
*first = std::move(*scan);
++first;
}
}
return first;
}
Or, if you don't care about the order of the elements, you could simply sort it, then use the first solution.
This is possible, since a single element of a string is an element within a char-array and thus representable as pointer, i. e. you can retrieve the address of the element. Therefore you can set word[3] = null. Your if-construct is valid but the compiler prints a warning, this is because NULL is only a pointer constant. Alternatives would be: if (!word[3]) or if(word[3] == 0).
But in any case you should consider using STL algorithms for removing duplicates.
I think you should take a look at the algorithm in the STL.
You are not very specific about what you want to remove but maybe this helps:
std::string string_with_dup("AABBCCDD");
std::string string_without_dup;
std::cout << string_with_dup << std::endl;
// with copy
std::unique_copy(string_with_dup.begin(), string_with_dup.end(), std::back_inserter(string_without_dup));
std::cout << string_without_dup << std::endl;
// or inplace
string_with_dup.erase(std::unique(string_with_dup.begin(), string_with_dup.end()), string_with_dup.end());
std::cout << string_with_dup << std::endl;
If you want to remove all duplicates (not only the adjacent ones, you should use the erase-remove idiom with something like this
#include <iostream>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
struct is_repeated {
is_repeated( map<char,int>& x ) :r(&x) {};
map<char,int>* r;
bool operator()( char c ) {
(*r)[c]++;
if( (*r)[c] > 1 )
return true;
return false;
}
};
int main (int argc, char**argv)
{
map<char,int> counter_map;
string v = "hello hello hello hello hello hello hello";
cout << v << endl;
is_repeated counter(counter_map);
v.erase( remove_if(v.begin(), v.end(), counter ), v.end() );
cout << v << endl;
}
outputs (as of this):
hello hello hello hello hello hello hello
helo
My question is related to this.
I wanted to perform a sort() operation over the set with the help of a lambda expression as a predicate.
My code is
#include <set>
#include <string>
#include <iostream>
#include <algorithm>
int main() {
using namespace std;
string s = "abc";
set<string> results;
do {
for (int n = 1; n <= s.size(); ++n) {
results.insert(s.substr(0, n));
}
} while (next_permutation(s.begin(), s.end()));
sort (results.begin(),results.end());[](string a, string b)->bool{
size_t alength = a.length();
size_t blength = b.length();
return (alength < blength);
});
for (set<string>::const_iterator x = results.begin(); x != results.end(); ++x) {
cout << *x << '\n';
}
return 0;
}
But the numbers and types of errors were so complex that I couldn't understand how to fix them. Can someone tell me whats wrong with this code.
Edit: Note that Steve Townsend's solution is actually the one you're searching for, as he inlines as a C++0x Lambda what I write as C++03 code below.
Another solution would be to customize the std::set ordering function:
The std::set is already ordered...
The std::set has its own ordering, and you are not supposed to change it once it is constructed. So, the following code:
int main(int argc, char* argv[])
{
std::set<std::string> aSet ;
aSet.insert("aaaaa") ;
aSet.insert("bbbbb") ;
aSet.insert("ccccccc") ;
aSet.insert("ddddddd") ;
aSet.insert("e") ;
aSet.insert("f") ;
outputSet(aSet) ;
return 0 ;
}
will output the following result:
- aaaaa
- bbbbb
- ccccccc
- ddddddd
- e
- f
... But you can customize its ordering function
Now, if you want, you can customize your set by using your own comparison function:
struct MyStringLengthCompare
{
bool operator () (const std::string & p_lhs, const std::string & p_rhs)
{
const size_t lhsLength = p_lhs.length() ;
const size_t rhsLength = p_rhs.length() ;
if(lhsLength == rhsLength)
{
return (p_lhs < p_rhs) ; // when two strings have the same
// length, defaults to the normal
// string comparison
}
return (lhsLength < rhsLength) ; // compares with the length
}
} ;
In this comparison functor, I did handle the case "same length but different content means different strings", because I believe (perhaps wrongly) that the behaviour in the original program is an error. To have the behaviour coded in the original program, please remove the if block from the code.
And now, you construct the set:
int main(int argc, char* argv[])
{
std::set<std::string, MyStringLengthCompare> aSet ;
aSet.insert("aaaaa") ;
aSet.insert("bbbbb") ;
aSet.insert("ccccccc") ;
aSet.insert("ddddddd") ;
aSet.insert("e") ;
aSet.insert("f") ;
outputSet(aSet) ;
return 0 ;
}
The set will now use the functor MyStringLengthCompare to order its items, and thus, this code will output:
- e
- f
- aaaaa
- bbbbb
- ccccccc
- ddddddd
But beware of the ordering mistake!
When you create your own ordering function, it must follow the following rule:
return true if (lhs < rhs) is true, return false otherwise
If for some reason your ordering function does not respect it, you'll have a broken set on your hands.
std::sort rearranges the elements of the sequence you give it. The arrangement of the sequence in the set is fixed, so the only iterator you can have is a const iterator.
You'll need to copy results into a vector or deque (or such) first.
vector sortable_results( results.begin(), results.end() );
You can customize the ordering of the elements in the set by providing a custom predicate to determine ordering of added elements relative to extant members. set is defined as
template <
class Key,
class Traits=less<Key>,
class Allocator=allocator<Key>
>
class set
where Traits is
The type that provides a function
object that can compare two element
values as sort keys to determine their
relative order in the set. This
argument is optional, and the binary
predicate less is the default
value.
There is background on how to use lambda expression as a template parameter here.
In your case this translates to:
auto comp = [](const string& a, const string& b) -> bool
{ return a.length() < b.length(); };
auto results = std::set <string, decltype(comp)> (comp);
Note that this will result in set elements with the same string length being treated as duplicates which is not what you want, as far as I can understand the desired outcome.
sort requires random access iterators which set doesn't provide (It is a bidirectional iterator). If you change the code to use vector it compiles fine.
You cannot sort a set. It's always ordered on keys (which are elements themselves).
To be more specific, std::sort requires random access iterators. The iterators provided by std::set are not random.
Since I wrote the original code you're using, perhaps I can expand on it... :)
struct cmp_by_length {
template<class T>
bool operator()(T const &a, T const &b) {
return a.length() < b.length() or (a.length() == b.length() and a < b);
}
};
This compares by length first, then by value. Modify the set definition:
set<string, cmp_by_length> results;
And you're good to go:
int main() {
using namespace std;
string s = "abc";
typedef set<string, cmp_by_length> Results; // convenience for below
Results results;
do {
for (int n = 1; n <= s.size(); ++n) {
results.insert(s.substr(0, n));
}
} while (next_permutation(s.begin(), s.end()));
// would need to add cmp_by_length below, if I hadn't changed to the typedef
// i.e. set<string, cmp_by_length>::const_iterator
// but, once you start using nested types on a template, a typedef is smart
for (Results::const_iterator x = results.begin(); x != results.end(); ++x) {
cout << *x << '\n';
}
// of course, I'd rather write... ;)
//for (auto const &x : results) {
// cout << x << '\n';
//}
return 0;
}
std::set is most useful to maintain a sorted and mutating list. It faster and smaller to use a vector when the set itself wont change much once it's been built.
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
int main() {
using namespace std;
string s = "abc";
vector<string> results;
do {
for (size_t n = 1; n <= s.size(); ++n) {
results.push_back(s.substr(0, n));
}
} while (next_permutation(s.begin(), s.end()));
//make it unique
sort( results.begin(), results.end() );
auto end_sorted = unique( results.begin(), results.end() );
results.erase( end_sorted, results.end() );
//sort by length
sort (results.begin(),results.end());
[](string lhs, string rhs)->bool
{ return lhs.length() < rhs.length(); } );
for ( const auto& result: results ) {
cout << result << '\n';
}
}
I used the classic, sort/unique/erase combo to make the results set unique.I also cleaned up your code to be a little bit more c++0x-y.