根据谓词插入不同容器的插入器迭代器

Posted

技术标签:

【中文标题】根据谓词插入不同容器的插入器迭代器【英文标题】:inserter iterator to insert in different container depending on predicate 【发布时间】:2020-04-12 00:46:35 【问题描述】:

假设我有一个数字向量,我想创建两个向量来分隔奇数和偶数的数字。用一个简单的 for 轻松实现:

std::vector<int> odds;
std::vector<int> evens;
std::vector<int> numbers;
for (int number : numbers) 
    if isOdd(number)
        odds.push_back(number);
    else
        evens.push_back(number);

我想知道是否有任何类型的 inserter_iterator 可以做到这一点,以便我可以编写类似的东西

std::vector<int> odds;
std::vector<int> evens;
std::vector<int> numbers;

auto pred = [](int i)  return isOdd(i) ? True : False;;
auto identity = [](int i) return i;;
std::transform(std::begin(numbers), std::end(numbers), some_inserter(odd, evens, pred), identity);

这只是出于好奇,我正在尝试了解算法和迭代器的工作原理。基于范围的解决方案也是有效的。

【问题讨论】:

C++ 库中没有类似的东西,但您可以自己编写。 std::transform 具有“身份”转换功能只是 std::copy。 是的,你是对的。我已经更改了我的原始代码。谢谢 【参考方案1】:

可以使用std::partition_copy算法,如下https://en.cppreference.com/w/cpp/algorithm/partition_copy

int main() 
    std::vector vec = 1, 2, 3, 4, 5, 6;//the vector to be partitioned
    std::vector<int> odds(vec.size());//will hold odds (to have the sam size as the original (the maximum case) )
    std::vector<int> evens(vec.size());//will hold evens

    auto [oddsEnd, evensEnd] =
            std::partition_copy(vec.begin(), vec.end(), odds.begin(), evens.begin(), [](int i)return i%2!=0;);//will copy at the front (won't insert at the back)
    odds.erase(oddsEnd, odds.end());//to erase the undesired excess
    evens.erase(evensEnd, evens.end());//to erase the undesired excess


或使用 std::back_inserter https://en.cppreference.com/w/cpp/iterator/back_inserter(参见 cmets)

int main() 
    std::vector vec = 1, 2, 3, 4, 5, 6;//the vector to be partitioned
    std::vector<int> odds;//will hold odds
    std::vector<int> evens;//will hold evens

    std::partition_copy(vec.begin(), vec.end(), std::back_inserter(odds), std::back_inserter(evens), [](int i)return i%2!=0;);//will insert


【讨论】:

当然可以,但是当您可以只使用 back_inserter 而不需要在最后擦除时,为什么要预先分配两个向量中的所有存储空间? @JohnZwinck 我觉得这样更有效率不是吗? 不,因为你在开始时不必要地复制了两次 vec 的内容,并且你使用的内存比最后需要的更多。 @JohnZwinck。所以,利弊(OP可以决定适当的方法) 这看起来很有希望,但如果我不是在做一个复制而是一个实际的转换(与身份谓词不同的东西)会发生什么?【参考方案2】:

您可以简单地使用for_each,几乎可以像您描述的那样使用:

std::vector<int> odds;
std::vector<int> evens;
std::vector<int> numbers;

auto pred = [](int i)  return i % 2;;
auto identity = [](int i) return i;;

std::for_each(std::begin(numbers), std::end(numbers), [&](int number) mutable 
    int iden = identity(number);
    if (pred(iden)) odds.push_back(iden);
    else evens.push_back(iden);
);

// Or with range-based loop:

for (auto &elem : numbers) 
    if (pred(iden)) odds.push_back(iden);
    else evens.push_back(iden);

【讨论】:

是的,但这不是惯用的,因为基于范围的 for 循环是在 2011 年引入的。OP 的原始代码更好。 @JohnZwinck 你说得对,我添加了替代方案。无论如何,我想知道这里是否仍然需要这个答案,因为 asmmo 答案要好得多。

以上是关于根据谓词插入不同容器的插入器迭代器的主要内容,如果未能解决你的问题,请参考以下文章

迭代器失效

迭代器

STL之--插入迭代器(back_inserter,inserter,front_inserter的区别)

10.4 再探迭代器-插入/IO/反向

C ++ 插入迭代器

容器操作使迭代器失效