用于映射的联合迭代器
Union iterator for maps?
[前言:像std::map
这样的关联c++容器有点像只有一个键列的微型数据库。Boost的bimap
将其提升为两列表,并在两列中进行查找,但这是到目前为止的类比-没有"polymap"来概括这个想法。
在任何情况下,我都想继续把映射看作数据库,我现在想知道是否有一个迭代器(或其他解决方案)允许我对几个组成映射进行UNION。也就是说,所有映射都具有相同的类型(或者至少具有相同的值类型和比较器),并且我想要一个单一的迭代器,它将整个集合视为一个大的multimap(重复键可以),并允许我以正确的联合顺序遍历它。
是否存在这样的东西,也许在Boost中?还是很容易组装起来?在伪代码中:
std::map<K, M> m1, m2;
union_iterator<K, M> u(m1, m2)
for(auto it = u.begin(); it != u.end(); ++it) { /* ... */ }
例如,如果我们有:
m1 = { { 9:00, "Check in"}, { 12:00, "Break" }, { 16:00, "Check out"} };
m2 = { { 10:30, "coffee" }, { 12:15, "baked beans" }, { 15:00, "lies" } };
那么我希望迭代器产生:
9:00, "Check in"; 10:30, "coffee"; 12:00, "Break"; 12:15, "baked beans"; ...
有一个"polymap": Boost.MultiIndex.
正如我所宣布的,我有一个非常酷的东西。
我现在把它贴出来,因为我不确定今晚是否能及时回来贴出来。我将花几句话来解释。(在这篇文章)
p。包括将被削减(约20%);我可能还会对代码做一些更一般的工作。
关于这段代码有很多可说的:它不是很高效,也不是很干净。然而,它几乎是无限通用的,应该像其他任何东西一样扩展。所有代码都可以在github的gist中找到:
- merge_maps_iterator.hpp
- Makefile
- test.cpp——一组相当神秘的测试用例,展示了
的通用性(我并不是说用int和float来设置映射键是个好主意(更不用说同时使用这两种键了)——只是表明这是可以做到的)
== input ========================================
{ 2, aap } { 23, mies } { 100, noot } { 101, broer }
{ b, 3.14 }
== output =======================================
2: aap;
23: mies;
98: 3.14;
100: noot;
101: broer;
== input ========================================
{ b, 3.14 }
{ 2, aap } { 23, mies } { 100, noot } { 101, broer }
== output =======================================
2: aap;
23: mies;
98: 3.14;
100: noot;
101: broer;
== input ========================================
{ 2, aap } { 23, mies } { 100, noot } { 101, broer }
{ 2, aap } { 23, mies } { 100, noot } { 101, broer }
== output =======================================
2: aap;aap;
23: mies;mies;
100: noot;noot;
101: broer;broer;
== input ========================================
{ b, 3.14 }
{ b, 3.14 }
== output =======================================
b: 3.14;3.14;
== input ========================================
{ 1.0, dag } { 22.0, bye } { 24.0, Tschüß }
{ 1, true } { 22, false } { 24, true }
{ b, 3.14 }
{ 2, aap } { 23, mies } { 100, noot } { 101, broer }
== output =======================================
1.0: dag;true;
2.0: aap;
22.0: bye;false;
23.0: mies;
24.0: Tschüß;true;
98.0: 3.14;
100.0: noot;
101.0: broer;
== input ========================================
{ 1.0, dag } { 2.0, EXTRA } { 22.0, bye } { 24.0, Tschüß }
{ 1, true } { 22, false } { 24, true }
{ b, 3.14 }
{ 2, aap } { 23, mies } { 100, noot } { 101, broer }
== output =======================================
1.0: dag;true;
2.0: EXTRA;aap;
22.0: bye;false;
23.0: mies;
24.0: Tschüß;true;
98.0: 3.14;
100.0: noot;
101.0: broer;
将两个mapS
复制到一个临时文件中,将一个附加到另一个(如果您可以修改它们),或者使用vector
作为std::set_union
和自定义比较器的临时文件,这是最简单的替代解决方案。
我将如何实现thiton的答案:
template <class container> class union_iterator
{
private:
typedef std::pair<typename container::const_iterator, typename container::const_iterator> container_range;
class container_range_compare
{
public:
bool operator()(const container_range &lhs, const container_range &rhs) const
{
return typename container::value_compare()(*lhs.first, *rhs.first);
}
};
std::priority_queue<container_range, container_range_compare> m_range_queue;
container::const_iterator m_current_iterator;
bool m_is_valid;
void add_container(const container &cont)
{
add_container_range(std::make_pair(cont.begin(), cont.end()));
}
void add_container_range(const container_range &range)
{
if (range.first!=range.second)
{
m_range_queue.push(range);
}
}
public:
union_iterator(const container &a): m_valid(false)
{
add_container(a);
}
bool next()
{
m_is_valid= false;
if (!m_range_queue.empty())
{
container_range range= m_range_queue.pop();
m_current_iterator= range.first;
++range.first;
add_container_range(range);
m_is_valid= true;
}
return m_is_valid;
}
typename const container::value_type &operator *() const
{
return *m_current_iterator;
}
typename const container::value_type *operator ->() const
{
return m_current_iterator.operator ->();
}
};
它的用法与union_iterator<K, V>
略有不同,但它实现了基本的思想。您可以扩展构造函数以接受多个映射,并在while (iterator.next())
循环中使用它,而不是for (...)
循环。
编辑:我简化了next()
做所有的弹出和推一次。所以现在更简单了!(也可以花费一些精力使其像STL迭代器一样,但这会很繁琐。)
使用boost function_output_iterator非常简单的解决方案:
typedef std::map< std::string, std::string > Map;
Map first_map, second_map;
... // fill maps
// iterate over maps union
std::merge(
first_map.begin(), first_map.end(),
second_map.begin(), second_map.end(),
boost::make_function_output_iterator(
[]( const Map::value_type & pair )
{
std::cout <<
"key = " << pair.first <<
"; value = " << pair.second << std::endl;
}
),
first_map.value_comp()
);
我们可以通过使用boost::set_union (range version)而不是std::set_union来使这个解决方案更美观。
UPD更新版本使用不同的键/值类型:
typedef std::map< int, char > FirstMap;
typedef std::map< short, std::string > SecondMap;
FirstMap first_map;
SecondMap second_map;
... // fill maps
struct CustomOutput
{
void operator()( const FirstMap::value_type & pair ) const
{
std::cout << "key = " << pair.first <<
"; value = " << pair.second << std::endl;
}
void operator()( const SecondMap::value_type & pair ) const
{
std::cout << "key = " << pair.first <<
"; value = " << pair.second << std::endl;
}
};
struct CustomPred
{
bool operator()( const FirstMap::value_type & first_pair, const SecondMap::value_type & second_pair ) const
{ return first_pair.first < second_pair.first; }
bool operator()( const SecondMap::value_type & second_pair, const FirstMap::value_type & first_pair ) const
{ return second_pair.first < first_pair.first; }
};
// iterate over maps union
std::merge(
first_map.begin(), first_map.end(),
second_map.begin(), second_map.end(),
boost::make_function_output_iterator( CustomOutput() ),
CustomPred()
);
UPD2 std::set_union替换为std::merge
还是很容易组装起来?
配置应该相当容易:对于N个基映射,迭代器包含一个优先级队列,按基迭代器指向的元素的N个键进行优先级排序。对于解引用,在队列前面解引用迭代器。对于increment,在队列的前面对迭代器进行自增,如果它的增量不在队列的末尾,则重新插入。
这是很容易做到的:
template<class It>
class union_iterator
{
public:
union_iterator(It it1_begin, It it1_end, It it2_begin, It it2_end)
: current1(it1_begin), current2(it2_begin), end1(it1_end), end2(it2_end)
{ if (it1_begin != it1_end && it2_begin != it2_end) {
if (*it1_begin < *it2_begin) { current= ¤t1; }
else { current = ¤t2; }
} else if (it1_begin==it1_end) { current=¤t2; }
else { current = ¤t1; }
}
void operator++() {
if (current1!=end1 && current2 !=end2) {
if (*current1 < *current2)
{ ++current1; current = ¤t1; }
else { ++current2; current=¤t2; }
} else if (current1==end1 && current2 != end2) {
++current2;
current = ¤t2;
} else if (current1!=end1 && current2 == end2) {
++current1;
current = ¤t1;
}
}
typename std::iterator<It1>::value_type operator*() { return **current; }
private:
It current1;
It current2;
It end1;
It end2;
It *current;
};
但真正的问题是实现普通迭代器所需的所有剩余成员函数:-)。Boost提供了一些库来帮助您这样做,但它可能仍然相当困难。
这不是你想要的迭代器,但我只是在标准库中找到了这个函数:
§25.4.5.2 set_union [set.union]
template<class InputIterator1, class InputIterator2, class OutputIterator, class Compare> OutputIterator set_union(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp);
- Effects:构造两个范围元素的排序交集;也就是说,在两个范围中都存在的元素的集合。
- 要求:结果范围不得与原始范围重叠。
- 返回:构造范围的结束。
- 复杂度:最多2 * ((last1 - first1) + (last2 - first2)) - 1比较。
- 备注:如果[first1,last1)包含m个相互等价的元素,[first2, last2)包含n个相互等价的元素,则从第一个范围依次复制最小(m, n)个元素到输出范围。
还有std::set_intersection
, std::set_difference
和std::set_symmetric_difference
- 错误 C2760:语法错误:映射迭代器上意外的标记"标识符",预期的";"
- 映射迭代器与运算符不匹配
- 使用迭代器替换映射中的常量项的方法
- 映射中循环的迭代器
- std::使用迭代器映射查找距离,程序不会终止
- 创建以向量/列表迭代器为键的映射
- 我可以将映射迭代器与 OpenMP 并行使用吗?
- c++ 在循环中删除迭代器(映射的映射)
- std::map 擦除 - 将迭代器传递给错误的映射
- BAD_ACCES映射 C++ 上的迭代器
- 映射/集迭代器不可取消引用 (C++) - 调试断言失败
- 取消引用映射迭代器时返回对临时的引用
- 来自并发哈希映射的迭代器是否安全
- 为什么映射迭代器显示基本操作数无效错误
- C++有关映射和迭代器的错误
- 复制映射迭代器对值的省略
- ->第一个/第二个到空映射迭代器开始
- 将迭代器映射到特定字段上的迭代器(可以使用 Boost)
- c++如何将迭代器映射的集合作为键
- 有没有一种方法可以将迭代器映射到它的容器