通过只循环一次来组合c++标准算法



我目前已经启动并运行了这段代码:

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

我想让它更紧凑和可读:

  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_;
};

当然,这仍然是非常原始的,但它应该给的想法。使用上面的适配器,我可以编写如下代码:

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

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

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);

如果标准包含这样一个适配器,我会使用它,因为这意味着它是完美无瑕的,但由于情况并非如此,我可能会保持我的循环。

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

我认为没有一种干净的方法可以用单一的标准算法来做到这一点。据我所知,没有一个使用谓词(您需要一个谓词来决定何时提前中断),并允许修改源序列的元素。

如果你真的想用"标准"的方式来做,你可以写你自己的泛型算法。我们叫它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 << '.';
}

这是否比你有更好的是有争议的:)有时一个普通的for循环是最好的。

在更好地理解你的问题之后,我有一个可能有效的想法,但需要Boost。

您可以使用transform_iterator对所有字符调用toupper,并将其用作find_ifremove_if的输入器。我不是很熟悉Boost,所以不能提供一个例子。

正如@jrok所指出的,transform_iterator只会在迭代期间转换值,而不会实际修改原始容器。为了解决这个问题,不是在相同的序列上操作,而是使用remove_copy_if之类的东西复制到一个新的序列。只要谓词不为真,就会复制,因此需要std::not1。这将取代remove_if的情况。

使用std::copy进行复制,直到std::find_if返回的迭代器使另一个case工作。

最后,如果输出字符串为空,则需要一个std::inserter类型的迭代器来输出

最新更新