通过自定义排序迭代boost multi_index

时间:2015-09-06 21:30:05

标签: c++ boost containers boost-multi-index

我有一个带有多个索引的boost multi_index容器。如何在迭代时使用指定的自定义比较迭代元素。

例如,假设Element::nameElement::indexmulti_index容器编入索引

bool myCompare(const Element &lhs, const Element &rhs)
{
    if(lhs.name == rhs.name)
    {
        return lhs.index < rhs.index;
    }
    else
    {
        return lhs.name < rhs.name;
    }
}

但是,迭代不应局限于上述情况,而是根据Element的索引成员允许任何类型的顺序,类似于SQL SELECT顺序。

2 个答案:

答案 0 :(得分:2)

这不是多索引容器的功能。

但是,您可以轻松创建额外的临时索引:

std::vector<boost::reference_wrapper<Element const> > temporary(table.begin(), table.end());

现在,您可以通过自定义比较对名为ordering的索引进行排序:

std::sort(temporary.begin(), temporary.end(), myCompare);

// iterate table in that order:
for(Element const& e: temporary)
    std::cout << e.index << "\t" << e.name << "\n";
  

奖金:您可以使用multi_index::random_accesss轻松地在rearrange索引中保留该排序:

table.get<external>().rearrange(temporary.begin());
     

(其中external标记随机访问索引)。

更多说明:

  • 您的谓词没有定义适当的弱总排序。看来你可能想要这样做:

    #include <tuple>
    bool myCompare(const Element &lhs, const Element &rhs) {
        return std::tie(lhs.name, lhs.index) < std::tie(rhs.name, rhs.index);
    }
    

样本

<强> Live On Coliru

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/random_access_index.hpp>

struct Element {
    int index;
    std::string name;
};

#include <tuple>
bool myCompare(const Element &lhs, const Element &rhs) {
    return std::tie(lhs.name, lhs.index) < std::tie(rhs.name, rhs.index);
}

namespace bmi = boost::multi_index;

using Table = boost::multi_index_container<Element,
      bmi::indexed_by<
        bmi::random_access<bmi::tag<struct external> >
      > >;

#include <iostream>
#include <vector> // for the temporary index

int main() {
    Table table;

    // generate 30 random records...
    std::generate_n(back_inserter(table), 30,
            []{ return Element { rand()%100, std::string(1, 'a'+rand()%26) }; }
        );

    {
        // temporary index:
        std::vector<boost::reference_wrapper<Element const> > temporary(table.begin(), table.end());

        // iterate table in the order specified by myCompare:
        std::sort(temporary.begin(), temporary.end(), myCompare);

        for(Element const& e: temporary)
            std::cout << e.index << "\t" << e.name << "\n";

        // now to rearrange a random-access index on the multi-index container:
        table.get<external>().rearrange(temporary.begin());
    }
}

打印(例如)

98  a
21  b
93  b
15  c
56  d
62  d
62  d
67  d
91  e
84  f
62  g
49  h
11  i
40  k
29  l
29  m
63  o
86  q
67  r
69  r
77  r
90  r
82  s
93  s
22  w
83  w
96  x
11  y
13  y
72  y

你可以看到,名字相同的地方,较低的索引是第一位的。

更新评论:

  

嗯?你在寻找魔法仙尘吗?

你要求魔仙尘吗?没有魔力。

DBMS不会使用任何与众不同的东西。他们通常使用BTrees,非常类似于您花园下的数据结构 变种std::map,或者bmi::ordered_[non_]unique指数。所以在这方面,Boost MultiIndex是(接近)你想要的, 已经。

RDBMS-es带来的主要添加事物是持久性的。有了它,一切都变得更有用,更具可扩展性和更少 高效。

  

所以,不要把DBMS-es视为效率的圣杯。

有一个原因是内存中的键值数据存储被广泛用于性能。另一个重要的注意事项是检索 SQL中的有序结果集根本不能很好地执行, 除非索引已经存在

现在,如果您希望获得最佳性能,您可能需要设计自己的数据结构(可能在Boost的帮助下) 侵入性的,但看到你提出这些问题的程度,我会在很长一段时间内使用Boost Multi Index 你做。只需创建有时需要组合的索引,并编写一些“循环代码”¹,根据需要组合它们。

IDEAS

现在开箱即用,可能想知道如何以DBMS的方式“轻松”实现两级排序 引擎可能。

  1. 与DBMS引擎一样,最简单的方法是在准备就绪时拥有一个始终uptodate索引: 的 Demo

    using Table = boost::multi_index_container<Element,
        bmi::indexed_by<
            bmi::sequenced<bmi::tag<struct insertion_order> >,
            // ready made index for traversal in composite order
            bmi::ordered_non_unique<bmi::tag<struct readymade>, 
                bmi::composite_key<Element,
                    bmi::member<Element, std::string, &Element::name>,
                    bmi::member<Element, int, &Element::index> 
                >
            >
        > >;
    
    int main() {
        // generate 30 random records...
        Table table;
        std::generate_n(back_inserter(table), 30, []{ return Element { rand()%100, std::string(1, 'a'+rand()%26) }; });
    
        // effectively "zero" cost iteration there:
        for(Element const& e: table.get<readymade>())
            std::cout << e.index << "\t" << e.name << "\n";
    }
    

    请注意,您也可以部分地在Boost MultiIndex中使用有序复合索引

        for(Element const& e: boost::make_iterator_range(table.get<readymade>().equal_range("y")))
            std::cout << e.index << "\t" << e.name << "\n";
    

    打印(对于与上面相同的随机种子):

    11  y
    13  y
    72  y
    
  2. 或者,您可以定义单独的索引,并将它们与您自己的算法一起使用以实现两级排序:

    using Table = boost::multi_index_container<Element,
        bmi::indexed_by<
            bmi::sequenced<bmi::tag<struct insertion_order> >,
    
            // separate indices that we might combine in some way later::
            bmi::ordered_non_unique<bmi::tag<struct by_index>, bmi::member<Element, int, &Element::index> >,
            bmi::ordered_non_unique<bmi::tag<struct by_name>,  bmi::member<Element, std::string, &Element::name> >
        > >;
    

    现在组合索引成为“引擎”的工作,或者在这种情况下是你的算法。这是一个想法:

    template <typename Index1, typename Index2, typename Table, typename Function>
        void SelectOrderBy2(Table const& table, Function function) {
    
            using T = typename Table::value_type const;
            auto& idx1 = table.template get<Index1>();
            auto& idx2 = table.template get<Index2>();
    
            auto it = idx1.begin(), end = idx1.end();
    
            while (it!=end) {
                auto next = idx1.upper_bound(idx1.key_extractor()(*it));
    
                std::set<boost::reference_wrapper<T>, typename Table::template index<Index2>::type::value_compare>
                    set(idx2.value_comp());
    
                while (it != next)
                    set.insert(set.end(), boost::cref(*it++));
    
                for (auto& e: set)
                    function(e);
            }
        }
    

    这是一些非常高级的模板代码,但您可以非常简单地使用它:

    SelectOrderBy2<by_name, by_index>(
            table, 
            [](Element const& e) { std::cout << e.index << "\t" << e.name << "\n"; }
        );
    

    Live On Coliru

  3. ¹或许更好地称为通用/专用算法......:)

答案 1 :(得分:2)

添加到@ sehe的答案,如果你真的想快速访问任何属性组合,可以通过一些元编程和一些组合来完成,如{ {3}}。一般来说,如果您有 N 属性 C N ,floor( N / 2)),则需要索引( C a b )代表this example):例如,要处理4个属性,你必须定义6个不同的指数。这仅涵盖相等查询,而不是 a &lt; 5或10&lt; a &lt; = 12。