简体   繁体   English

使用条件迭代器插入向量

[英]Insert into vector with conditional iterator

Say I have a vector with various entries, which I want to insert into another vector, while leaving out entries that satisfy a condition. 假设我有一个带有各种条目的vector ,我想插入到另一个向量中,同时省略满足条件的条目。

For example, I want to insert a vector while leaving out all three's. 例如,我想插入一个向量,同时省略所有三个。

{1, 3, 2, 3, 4, 5, 3} -> { /* previous content, */ 1, 2, 4, 5}

What I came up with so far uses std::partition , which does not preserve the relative order and rearranges the source vector. 到目前为止我想出的是使用std::partition ,它不保留相对顺序并重新排列源向量。

std::vector<int> source({1, 3, 2, 3, 4, 5, 3});
std::vector<int> target;

auto partition = std::partition(std::begin(source),
                   std::end(source), [](const auto& a) { return a == 3; });
target.insert(std::begin(target), partition, std::end(source));

What I am looking for is more of an iterator that checks a condition and moves on if the condition is not satisfied. 我正在寻找的更多是一个迭代器,它检查条件并在条件不满足时继续运行。 Something like this: 像这样的东西:

target.insert(std::begin(target),
              conditional_begin(source, [](const auto& a) { return a != 3; },
              conditional_end(source));

I suppose a conditional_end function would be necessary, since std::end would return a different iterator type than conditional_begin . 我想一个conditional_end函数是必要的,因为std::end会返回一个与conditional_begin不同的迭代器类型。

Maybe I have overlooked something, so my questions are: 也许我忽略了一些事情,所以我的问题是:

  • Does the standard library provide something similar? 标准库是否提供类似的东西?
  • Is there a different easy way to achieve my goal? 有没有不同的简单方法来实现我的目标?
  • Is there an easy way to implement the conditional iterator functionality? 有没有一种简单的方法来实现条件迭代器功能?

Is there a different easy way to achieve my goal? 有没有不同的简单方法来实现我的目标?

Yes, the standard already has this functionality built in. The function you are looking for is std::copy_if . 是的,标准已经内置了此功能。您正在寻找的功能是std::copy_if

std::vector<int> source({1, 3, 2, 3, 4, 5, 3});
std::vector<int> target;

std::copy_if(source.begin(), 
             source.end(), 
             std::back_inserter(target), [](auto val){ return val != 3; });

Here, std::back_inserter(target) , will call push_back on target for each element that the predicate returns true . 这里, std::back_inserter(target)将为谓词返回true每个元素调用target push_back

Yes, you can create a custom iterator that does what you want but it is currently a little tedious to create custom iterators using standard C++. 是的,您可以创建一个自定义迭代器来执行您想要的操作,但使用标准C ++创建自定义迭代器当前有点繁琐。 It would look something like this: 它看起来像这样:

template <typename Itr, typename F> 
struct ConditionalIterator {
  Itr itr;
  Itr end;
  F condition;

  using value_type = typename Itr::value_type;
  using difference_type = typename Itr::difference_type;
  using pointer = typename Itr::pointer;
  using reference = typename Itr::reference;
  using iterator_category = std::forward_iterator_tag;

  ConditionalIterator() = default;
  ConditionalIterator(Itr itr, Itr end, F condition): itr(itr), end(end), condition(condition) {}

  bool      operator!=(const ConditionalIterator &other) const { return other.itr != itr; }
  reference operator*() const { return *itr; }
  pointer   operator->() const { return &(*itr); }

  ConditionalIterator& operator++() {
    for (; ++itr != end;) {
      if (condition(*itr))
        break;
    }
    return *this;
  }
  ConditionalIterator operator++(int) {
    ConditionalIterator ret(*this);
    operator++();
    return ret;
  }
};

You can then create something like the conditional_begin and conditional_end helper functions you asked for. 然后,您可以创建类似您要求的conditional_beginconditional_end帮助函数。 The only issue is that std::vector::insert expects the two iterators to have the same type. 唯一的问题是std::vector::insert期望两个迭代器具有相同的类型。 If we use a lambda for our condition then this will be part of the type of our conditional iterator. 如果我们将lambda用于我们的条件,那么这将是条件迭代器类型的一部分。 So we need to pass the lambda to both helper functions so that they return iterators with matching types: 所以我们需要将lambda传递给两个辅助函数,以便它们返回具有匹配类型的迭代器:

template <typename C, typename F> 
auto conditional_begin(const C &source, F f) {
  return ConditionalIterator<typename C::const_iterator, F>(source.begin(),
                                                            source.end(), f);
}

template <typename C, typename F> 
auto conditional_end(const C &source, F f) {
  return ConditionalIterator<typename C::const_iterator, F>(source.end(),
                                                            source.end(), f);
}

Which you could call with a lambda like this: 您可以使用这样的lambda调用:

auto condition = [](const auto &a) { return a != 3; };
target.insert(std::begin(target),
              conditional_begin(source, std::ref(condition)),
              conditional_end(source, std::ref(condition)));

Live demo . 现场演示

My crude tests show, in this case, this ends up being significantly faster than simply using copy_if and back_inserter because std::vector::insert first works out how much memory to allocate before inserting. 我的原始测试显示,在这种情况下,这最终比简单地使用copy_ifback_inserter快得多,因为std::vector::insert首先计算出在插入之前要分配多少内存。 Just using back_inserter will cause multiple memory allocations. 仅使用back_inserter将导致多个内存分配。 The difference in performance will depend on how expensive the condition is to evaluate. 性能的差异将取决于评估条件的成本。 You can get the same speedup by using count_if to reserve enough space before using copy_if : 在使用copy_if之前,可以使用count_if保留足够的空间来获得相同的加速:

auto count = static_cast<size_t>(std::count_if(source.begin(),
                                 source.end(), condition));
target.reserve(target.size() + count);

std::copy_if(source.begin(),
             source.end(),
             std::back_inserter(target), condition);

Live demo . 现场演示

As ranges will be standardized soon, this is an alternative using range-v3 , the reference library for the proprosal: 由于范围很快就会标准化,因此这是使用range-v3 (proprosal的参考库)的替代方案:

#include <range/v3/view/concat.hpp>
#include <range/v3/view/filter.hpp>

using namespace ranges;

const std::vector<int> source{1, 3, 2, 3, 4, 5, 3};
const std::vector<int> target = view::concat(source,
    source | view::filter([](auto i){ return i != 3; }));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM