根据谓词插入不同容器的插入器迭代器
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 答案要好得多。以上是关于根据谓词插入不同容器的插入器迭代器的主要内容,如果未能解决你的问题,请参考以下文章