How I should define lambda to take char from string iterator? In the code below lambda detect_bracket has problem with input parameter x.
I don't want to delete ALL brackets from the string, just at the beginning and at the end.
auto detect_bracket = [](char* x){ return(')' == x || '(' == x);};
this->str.erase(std::remove_if(str.begin(), str.begin(),
detect_bracket)
);
this->str.erase(std::remove_if(str.back(), str.back(),
detect_bracket)
);
You should take char as the parameter type of the lambda with std::remove_if, since the signature of the predicate function is supposed to check the element directly.
auto detect_bracket = [](char x){ return(')' == x || '(' == x);};
this->str.erase(std::remove_if(str.begin(), str.end(),
detect_bracket)
);
Note std::string::back() won't work with std::remove_if. It will return a char and std::remove_if expects a range expressed by iterator.
And str.begin(), str.begin() is just an empty range, if you just want to remove element at the begin and end, you could
auto detect_bracket = [](char x){ return(')' == x || '(' == x);};
if (!this->str.empty()) {
this->str.erase(std::remove_if(str.begin(), str.begin() + 1, detect_bracket), str.begin() + 1);
}
if (!this->str.empty()) {
this->str.erase(std::remove_if(str.end() - 1, str.end(), detect_bracket), str.end());
}
Note we need to specify the correct end iterator for std::string::erase, because std::remove_if will return an iterator even if it found nothing, and then the char will be erased wrongly.
LIVE
std::remove_if is a function with the following signature:
template< class ForwardIt, class UnaryPredicate >
ForwardIt remove_if( ForwardIt first, ForwardIt last, UnaryPredicate p );
p - unary predicate which returns true if the element should be removed.
The signature of the predicate function should be equivalent to the following:
bool pred(const Type &a);
The type Type must be such that an object of type ForwardIt can be
dereferenced and then implicitly converted to Type.
All you need is to change your function parameter from char* to char.
Multiple remove_if and erase calls are anyway modifying/invalidating the string. Why not simply create a new string, and conditionally assign source string from 0th location or 1st location? And then assign till the last or second last character, conditionally?
string target;
target.assign(source.begin() + skip_if_bracket_at_begin,
source.end() - skip_if_bracket_at_end);
Related
I am using C++ erase remove idiom and facing a weird problem.
If I access element using string index result is not as expected.
string str = "A man, a plan, a canal: Panama";
str.erase(remove(str.begin(), str.end(), str[1]), str.end());
Result : Aman, a plan, a canal: Panaa
and if I use as below, result is as expected.
string str = "A man, a plan, a canal: Panama";
str.erase(remove(str.begin(), str.end(), ' '), str.end());
Result : Aman,aplan,acanal:Panama
Look at the signature of std::remove:
template< class ForwardIt, class T >
ForwardIt remove( ForwardIt first, ForwardIt last, const T& value );
The value is passed by const reference, therefore after removing first space your str[1] points to a wrong memory. It's unsafe to access container elements while modifying the container.
The algorithm std::remove is declared the following way
template<class ForwardIterator, class T>
ForwardIterator remove(
ForwardIterator first, ForwardIterator last,
const T& value // <-- reference!
);
As you can see the third parameter is declared as a reference const T& value.
So in this call
str.erase(remove(str.begin(), str.end(), str[1]), str.end());
the third argument is a reference to the object str[1] the value of which is changed within the algorithm to the letter 'm' when the first character ' ' is encountered..
If you would write for example
str.erase(remove(str.begin(), str.end(), str[1] + 0), str.end());
you would get the expected result because in this case the reference refers to a temporary object.
I'm (forward) iterating over a std::map and would like to find if the iterator points to the second last element. I can't seem to find how to do that anywhere.
I've got:
bool
isSecondLastFile(const TDateFileInfoMap::const_iterator &tsFile)
{
TDateFileInfoMap::reverse_iterator secondLastIt = mFileInfoMap.rbegin() + 1;
return (tsFile == secondLastIt);
}
Where TDateFileInfoMap is std::map
I'm getting:
error: no match for ‘operator==’ in ‘tsFile == secondLastIt’
/usr/lib/gcc/i686-redhat-linux/4.4.7/../../../../include/c++/4.4.7/bits/stl_tree.h:287: note: candidates are: bool std::_Rb_tree_const_iterator<_Tp>::operator==(const std::_Rb_tree_const_iterator<_Tp>&) const [with _Tp = std::pair<const long int, TFileInfo>]
Does that mean I can't compare the forward and reverse iterator?
How do I figure out if the forward iterator is pointing at the second last element?
std::map's iterator type is BidirectionalIterator. Just decrement the end iterator twice--first to get the last element since m.end() returns an iterator at the after the end position, and then again to get the second-last element:
auto penultimate = std::prev(m.end(), 2);
Then you can simply check for equality with the resultant iterator:
auto it = m.begin();
it == penultimate;
see it live on Coliru
Naturally, you should check that the map has two elements first if it's not guaranteed by other logic in your program.
Does that mean I can't compare the forward and reverse iterator?
Yes you can't compare them directly.
You can use base() to get the underlying base iterator.
Returns the underlying base iterator. That is
std::reverse_iterator(it).base() == it.
The base iterator refers to the element that is next (from the
std::reverse_iterator::iterator_type perspective) to the element the
reverse_iterator is currently pointing to. That is &*(rit.base() - 1) == &*rit.
e.g.
return (tsFile == (++secondLastIt).base());
BTW: mFileInfoMap.rbegin() + 1 won't compile since the iterator of std::map is not RandomAccessIterator. You might write:
TDateFileInfoMap::reverse_iterator secondLastIt = mFileInfoMap.rbegin();
++secondLastIt;
Note that we're not checking whether the map is empty or has only one element.
A simple solution for forward iterators:
template <typename ForwardIterator>
inline bool isNthLast(std::size_t n, ForwardIterator pos, ForwardIterator last) {
for( ;; --n, ++pos) {
if(n == 0)
return (pos == last);
if(pos == last)
return false;
}
}
bool isSecondLastFile(TDateFileInfoMap::const_iterator sFile) {
return isNthLast(2, sFile, mFileInfoMap.end());
}
Let's say you have a set with name s.
s= {s1,s2,...,sN-1, sN}
Now to iterate from s1.. to sN-1 (which is second last element) we will use STL functions, s.begin() and s.end().
end = s.end(); //end points to end
end--// end points to sN
Now in the for loop when itr (starts from the beginning of set) becomes equal to sN the loop will break, and you will get s1,s2,..sN-1 inside the loop.
map<int,int> s;
// to iterate till fixed range in map
auto end =s.end();
end--; // end to second last;
for(auto itr = s.begin(); itr!=end;itr++){
// do your operation
}
I am having trouble executing this block of code. The first base condition of this code(for searching a string in a list of strings) does not work. Thanks.
int string_check(list<string> l,list<string>::iterator it,string s)
{
if(it==l.end()) return 0;
if(*it==s) return 1;
return(string_check(l,++it,s));
}
You're passing the list by value, so l.end() is the end of a different list each time, and never the one that it came from.
Either pass the list by reference; or pass the end iterator rather than the list itself. That would be a more flexible solution, allowing you to decouple the function from a specific container type and support any range of input iterators:
template <typename InIter, typename T>
bool contains(InIter begin, InIter end, T const & value) {
if (begin == end) return false;
if (*begin == value) return true;
return contains(++begin, end, value);
}
Recursion is often a bad idea, as the stack is typically fairly small and causes horrible bugs if it overflows. Unless this is an exercise in implementing such a function, use iteration:
for (; begin != end; ++begin) {
if (*begin == value) return true;
}
return false;
or the standard library:
return std::find(begin, end, value) != end;
You need to write int string_check(const list<string>& l, const list<string>::iterator it&, const string& s) instead.
Else you're taking a value copy of the std::list and the iterator, so any modifications to them will not be reflected in the caller.
I'm passing the objects by constant reference. This helps program stability since the function body cannot modify the parameters passed. I'm also passing the string in this way too in order to prevent an unnecessary value copy.
I would define the function differently. It is enough to specify a pair of iterators that specify the target range in the list.
bool string_check( std::list<std::string>::const_iterator first,
std::list<std::string>::const_iterator last,
const std::string &s )
{
return ( first != last ) &&
( *first == s || string_check( ++first, last, s ) );
}
That is there is no need to pass also the list itself.
The same way you could define a template function that could deal with any type of the list. Only the name of the function you should select more suitable.
For example
template <class InputIterator, class T>
bool find( InputIterator first,
InputIterator last,
const T &value )
{
return ( first != last ) &&
( *first == value || string_check( ++first, last, value ) );
}
As for your function implementation then the first parameter that is the list shall be be declared as reference
list<string> &l
Moover it should be declared as a const reference
const list<string> &l
that the function could be called for constant list. In this case you have also to change the type of the iterator.
I'm following the book Accelerated C++, and to write a function to split a string into a vector of words (separated by space characters), find_if is utilized.
vector<string> split(const string& str) {
typedef string::const_iterator iter;
vector<string> ret;
iter i = str.begin();
while (i != str.end()) {
i = find_if(i, str.end(), not_space);
iter j = find_if(i, str.end(), space);
if (i != str.end())
ret.push_back(string(i, j));
i = j;
}
return ret;
}
and the definitions of space and not_space:
bool space(char c) {
return isspace(c);
}
bool not_space(char c) {
return !isspace(c);
}
Is it necessary to write two separate predicates here, or could one simply pass !space in place of not_space?
Just use std::not1(std::ptr_fun(space)). std::not1 is declared in <functional>.
(There is also a std::not2 for use with binary predicates; std::not1 is for unary predicates.)
You cannot simply use !space instead of not_space because all you'll be doing in that case is passing false to find_if. That happens because space will decay to a pointer to function, and function pointers are implicitly convertible to bool. Applying ! to the boolean value will always result in false (because the function pointer is never going to be nullptr).
You can reuse the function space by wrapping it in std::not1, which will negate the result of the predicate passed to it. Unfortunately, it's not as simple as writing std::not1(space), because not1 requires that the predicate define a nested type named argument_type, which your predicate doesn't satisfy.
To convert your function into a predicate usable with not1, you must first wrap it in std::ptr_fun. So the line in your split function becomes:
i = find_if(i, str.end(), std::not1(std::ptr_fun(space)));
With C++11, there's no need for the not1 and ptr_fun shenanigans, just use a lambda expression:
i = find_if(i, str.end(), [](char c) {return !space(c);});
You can also declare
template <bool find_space> bool space(char c) {
return find_space ^ (!isspace(c));
}
and then refer to it as space<true> and space<false> in the argument to find_if(). Much more versatile than std::not1().
How would I do something in c++ similar to the following code:
//Lang: Java
string.replaceAll(" ", " ");
This code-snippet would replace all multiple spaces in a string with a single space.
bool BothAreSpaces(char lhs, char rhs) { return (lhs == rhs) && (lhs == ' '); }
std::string::iterator new_end = std::unique(str.begin(), str.end(), BothAreSpaces);
str.erase(new_end, str.end());
How this works. The std::unique has two forms. The first form goes through a range and removes adjacent duplicates. So the string "abbaaabbbb" becomes "abab". The second form, which I used, takes a predicate which should take two elements and return true if they should be considered duplicates. The function I wrote, BothAreSpaces, serves this purpose. It determines exactly what it's name implies, that both of it's parameters are spaces. So when combined with std::unique, duplicate adjacent spaces are removed.
Just like std::remove and remove_if, std::unique doesn't actually make the container smaller, it just moves elements at the end closer to the beginning. It returns an iterator to the new end of range so you can use that to call the erase function, which is a member function of the string class.
Breaking it down, the erase function takes two parameters, a begin and an end iterator for a range to erase. For it's first parameter I'm passing the return value of std::unique, because that's where I want to start erasing. For it's second parameter, I am passing the string's end iterator.
So, I tried a way with std::remove_if & lambda expressions - though it seems still in my eyes easier to follow than above code, it doesn't have that "wow neat, didn't realize you could do that" thing to it.. Anyways I still post it, if only for learning purposes:
bool prev(false);
char rem(' ');
auto iter = std::remove_if(str.begin(), str.end(), [&] (char c) -> bool {
if (c == rem && prev) {
return true;
}
prev = (c == rem);
return false;
});
in.erase(iter, in.end());
EDIT realized that std::remove_if returns an iterator which can be used.. removed unnecessary code.
A variant of Benjamin Lindley's answer that uses a lambda expression to make things cleaner:
std::string::iterator new_end =
std::unique(str.begin(), str.end(),
[=](char lhs, char rhs){ return (lhs == rhs) && (lhs == ' '); }
);
str.erase(new_end, str.end());
Why not use a regular expression:
boost::regex_replace(str, boost::regex("[' ']{2,}"), " ");
how about isspace(lhs) && isspace(rhs) to handle all types of whitespace