通过仅循环一次来组合C++标准算法

Jos*_*ari 8 c++ algorithm stl std c++11

我目前已启动并运行此代码:

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
Run Code Online (Sandbox Code Playgroud)

我希望通过以下方式使其更加紧凑和可读:

  1. 编写现有的标准C++算法(*)
  2. 仅执行一次循环

(*)我假设结合现有算法使我的代码更具可读性......

另一种解决方案

除了定义自定义transform_until算法(如jrok所建议)之外,还可以定义一个自定义迭代器适配器,该适配器将使用底层迭代器进行迭代,但在返回之前通过修改底层引用来重新定义operator*().像这样的东西:

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_;
};
Run Code Online (Sandbox Code Playgroud)

当然这仍然是非常原始的,但它应该给出这个想法.使用上面的适配器,我可以编写以下内容:

word.erase(std::find_if(it, it_end, std::not1(std::ref(::isalpha))), word.end());
Run Code Online (Sandbox Code Playgroud)

预先定义了以下内容(可以通过一些模板魔术简化):

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);
Run Code Online (Sandbox Code Playgroud)

如果标准将包含这样的适配器,我会使用它,因为它意味着它是完美的,但由于事实并非如此,我可能会保持我的循环不变.

这样的适配器将允许重用现有的算法并以今天不可能的不同方式混合它们,但它也可能有缺点,我现在可能会忽略...

jro*_*rok 9

我不认为使用单一标准算法可以做到这一点.我所知道的没有一个谓词(你需要一个来决定何时提前破解)并允许修改源序列的元素.

如果你真的想以"标准"的方式做,你可以编写自己的通用算法.我们叫它,嗯,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 << '.';
}
Run Code Online (Sandbox Code Playgroud)

这是否比你拥有的更好是有争议的:)有时一个简单的for循环是最好的.

  • +1,这和我认为的OP一样好.虽然他应该坚持计划A并且只是保持他的循环,但这个答案是一个非常好的概念 - 编写自定义容器变换器的概念,适当的名称全部,并仍然回答OP的问题.即使OP不使用它,仍然是一个很好的答案. (5认同)