我有一个清单:
list<Unit *> UnitCollection;
包含Unit对象,其中包含一个访问者:
bool Unit::isUnit(string uCode)
{
if(this->unitCode == uCode)
return true;
else
return false;
}
如何通过uCode搜索我的UnitCollection列表并返回相应的元素(最好是迭代器)。
在伪代码中,它看起来像这样:
for every item in my UnitCollection:
if the unit.isUnit(someUnitIpass)
do something
else
next unit
我查看了find()方法,但是我不确定你是否可以传入一个布尔方法,而不是搜索项参数,如果这是有意义的。
答案 0 :(得分:4)
您可以将访问者功能更改为更简单的格式
return unitCode == uCode;
你最好寻找元素的位置而不是它的索引。从索引获取元素是O(n)操作,而从其位置获取元素是O(1)操作。所以,使用STL和boost::bind()
的一点帮助:
#include <algorithm>
#include <boost/bind.hpp>
// ...
std::string uCode("uCode to search for");
std::list<Unit*>::iterator pos = std::find_if(unitCollection.begin(),
unitCollection.end(),
boost::bind(&Unit::isUnit,
_1, uCode));
STL确实有std::mem_fun()
,与std::bind2nd()
一起会得到相同的结果。问题是mem_fun()
仅适用于不带参数的成员函数。另一方面,boost::bind()
功能更强大,并且可以很好地解决问题。你应该期待下一个标准,它应该在弥赛亚到来之后立即出现。
如果项目中没有提升,那么你真的应该安装它。如果标准库是C ++的妻子,那么Boost就是C ++的年轻情人。他们都应该在那里,他们相处得很好。
话虽如此,你可以将函数提取到一个独立的函数对象中,正如彼已提到的那样:
struct has_uCode {
has_uCode(cont std::string& uc) : uc(uc) { }
bool operator()(Unit* u) const { return u->isUnit(uc); }
private:
std::string uc;
};
然后,您可以这样调用std::find_if()
:
std::list<Unit*>::iterator pos = std::find_if(unitCollection.begin(),
unitCollection.end(),
has_uCode("this and that"));
还有一件事:我不知道uCode的样子,但如果它们很大,那么你可以通过维护这些字符串的哈希来加快速度,这样在你的搜索谓词中你只能比较哈希值。散列可能是常规整数:比较整数非常快。
还有一件事:如果您经常运行此搜索过程,您可能还会考虑更改容器类型,因为这确实是一个昂贵的过程:按列表长度的顺序。
答案 1 :(得分:3)
您可以查看jpalecek建议的find_if,然后使用distance查找从find_if和UnitCollection.begin()返回的迭代器之间的距离,该距离应该是索引列表中的元素。
至于谓词,你可以编写一个这样的函数对象:
struct predicate
{
predicate( const std::string &uCode ) : uCode_(uCode) {}
bool operator() ( Unit *u )
{
return u->isUnit( uCode_ )
}
private:
std::string uCode_;
};
然后像这样使用它:
predicate pred("uCode");
std::list<Unit*>::iterator i;
i = std::find_if( UnitCollection.begin(), UnitCollection.end(), pred );
或者至少我认为这是一种方法。
答案 2 :(得分:3)
您的谓词可能类似于:
struct unit_predicate {
unit_predicate(const string& s): str(s) {}
bool operator()(const Unit* unit) const {
return unit->isUnit(str);
}
const string& str;
};
UnitCollection::const_iterator unit = std::find_if(units.begin(), units.end(), unit_predicate("Some Unit"));
其他几条评论:
你的isUnit函数最好采用(const)引用的字符串来避免不必要的副本。
你说你想要退回一个项目的索引;这通常不适用于链表,因为您无法通过索引获取项目。如果你想通过索引来处理它们,那么std::vector
可能对你更有用。
答案 3 :(得分:1)
查看find_if
。
如果您可以使用提升,则可以将其与boost::lambda
一起使用。
namespace bl=boost::lambda;
std::find_if(...begin... , ...end... , bl::bind(&Unit::isUnit, *bl::_1, "code"))
或者你可以酿造自己的仿函数。
struct isUnitor // : public std::unary_function<Unit*, bool> -- this is only needed for the negation below
{
string arg;
isUnitor(const string& s) : arg(s) {}
bool operator()(Unit* u) const { return u->isUnit(arg); }
};
std::find_if(...begin... , ...end... , isUnitor("code"))
或者,如果您想要索引(对于否定,请查看here):
std::count_if(...begin... , ...end... , not1(isUnitor("code")))