我有一类兴趣(称之为X) 我有一个std :: list< X *> (称之为L) 我有一个功能(称之为F)。
F(L)根据检查列表中每个X的内部状态的算法返回L的子集(std :: list< X *>)。
我正在向我的应用程序添加一个std :: map< int,X *> (称之为M),我需要定义F(M)以与F(L)相同的方式操作 - 也就是说,F(M)必须返回std :: list< X *>同样,通过检查地图中每个X的内部状态来确定。
作为一个自我描述的懒惰程序员,我立即看到该算法将[逻辑]相同,并且每个数据类型(std :: list和std :: map)都是可迭代的模板。我不想两次保持相同的算法,但我不确定如何继续前进。
一种方法是从F(M)中取出X *(即键值映射中的'值'),将它们放入std :: list< X *>中,将处理过去到F(std :: list< X *>),传递返回std :: list< X *&gt ;;回过头来。我看不出这是唯一的方法。
我的问题:如何在一个地方维护核心算法,但保留迭代序列或对关联容器的值的能力?
谢谢!
答案 0 :(得分:7)
首先,除std::remove_copy_if
之外,两者的条件都可以完成。尽管名称remove_copy_if
,但不会删除原始集合中的任何内容。我认为,如果它被称为filtered_copy
,人们会更容易理解它。它将元素从一个集合复制到另一个集合。对于每个元素,它调用一个谓词,当且仅当谓词为该元素返回false时才复制该项。
这使您只有一个责任:实现查看每个X *的测试函数,并说明是否应将其排除在您正在制作的副本之外。由于您希望以两种不同的方式应用一个逻辑,因此我将逻辑封装在类的私有函数中。这两种方式可以作为类的operator()
的重载版本提供给外部世界:
class F {
bool do_test(X const *x) const { return x.internal_stuff; }
public:
bool operator()(X const *x) const { return do_test(x); }
bool operator()(std::pair<int, X const *> const &p) const {
return do_test(p.second);
}
};
由于operator()(X const *)
是do_test()
的纯粹笨蛋,你可能想摆脱它,但IMO可能弊大于利。
在任何情况下,这都会将您的逻辑完全放在一个地方(F::do_test
)。它还提供了一种简单,一致的语法,用于创建list<X *>
或std::map<int, X *>
的过滤副本:
std::list<X *> result;
std::remove_copy_if(coll.begin(), coll.end(), std:back_inserter(result), F());
最后一点:std::list
可能是现存最常用的集合。虽然它确实有它的用途,但它们确实非常罕见。 <{1}}和std::vector
非常经常更好。
答案 1 :(得分:2)
编写一个函数,接受两个正向迭代器作为它的参数(开头和结尾),然后函数只测试迭代器的值,如果它通过测试就将它添加到列表中,递增迭代器,并测试它没有到达终点(如果确实如此,就会中断。)
然后你只需调用该函数并将其传递给集合的begin()和end()迭代器。
答案 2 :(得分:1)
如下:
template<typename T>
struct func {
std::list<T>& r;
func(std::list<T>& r_) : r(r_) {}
bool algorithm(const T& t) {
return t<5; // obviously meant to be replaced :)
}
void operator()(const T& t) {
if (algorithm(t)) r.push_back(t);
}
void operator()(const std::pair<int, T>& t) {
if (algorithm(t.second)) r.push_back(t.second);
}
};
template<typename T, typename ForwardIterator>
std::list<T> subset(ForwardIterator begin, ForwardIterator end) {
std::list<T> r;
std::for_each(begin, end, func<T>(r));
return r;
}
答案 3 :(得分:0)
一种解决方案是将算法从两个函数中移出,这些函数只是迭代它们的容器,并调用算法函数来确定特定项是否属于返回列表。
答案 4 :(得分:0)
你可能是对的,你建议的方法不是唯一的解决方案,但它可能是最容易正确编写和理解的。如果您正在编写生产代码,我肯定会从那里开始。只有在您需要时才能分析代码并获得更好的效果。
在探索其他选项时,您可以查看boost::bind
。当我试图做一些类似的事情时,我收到了this answer。我认为std::tr1::bind
基本相同,所以如果你没有Boost,你应该可以替换TR1版本。