假设我有以下两种数据结构:
std::vector<int> all_items;
std::set<int> bad_items;
all_items
向量包含所有已知项目,bad_items
向量包含不良项目列表。这两个数据结构完全相互独立地填充。
编写一个返回std::vector<int>
的方法的正确方法是什么?包含all_items
中不包含bad_items
的所有元素?
目前,我有一个笨重的解决方案,我认为可以更简洁地完成。我对STL功能适配器的理解不足。因此问题。我目前的解决方案是:
struct is_item_bad {
std::set<int> const* bad_items;
bool operator() (int const i) const {
return bad_items.count(i) > 0;
}
};
std::vector<int> items() const {
is_item_bad iib = { &bad_items; };
std::vector<int> good_items(all_items.size());
std::remove_copy_if(all_items.begin(), all_items.end(),
good_items.begin(), is_item_bad);
return good_items;
}
假设all_items
,bad_items
,is_item_bad
和items()
都是某些包含类的一部分。有没有办法把它们写成items()
getter:
struct is_item_bad
?我原本希望在count
上使用std::set
方法作为函子,但我无法用正确的方式表达w remove_copy_if
算法
编辑:修复了items()
中的逻辑错误。实际的代码没有问题,这是一个转录错误。
编辑:我接受了一个不使用std::set_difference
的解决方案,因为它更通用,即使std::vector
未排序也会有效。我选择在我的代码中使用C ++ 0x lambda表达式语法。我的最终items()
方法如下所示:
std::vector<int> items() const {
std::vector<int> good_items;
good_items.reserve(all_items.size());
std::remove_copy_if(all_items.begin(), all_items.end(),
std::back_inserter(good_items),
[&bad_items] (int const i) {
return bad_items.count(i) == 1;
});
}
在大约800万个项目的向量上,上述方法在3.1s中运行。我替补标记了std::set_difference
方法,它在大约2.1s内运行。感谢所有提供了很好答案的人。
答案 0 :(得分:8)
正如jeffamaphone建议的那样,如果你可以对任何输入向量进行排序,你可以使用std::set_difference
,这是有效的,代码更少:
#include <algorithm>
#include <set>
#include <vector>
std::vector<int>
get_good_items( std::vector<int> const & all_items,
std::set<int> const & bad_items )
{
std::vector<int> good_items;
// Assumes all_items is sorted.
std::set_difference( all_items.begin(),
all_items.end(),
bad_items.begin(),
bad_items.end(),
std::back_inserter( good_items ) );
return good_items;
}
答案 1 :(得分:3)
由于你的函数将返回一个向量,你必须在任何情况下创建一个新的向量(即复制元素)。在这种情况下,std::remove_copy_if
很好,但您应该正确使用它:
#include <iostream>
#include <vector>
#include <set>
#include <iterator>
#include <algorithm>
#include <functional>
std::vector<int> filter(const std::vector<int>& all, const std::set<int>& bad)
{
std::vector<int> result;
remove_copy_if(all.begin(), all.end(), back_inserter(result),
[&bad](int i){return bad.count(i)==1;});
return result;
}
int main()
{
std::vector<int> all_items = {4,5,2,3,4,8,7,56,4,2,2,2,3};
std::set<int> bad_items = {2,8,4};
std::vector<int> filtered_items = filter(all_items, bad_items);
copy(filtered_items.begin(), filtered_items.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
}
要在C ++ 98中执行此操作,我想您可以使用mem_fun_ref
和bind1st
将set :: count转换为内嵌函数,但是存在问题(由此产生的问题)在C ++ 0x中对bind1st的弃用意味着取决于你的编译器,你最终可能会使用std :: tr1 :: bind:
remove_copy_if(all.begin(), all.end(), back_inserter(result),
bind(&std::set<int>::count, bad, std::tr1::placeholders::_1)); // or std::placeholders in C++0x
在任何情况下,我认为显式函数对象更具可读性:
struct IsMemberOf {
const std::set<int>& bad;
IsMemberOf(const std::set<int>& b) : bad(b) {}
bool operator()(int i) const { return bad.count(i)==1;}
};
std::vector<int> filter(const std::vector<int>& all, const std::set<int>& bad)
{
std::vector<int> result;
remove_copy_if(all.begin(), all.end(), back_inserter(result), IsMemberOf(bad));
return result;
}
答案 2 :(得分:2)
冒着出现过时的风险:
std::set<int> badItems;
std::vector<int> items;
std::vector<int> goodItems;
for ( std::vector<int>::iterator iter = items.begin();
iter != items.end();
++iter)
{
int& item = *iter;
if ( badItems.find(item) == badItems.end() )
{
goodItems.push_back(item);
}
}
答案 3 :(得分:1)
std::remove_copy_if
返回目标集合的迭代器。在这种情况下,它将返回good_items.end()
(或类似的东西)。 good_items
在方法结束时超出范围,因此这会导致一些内存错误。您应该返回good_items
或通过引用传递new vector<int>
,然后clear
,resize
并填充它。这将摆脱临时变量。
我相信你必须定义自定义函子,因为该方法取决于你没有得到hackey AFAIK时无法指定的对象bad_items
。