Combining standard C ++ algorithms by cyclizing only once

This code currently works and works:

string word="test,"; string::iterator it = word.begin(); for (; it != word.end(); it++) { if (!isalpha(*it)) { break; } else { *it = toupper(*it); } } word.erase(it, word.end()); // word should now be: TEST 

I would like to make it more compact and readable:

  • Compilation of existing standard C ++ algorithms (*)
  • Run the loop only once

(*) I assume that combining existing algorithms makes my code more readable ...

Alternative solution

In addition to defining a custom transform_until algorithm, as suggested by jrok, it would be possible to define a custom iterator adapter that will iterate using the base iterator, but override the * () operator by changing the base link before returning it, Something like this :

 template <typename Iterator, typename UnaryFunction = typename Iterator::value_type (*)(typename Iterator::value_type)> class sidefx_iterator: public std::iterator< typename std::forward_iterator_tag, typename std::iterator_traits<Iterator>::value_type, typename std::iterator_traits<Iterator>::difference_type, typename std::iterator_traits<Iterator>::pointer, typename std::iterator_traits<Iterator>::reference > { public: explicit sidefx_iterator(Iterator x, UnaryFunction fx) : current_(x), fx_(fx) {} typename Iterator::reference operator*() const { *current_ = fx_(*current_); return *current_; } typename Iterator::pointer operator->() const { return current_.operator->(); } Iterator& operator++() { return ++current_; } Iterator& operator++(int) { return current_++; } bool operator==(const sidefx_iterator<Iterator>& other) const { return current_ == other.current_; } bool operator==(const Iterator& other) const { return current_ == other; } bool operator!=(const sidefx_iterator<Iterator>& other) const { return current_ != other.current_; } bool operator!=(const Iterator& other) const { return current_ != other; } operator Iterator() const { return current_; } private: Iterator current_; UnaryFunction fx_; }; 

Of course, this is still very rude, but this should give an idea. Using the above adapter, I could write the following:

 word.erase(std::find_if(it, it_end, std::not1(std::ref(::isalpha))), word.end()); 

with the following predefined (which can be simplified with the help of some template magic):

 using TransformIterator = sidefx_iterator<typename std::string::iterator>; TransformIterator it(word.begin(), reinterpret_cast<typename std::string::value_type(*)(typename std::string::value_type)>(static_cast<int(*)(int)>(std::toupper))); TransformIterator it_end(word.end(), nullptr); 

If the standard included such an adapter, I would use it because it would mean that it was perfect, but since it is not, I will probably keep my cycle as it is.

Such an adapter will allow you to reuse existing algorithms and mix them in different ways, which is impossible today, but may have drawbacks, which I probably don’t notice right now ...

+8
c ++ algorithm c ++ 11 std stl
source share
2 answers

I don’t think there is a clean way to do this using one standard algorithm. None of those that I know takes a predicate (you need to decide when to break early) and allows you to change the elements of the original sequence.

You can write your own generic algorithm if you really want to do this in a “standard” way. Let me call it, hmm, transform_until :

 #include <cctype> #include <string> #include <iostream> template<typename InputIt, typename OutputIt, typename UnaryPredicate, typename UnaryOperation> OutputIt transform_until(InputIt first, InputIt last, OutputIt out, UnaryPredicate p, UnaryOperation op) { while (first != last && !p(*first)) { *out = op(*first); ++first; ++out; } return first; } int main() { std::string word = "test,"; auto it = transform_until(word.begin(), word.end(), word.begin(), [](char c) { return !::isalpha(static_cast<unsigned char>(c)); }, [](char c) { return ::toupper(static_cast<unsigned char>(c)); }); word.erase(it, word.end()); std::cout << word << '.'; } 

It is debatable whether this is better than what you are :) Sometimes a simple loop is better.

+9
source share

After a better understanding of your question, I got an idea that may work, but requires Boost .

You can use transform_iterator which calls toupper for all characters and uses this as input identifier for find_if or remove_if . I am not familiar enough with Boost to provide an example.

As @jrok points out, transform_iterator will only convert the value during the iteration and will not actually modify the original container. To get around this, instead of working in the same sequence, you would like to copy to a new one using something like remove_copy_if . This copies as long as the predicate is NOT true, so std::not1 required. This will replace the remove_if case.

Use std::copy to copy until the iterator returns with std::find_if to make another thing work.

Finally, if your output string is empty, iterator std::inserter type is required for output.

0
source share

All Articles