簡體   English   中英

使用條件迭代器插入向量

[英]Insert into vector with conditional iterator

假設我有一個帶有各種條目的vector ,我想插入到另一個向量中,同時省略滿足條件的條目。

例如,我想插入一個向量,同時省略所有三個。

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

到目前為止我想出的是使用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));

我正在尋找的更多是一個迭代器,它檢查條件並在條件不滿足時繼續運行。 像這樣的東西:

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

我想一個conditional_end函數是必要的,因為std::end會返回一個與conditional_begin不同的迭代器類型。

也許我忽略了一些事情,所以我的問題是:

  • 標准庫是否提供類似的東西?
  • 有沒有不同的簡單方法來實現我的目標?
  • 有沒有一種簡單的方法來實現條件迭代器功能?

有沒有不同的簡單方法來實現我的目標?

是的,標准已經內置了此功能。您正在尋找的功能是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; });

這里, std::back_inserter(target)將為謂詞返回true每個元素調用target push_back

是的,您可以創建一個自定義迭代器來執行您想要的操作,但使用標准C ++創建自定義迭代器當前有點繁瑣。 它看起來像這樣:

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

然后,您可以創建類似您要求的conditional_beginconditional_end幫助函數。 唯一的問題是std::vector::insert期望兩個迭代器具有相同的類型。 如果我們將lambda用於我們的條件,那么這將是條件迭代器類型的一部分。 所以我們需要將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);
}

您可以使用這樣的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)));

現場演示

我的原始測試顯示,在這種情況下,這最終比簡單地使用copy_ifback_inserter快得多,因為std::vector::insert首先計算出在插入之前要分配多少內存。 僅使用back_inserter將導致多個內存分配。 性能的差異將取決於評估條件的成本。 在使用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);

現場演示

由於范圍很快就會標准化,因此這是使用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