【问题标题】:STL list - how to find a list element by its object fieldsSTL list - 如何通过其对象字段查找列表元素
【发布时间】:2010-05-14 01:30:03
【问题描述】:

我有一个清单:

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() 方法,但我不确定是否可以传递布尔方法而不是搜索项参数。

【问题讨论】:

  • 索引和列表不在一起。你最好返回一个迭代器或指针。

标签: c++ list stl


【解决方案1】:

首先是一个无关紧要的评论

您可以将访问器函数更改为更简单的形式

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() 功能更强大,并且很好地解决了这里的问题。你应该在下一个标准中期待它,它应该在弥赛亚到来后立即出现。

但如果你没有 Boost

如果您的项目中还没有 boost,那么您真的应该安装它。如果说标准库是 C++ 的老婆,那么 Boost 就是 C++ 的小情人。他们应该都在那里,他们相处得很好。

话虽如此,您可以将函数提取到一个独立的函数对象中,正如 Peter 已经提到的:

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 的样子,但如果它们很大,那么您可以通过维护这些字符串的散列来加快处理速度,这样在您的搜索谓词中您只比较散列。哈希可能是常规整数:比较整数非常快。

还有一件事:如果您经常运行此搜索过程,您可能还会考虑更改容器类型,因为这确实是一个昂贵的过程:按照列表长度的顺序。

【讨论】:

  • 不幸的是,Boost 不可用:(
  • @Dominic Bou-Samra:然后使用bind2ndmem_fun_ref 和朋友。
  • @Billy ONeal 请参阅我上面的最后一段。这些不起作用,因为mem_fun() 生成一元函数,而函数的参数是调用函数的对象。 mem_fun() 或其任何朋友都没有生成二进制函数的版本。当然,@Dominic 可以 做的是创建一个函数(-object),该函数可以与标准算法一起独立工作,也可以与&lt;functional&gt; 中的任何标准适配器一起使用。这可能是您最好的方法。
【解决方案2】:

您可以按照 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 );

或者至少我认为这是一种方法。

【讨论】:

    【解决方案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"));
    

    其他几个cmets:

    您的 isUnit 函数最好通过 (const) 引用获取字符串,以避免不必要的复制。

    你说你想返回一个项目的索引;这对于链表通常是不明智的,因为您无法通过索引取回项目。如果你想通过索引来处理它们,也许std::vector 对你更有用。

    【讨论】:

    • 谢谢 - 这很完美。鉴于我的“做某事”正在删除一个项目,我也可以使用可爱的 remove_if。是的,索引是愚蠢的,但它是一个满足似乎是最不诚实的标准和代码规范的案例,使用它们告诉我们的内容。
    • @Dominic:你必须使用列表吗?
    • 是的 :( 我想使用矢量
    【解决方案4】:

    看看find_if

    如果你可以使用boost,你可以使用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")))
    

    【讨论】:

    • 我有(忘了提)但我仍然卡住了。我在 find_if 中使用什么谓词?该函数位于列表中包含的对象中。这个链接类似于我需要的cplusplus.com/forum/general/3656
    • 理想情况下,我会使用 boost,但我不能(标准):(
    猜你喜欢
    • 2017-05-10
    • 2012-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-30
    • 1970-01-01
    • 2021-02-18
    • 1970-01-01
    相关资源
    最近更新 更多