8

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

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_untiljrok 的建议定义自定义算法之外,还可以定义一个自定义迭代器适配器,该适配器将使用底层迭代器进行迭代,但通过在返回之前修改底层引用来重新定义 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);

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

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

4

2 回答 2

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 << '.';
}

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

于 2012-12-27T14:06:24.647 回答
0

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

您可以使用调用所有字符的transform_iteratortoupper并将其用作 or 的输入迭代find_ifremove_if。不过,我对 Boost 不够熟悉,无法提供示例。

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

用于std::copy复制直到迭代器返回std::find_if以使其他情况正常工作。

最后,如果您的输出字符串为空,则输出需要一种std::inserter迭代器。

于 2012-12-27T13:09:41.497 回答